Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions .claude/skills/task-memory/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
name: task-memory
description: >
Manage persistent tasks, reminders, and follow-ups. Use when the user
mentions deadlines, asks to remember something, sets reminders, says
"todo/task/remind/don't forget/track", or during heartbeat processing.
Also use when user asks "what's pending/open/due".
---

## Task Storage

Tasks live in `.tinyclaw/tasks/active/` as individual markdown files.
Completed tasks move to `.tinyclaw/tasks/completed/`.
Index at `.tinyclaw/tasks/index.json` for quick scanning.

File naming: `<timestamp>-<slug>.md` (e.g. `1738000000-call-mom.md`)
Use the template at [templates/task-template.md](templates/task-template.md) for new tasks.
See [examples/sample-tasks.md](examples/sample-tasks.md) for extraction examples.

## Creating Tasks

### Explicit (always create immediately)

Trigger words: "remind me", "add task", "todo", "track", "don't forget", "remember to"

1. Create task file in `.tinyclaw/tasks/active/`
2. Update `.tinyclaw/tasks/index.json`
3. Confirm to user: "Tracked: <title> (due: <date>)"

### Implicit (ask first)

When you detect action items, commitments, or deadlines in conversation:

1. Ask: "Should I track '<extracted item>' as a task?"
2. Only create if user confirms
3. Note in follow-ups: "extracted from conversation"

### Recurring

When user says "every day/week/friday/month":

1. Set `recurrence` field: `daily`, `weekly/<day>`, `monthly/<date>`
2. On completion of a recurring task, auto-create the next occurrence with updated due date
3. Completed instance moves to `completed/` as normal

### Dependencies

When user says "do X after Y is done" or "X depends on Y":

1. Set `blocked_by: [<task-id>]` in the new task
2. During heartbeat, skip reminders for blocked tasks
3. When blocking task completes, notify that dependent task is now unblocked

## Heartbeat Behavior

When processing a heartbeat message:

1. Read `.tinyclaw/tasks/index.json`
2. Get current time and compare against due dates
3. Decision tree:
- **No tasks due or overdue** → respond ONLY with `HEARTBEAT_OK` (nothing else)
- **Tasks due within 1 hour** → send urgent reminder via source channel
- **Tasks overdue** → send reminder with how long overdue
- **Tasks due today** → send a heads-up
- **Tasks with no due date older than 7 days** → gentle nudge
4. Format reminders as short, actionable messages
5. If `.tinyclaw/heartbeat.md` itself seems stale or incomplete, update it

## Task Commands (natural language)

| User says | Action |
|---|---|
| "what's pending?" / "open tasks" / "what's due?" | List active tasks grouped by priority |
| "done with X" / "completed X" / "finished X" | Move to `completed/`, update index |
| "push X to tomorrow" / "reschedule X" | Update due date in task file and index |
| "cancel X" / "drop X" / "nevermind X" | Move to `completed/` with `status: cancelled` |
| "make X urgent" / "prioritize X" | Update priority field |
| "update heartbeat to include X" | Modify `.tinyclaw/heartbeat.md` directly |

## Index Maintenance

After ANY task create, update, complete, or delete:

1. Read all files in `.tinyclaw/tasks/active/`
2. Rebuild `.tinyclaw/tasks/index.json` with format:

```json
[{ "id": 0, "title": "", "status": "", "due": "", "priority": "", "source_channel": "", "recurrence": null }]
```

## Priority Levels

- **urgent**: due within 1 hour or explicitly marked
- **high**: due today
- **normal**: due this week (default)
- **low**: no due date or explicitly marked
66 changes: 66 additions & 0 deletions .claude/skills/task-memory/examples/sample-tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Task Extraction Examples

## Explicit — Create immediately

User: "Remind me to call mom tomorrow at 6pm"
→ title: "Call mom"
→ due: tomorrow 18:00
→ priority: normal
→ recurrence: null
→ Response: "Tracked: Call mom (due: tomorrow 6:00 PM)"

User: "Todo: buy groceries"
→ title: "Buy groceries"
→ due: null
→ priority: low
→ Response: "Tracked: Buy groceries (no due date)"

## Recurring — Create with recurrence field

User: "Every Friday remind me to review open PRs"
→ title: "Review open PRs"
→ due: next friday 17:00
→ priority: normal
→ recurrence: weekly/friday
→ Response: "Tracked: Review open PRs (recurring: every Friday)"

User: "Remind me daily to check emails at 9am"
→ title: "Check emails"
→ due: tomorrow 09:00
→ priority: normal
→ recurrence: daily
→ Response: "Tracked: Check emails (recurring: daily at 9:00 AM)"

## Implicit — Ask confirmation first

User: "I need to finish that report by Friday"
→ Ask: "Should I track 'Finish report' as a task due Friday?"
→ If yes: create with follow-up note "extracted from conversation"
→ If no: do nothing

User: "I told Sarah I'd send the designs next week"
→ Ask: "Should I track 'Send designs to Sarah' as a task due next week?"

## Dependencies

User: "After the report is done, send it to Sarah"
→ title: "Send report to Sarah"
→ blocked_by: [<report-task-id>]
→ Response: "Tracked: Send report to Sarah (blocked by: Finish report)"

## Heartbeat Responses

### Nothing due
→ "HEARTBEAT_OK"

### Task overdue
→ "Reminder: 'Call mom' was due 2 hours ago. Want to reschedule or mark done?"

### Task due today
→ "Heads up: 'Review open PRs' is due today at 5:00 PM"

### Task due within 1 hour
→ "Urgent: 'Team standup' is due in 30 minutes!"

### Old task with no due date
→ "You've had 'Buy groceries' open for 8 days with no due date. Still relevant?"
19 changes: 19 additions & 0 deletions .claude/skills/task-memory/templates/task-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
id: ${TIMESTAMP}
title: ${TITLE}
status: active
priority: normal
due: ${DUE_DATE}
source_channel: ${CHANNEL}
source_sender: ${SENDER}
created: ${CREATED}
tags: []
recurrence: null
blocked_by: []
---

## Context
${CONTEXT}

## Follow-ups
- ${CREATED} — Task created ${EXTRACTION_TYPE}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Dependencies
node_modules/

# Build output
dist/

# TinyClaw runtime files
.tinyclaw/logs/
.tinyclaw/whatsapp-session/
.tinyclaw/queue/
.tinyclaw/tasks/
.tinyclaw/reset_flag
.wwebjs_cache
*.log
Expand Down
12 changes: 11 additions & 1 deletion .tinyclaw/heartbeat.md
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
Quick status check: Any pending tasks or issues? Keep response to 1-2 sentences.
# Heartbeat Check

1. Read `.tinyclaw/tasks/index.json`
2. Check for overdue, due-today, and due-within-1-hour tasks
3. If nothing needs attention: respond ONLY with "HEARTBEAT_OK"
4. If tasks need attention: send reminders to the source channel
5. If you notice this heartbeat file is stale or missing checks, update it

Optional checks:
- Pending tasks with no due date older than 7 days — nudge about them
- Blocked tasks where the blocker is now completed — notify unblocked
143 changes: 143 additions & 0 deletions docs/plans/2026-02-09-persistent-memory-task-tracking-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Feature 1: Persistent Memory + Task Tracking

## Overview

Add persistent task management to TinyClaw using Claude Code's native skills system. Claude extracts tasks from conversation (explicit and implicit), stores them as individual markdown files with YAML frontmatter, and proactively follows up via the heartbeat system — mirroring OpenClaw's core task tracking behavior.

## Architecture

```
User message ("remind me to call mom")
Claude (with task-memory skill loaded)
Creates .tinyclaw/tasks/active/1738000000-call-mom.md
Updates .tinyclaw/tasks/index.json
Heartbeat (every 5 min) reads index.json
Due/overdue? → Send reminder via source channel
Nothing due? → Respond "HEARTBEAT_OK" (silent, no message sent)
```

## Task Storage

```
.tinyclaw/tasks/
├── active/ # Current tasks
│ ├── 1738000000-call-mom.md
│ └── 1738000100-review-pr-42.md
├── completed/ # Done/cancelled tasks
│ └── 1737999000-send-invoice.md
└── index.json # Lightweight index for quick lookups
```

### Task File Format

```yaml
---
id: 1738000000
title: Call mom
status: active
priority: normal
due: 2026-02-10T18:00:00
source_channel: whatsapp
source_sender: "You"
created: 2026-02-09T14:30:00
tags: [personal]
recurrence: null
blocked_by: []
---

## Context
You mentioned wanting to call mom during our conversation about weekend plans.

## Follow-ups
- 2026-02-09 14:30 — Task created from conversation
```

### Index Format

```json
[
{
"id": 1738000000,
"title": "Call mom",
"status": "active",
"due": "2026-02-10T18:00:00",
"priority": "normal",
"source_channel": "whatsapp",
"recurrence": null
}
]
```

## Skill Definition

Location: `.claude/skills/task-memory/SKILL.md`

### Task Extraction

- **Explicit** (create immediately): Trigger words like "remind me", "add task", "todo", "track", "don't forget". Confirm to user after creation.
- **Implicit** (ask first): Claude detects action items, deadlines, commitments in conversation. Asks "Should I track X as a task?" before creating.
- **Recurring**: "every day/week/friday/month" sets `recurrence` field. On completion, auto-creates next occurrence.
- **Dependencies**: "do X after Y" sets `blocked_by` field linking to another task ID.

### Natural Language Commands

- "what's pending?" — list active tasks grouped by priority
- "done with X" / "completed X" — move to completed/, update index
- "push X to tomorrow" — update due date
- "cancel X" — move to completed/ with status: cancelled
- "update heartbeat to include X" — modify .tinyclaw/heartbeat.md

### Priority Levels

- **urgent**: due within 1 hour or explicitly marked
- **high**: due today
- **normal**: due this week (default)
- **low**: no due date or explicitly marked

## Heartbeat Changes

### Updated .tinyclaw/heartbeat.md

Replaces the static prompt with a smart checklist:

1. Read `.tinyclaw/tasks/index.json`
2. Check for overdue, due-today, and due-within-1-hour tasks
3. If nothing needs attention: respond ONLY with "HEARTBEAT_OK"
4. If tasks need attention: send reminders to the source channel
5. If heartbeat.md itself is stale, update it
6. Nudge about tasks with no due date older than 7 days

### HEARTBEAT_OK Suppression

Change to `queue-processor.js`: when Claude responds with `HEARTBEAT_OK` for a heartbeat message, skip writing to the outgoing queue. This prevents sending empty messages to WhatsApp.

```javascript
if (messageData.channel === 'heartbeat' && response.trim() === 'HEARTBEAT_OK') {
log('INFO', 'Heartbeat: all clear, no action needed');
return;
}
```

## Files to Create/Modify

| File | Action |
|---|---|
| `.claude/skills/task-memory/SKILL.md` | Create — main skill instructions |
| `.claude/skills/task-memory/templates/task-template.md` | Create — template for new tasks |
| `.claude/skills/task-memory/examples/sample-tasks.md` | Create — examples for Claude |
| `.tinyclaw/tasks/active/` | Create directory |
| `.tinyclaw/tasks/completed/` | Create directory |
| `.tinyclaw/tasks/index.json` | Create — empty array initially |
| `.tinyclaw/heartbeat.md` | Modify — smart checklist replacing static prompt |
| `queue-processor.js` | Modify — suppress HEARTBEAT_OK from outgoing queue |

## References

- [OpenClaw Heartbeat System](https://docs.openclaw.ai/gateway/heartbeat) — HEARTBEAT_OK silent response pattern
- [Beads Task Memory](https://github.com/steveyegge/beads) — git-backed task tracking for Claude Code
- [Claude Code Skills Docs](https://code.claude.com/docs/en/skills) — skill creation and frontmatter reference
- [Claude Code Memory Docs](https://code.claude.com/docs/en/memory) — auto memory and CLAUDE.md system
Loading