@pingvinen/donna-assistant 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,2 +1,129 @@
1
- # donna
2
- A Claude skill based personal assistant
1
+ # Donna
2
+
3
+ Your personal, digital version of Harvey Specter's amazing secretary.
4
+
5
+ Donna is an AI-powered personal assistant that manages the work that falls through the cracks — the 1:1 follow-ups, recurring process tasks, stakeholder requests, and self-initiated work that never makes it into Jira or Linear.
6
+
7
+ She lives inside your terminal. She knows your role, surfaces what needs attention each day, pulls data from your CLI tools, and captures tasks in seconds — all backed by plain markdown and git.
8
+
9
+ Donna is designed to be AI-agnostic, but currently only tested with [Claude Code](https://docs.anthropic.com/en/docs/claude-code). She works from any session, in any project — your tasks follow you wherever you work.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npx @pingvinen/donna-assistant
15
+ ```
16
+
17
+ The installer copies skill files into `~/.claude/commands/donna/` and shared runtime into `~/.donna/`. Run it again to upgrade.
18
+
19
+ **Requirements:** Node.js 18+, an AI coding assistant (tested with Claude Code)
20
+
21
+ ## Setup
22
+
23
+ ```
24
+ /donna:setup
25
+ ```
26
+
27
+ Donna will ask you for a **storage repo** — a git repository (local or remote) where all your tasks and daily files live. Everything is plain markdown, committed to git automatically. If you use [Obsidian](https://obsidian.md), you can open this repo as a vault for a free UI.
28
+
29
+ Then define your role so Donna can suggest recurring tasks relevant to your job:
30
+
31
+ ```
32
+ /donna:set-role
33
+ ```
34
+
35
+ ## Daily workflow
36
+
37
+ ### Start the day
38
+
39
+ ```
40
+ /donna:begin-the-day
41
+ ```
42
+
43
+ This is your morning ritual. Donna will:
44
+
45
+ - **Carry forward** open tasks from your last working day (handles weekends)
46
+ - **Surface recurring tasks** that are due today based on your role
47
+ - **Pull fresh data** from configured tools (GitHub PRs, Jira tickets, etc.)
48
+ - **Deduplicate** across all sources and give you a brief
49
+
50
+ ### Capture tasks throughout the day
51
+
52
+ ```
53
+ /donna:add-task Review Sarah's design doc before Thursday
54
+ ```
55
+
56
+ One command, zero prompts, committed to git in seconds. Can also be called without arguments for a more conversational capture.
57
+
58
+ ### Mark tasks done
59
+
60
+ ```
61
+ /donna:done design doc
62
+ ```
63
+
64
+ Fuzzy-matches by name. Run without arguments to pick from a list.
65
+
66
+ ### Refresh tool data mid-day
67
+
68
+ ```
69
+ /donna:run-tools
70
+ ```
71
+
72
+ Re-pulls data from all configured tools. New items appear, resolved items get closed automatically. Your manually-marked tasks always win.
73
+
74
+ ## External tools
75
+
76
+ Donna can pull tasks and status from CLI tools you already have installed. Register them with:
77
+
78
+ ```
79
+ /donna:add-tool gh
80
+ ```
81
+
82
+ Or call it without arguments — Donna will check your role and suggest tools typically associated with your job.
83
+
84
+ Donna verifies the tool is installed and authenticated, learns its capabilities, and asks what scope to query (which repos, projects, namespaces). Any CLI tool can be added — Donna uses the AI model's knowledge of well-known tools and parses `--help` output for everything else.
85
+
86
+ If a tool version is newer than what the AI model knows, Donna will inspect it to learn its current capabilities. You can also re-learn tools at any time:
87
+
88
+ ```
89
+ /donna:relearn-tools
90
+ ```
91
+
92
+ ## How state is stored
93
+
94
+ ```
95
+ <your-storage-repo>/
96
+ donna/
97
+ role.md # your job role definition
98
+ recurring.md # recurring task schedule
99
+ tools.md # registered tool configurations
100
+ daily/
101
+ 2026-03-16.md # today's tasks, tool data, notes
102
+ 2026-03-15.md
103
+ ...
104
+ ```
105
+
106
+ Every change is committed to git. Files are plain markdown with YAML frontmatter — human-readable, hand-editable, and version-controlled.
107
+
108
+ The storage repo is yours. Donna only writes to the `donna/` and `daily/` directories, so you're free to keep any other notes, files, or folder structures alongside it — great if you're already using the repo as an Obsidian vault or similar.
109
+
110
+ ## Idempotent by design
111
+
112
+ Most commands are safe to run again. Want to update your role? Run `/donna:set-role` again. Added a new tool mid-day? Run `/donna:begin-the-day` again to pull its data in. Re-running a command updates state rather than duplicating it.
113
+
114
+ ## All commands
115
+
116
+ | Command | What it does |
117
+ |---------|-------------|
118
+ | `/donna:setup` | First-time configuration (storage repo, directories) |
119
+ | `/donna:set-role` | Define your job role, get recurring task suggestions |
120
+ | `/donna:begin-the-day` | Morning brief with carry-forward, recurring tasks, tool data |
121
+ | `/donna:add-task` | Capture a task instantly |
122
+ | `/donna:done` | Mark a task complete |
123
+ | `/donna:add-tool` | Register an external CLI tool |
124
+ | `/donna:run-tools` | Refresh tool data mid-day |
125
+ | `/donna:relearn-tools` | Update tool knowledge after upgrades |
126
+
127
+ ## License
128
+
129
+ MIT
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ version: "0.4.0",
5
+ description: "Queue standing file move to donna/ subfolder (runs on next skill use)",
6
+ up(ctx) {
7
+ // Standing files live in the user's storage repo, which is not accessible
8
+ // from the migration context. We write a pending flag to state.md so that
9
+ // workflows can detect and execute the move on next skill run.
10
+ const statePath = ctx.path.join(ctx.donnaDir, "state.md");
11
+
12
+ if (ctx.fs.existsSync(statePath)) {
13
+ const content = ctx.fs.readFileSync(statePath, "utf8");
14
+ if (content.includes("move-standing-files")) {
15
+ // Already queued — idempotent, nothing to do
16
+ return;
17
+ }
18
+ }
19
+
20
+ const pendingFlag = "---\npending_migrations:\n - move-standing-files\n---\n";
21
+ ctx.fs.writeFileSync(statePath, pendingFlag, "utf8");
22
+ },
23
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pingvinen/donna-assistant",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Donna - your AI powered personal assistant",
5
5
  "bin": {
6
6
  "donna-assistant": "./bin/donna-assistant"
package/src/installer.cjs CHANGED
@@ -77,7 +77,7 @@ async function run(options = {}) {
77
77
  for (const provider of detected) {
78
78
  fs.cpSync(provider.stubSource, provider.stubTarget, { recursive: true });
79
79
  output.success(
80
- `Copied donna skills (setup, add-task, done, set-role, begin-the-day) to ${provider.stubTarget}`,
80
+ `Copied donna skills (setup, add-task, done, set-role, begin-the-day, add-tool, relearn-tools, run-tools) to ${provider.stubTarget}`,
81
81
  );
82
82
  }
83
83
  } else {
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: donna:add-tool
3
+ description: Declare an external CLI tool and teach Donna its capabilities
4
+ allowed-tools:
5
+ - Read
6
+ - Write
7
+ - Bash
8
+ - AskUserQuestion
9
+ ---
10
+
11
+ <objective>
12
+ Run the Donna add-tool workflow. This command declares external CLI tools (like gh, jira, kubectl) and teaches Donna their capabilities so they can be surfaced in the daily brief.
13
+ </objective>
14
+
15
+ <execution_context>
16
+ @~/.donna/workflows/add-tool.md
17
+ </execution_context>
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: donna:relearn-tools
3
+ description: Re-learn capabilities for tools whose installed version has changed
4
+ allowed-tools:
5
+ - Read
6
+ - Write
7
+ - Bash
8
+ ---
9
+
10
+ <objective>
11
+ Run the Donna relearn-tools workflow. This command checks each registered tool's installed version against its stored version and re-learns capabilities for tools that have been updated.
12
+ </objective>
13
+
14
+ <execution_context>
15
+ @~/.donna/workflows/relearn-tools.md
16
+ </execution_context>
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: donna:run-tools
3
+ description: Run external tools and pull fresh data into today's daily file
4
+ allowed-tools:
5
+ - Read
6
+ - Write
7
+ - Bash
8
+ ---
9
+
10
+ <objective>
11
+ Run the Donna run-tools workflow. This command executes all configured external tool commands, pulls fresh data, and smart-merges results into today's daily file, without running the full begin-the-day carry-forward and recurring task logic.
12
+ </objective>
13
+
14
+ <execution_context>
15
+ @~/.donna/workflows/run-tools.md
16
+ </execution_context>
@@ -21,6 +21,47 @@ Extract the `storage_repo`, `daily_folder` (default: `daily`), and `auto_push` (
21
21
  - Otherwise: do nothing.
22
22
  </step>
23
23
 
24
+ <step name="check-pending-migrations">
25
+ Read `~/.donna/state.md` with the Read tool. If the file does not exist or has no `pending_migrations` field in its YAML frontmatter, skip this step.
26
+
27
+ For each entry in `pending_migrations`:
28
+
29
+ **`move-standing-files`:** Move standing files from storage repo root to donna/ subfolder.
30
+
31
+ Run via Bash:
32
+ ```bash
33
+ STORAGE_REPO="<storage_repo>"
34
+ DONNA_DIR="$STORAGE_REPO/donna"
35
+ MOVED=0
36
+
37
+ mkdir -p "$DONNA_DIR"
38
+ for FILE in role.md recurring.md role-research.md; do
39
+ if [ -f "$STORAGE_REPO/$FILE" ] && [ ! -f "$DONNA_DIR/$FILE" ]; then
40
+ mv "$STORAGE_REPO/$FILE" "$DONNA_DIR/$FILE"
41
+ echo "Moved $FILE to donna/$FILE"
42
+ MOVED=$((MOVED + 1))
43
+ fi
44
+ done
45
+
46
+ echo "MOVED=$MOVED"
47
+ ```
48
+
49
+ If MOVED > 0, commit the move:
50
+ ```bash
51
+ git -C <storage_repo> add -A
52
+ git -C <storage_repo> diff --cached --quiet || git -C <storage_repo> commit -m "donna(migrate): move standing files to donna/ subfolder"
53
+ ```
54
+
55
+ If `auto_push` is true in config, also push.
56
+
57
+ After processing all pending migrations, update `~/.donna/state.md` with the Write tool: remove the completed entries from `pending_migrations`. If no entries remain, write:
58
+ ```markdown
59
+ ---
60
+ pending_migrations: []
61
+ ---
62
+ ```
63
+ </step>
64
+
24
65
  <step name="get-description">
25
66
  The task description is provided as the argument to this command (e.g., `/donna:add-task buy milk`).
26
67
 
@@ -0,0 +1,261 @@
1
+ # Donna Add-Tool Workflow
2
+
3
+ <objective>
4
+ Declare an external CLI tool, verify its installation and authentication, learn its capabilities, and persist the result to tools.md in the storage repo.
5
+ </objective>
6
+
7
+ <step name="read-config">
8
+ Read `~/.config/donna/config.md`.
9
+
10
+ If the file does not exist, print:
11
+ ```
12
+ ✗ Donna is not configured. Run /donna:setup first.
13
+ ```
14
+ Stop.
15
+
16
+ Extract the `storage_repo`, `daily_folder` (default: `daily`), and `auto_push` (default: false) fields from the YAML frontmatter.
17
+
18
+ **Obsidian sync:** Check if `<storage_repo>/.obsidian/daily-notes.json` exists.
19
+ - If it exists and has a `folder` field that differs from `<daily_folder>`: update `<daily_folder>` to match Obsidian's value, and update `~/.config/donna/config.md` with the new `daily_folder`. Print `✓ Synced daily folder with Obsidian: <daily_folder>`.
20
+ - If `<storage_repo>/.obsidian/` exists but `daily-notes.json` does not exist or has no `folder` field: write `<storage_repo>/.obsidian/daily-notes.json` with `{"folder":"<daily_folder>"}`. Print `✓ Configured Obsidian daily notes to use <daily_folder>/`.
21
+ - Otherwise: do nothing.
22
+ </step>
23
+
24
+ <step name="check-pending-migrations">
25
+ Read `~/.donna/state.md` with the Read tool. If the file does not exist or has no `pending_migrations` field in its YAML frontmatter, skip this step.
26
+
27
+ For each entry in `pending_migrations`:
28
+
29
+ **`move-standing-files`:** Move standing files from storage repo root to donna/ subfolder.
30
+
31
+ Run via Bash:
32
+ ```bash
33
+ STORAGE_REPO="<storage_repo>"
34
+ DONNA_DIR="$STORAGE_REPO/donna"
35
+ MOVED=0
36
+
37
+ mkdir -p "$DONNA_DIR"
38
+ for FILE in role.md recurring.md role-research.md; do
39
+ if [ -f "$STORAGE_REPO/$FILE" ] && [ ! -f "$DONNA_DIR/$FILE" ]; then
40
+ mv "$STORAGE_REPO/$FILE" "$DONNA_DIR/$FILE"
41
+ echo "Moved $FILE to donna/$FILE"
42
+ MOVED=$((MOVED + 1))
43
+ fi
44
+ done
45
+
46
+ echo "MOVED=$MOVED"
47
+ ```
48
+
49
+ If MOVED > 0, commit the move:
50
+ ```bash
51
+ git -C <storage_repo> add -A
52
+ git -C <storage_repo> diff --cached --quiet || git -C <storage_repo> commit -m "donna(migrate): move standing files to donna/ subfolder"
53
+ ```
54
+
55
+ If `auto_push` is true in config, also push.
56
+
57
+ After processing all pending migrations, update `~/.donna/state.md` with the Write tool: remove the completed entries from `pending_migrations`. If no entries remain, write:
58
+ ```markdown
59
+ ---
60
+ pending_migrations: []
61
+ ---
62
+ ```
63
+ </step>
64
+
65
+ <step name="detect-noted-tools">
66
+ Read `<storage_repo>/donna/role.md` and `<storage_repo>/donna/role-research.md` with the Read tool. If either file does not exist, skip reading that file.
67
+
68
+ Look for lines containing tool names noted by set-role's approve-tools step. These lines follow the pattern `✓ Noted: <ToolName>` or `- Noted: <ToolName>`. Collect the noted tool names as `<noted_tools>`.
69
+
70
+ If `<noted_tools>` has more than one entry and no specific tool name was provided as argument to the command, offer batch mode via AskUserQuestion:
71
+
72
+ ```
73
+ Set-role noted these tools: <list>. Configure all of them now? (yes/no)
74
+ ```
75
+
76
+ If yes, set batch mode active and process each tool sequentially through steps 4–11 below. If no, proceed to ask-tool-name for a single tool.
77
+ </step>
78
+
79
+ <step name="ask-tool-name">
80
+ If a tool name was provided as argument (e.g. `/donna:add-tool gh`), use it directly. Otherwise, use AskUserQuestion:
81
+
82
+ ```
83
+ What tool would you like to add? (e.g. gh, jira, kubectl)
84
+ ```
85
+
86
+ Store the answer as `<tool_name>`.
87
+
88
+ Resolve the CLI command — default to the tool name itself. If the tool name is in `<noted_tools>`, pre-fill any context from set-role notes. Use AskUserQuestion to confirm or override the CLI command:
89
+
90
+ ```
91
+ CLI command for <tool_name>? (default: <tool_name>)
92
+ ```
93
+
94
+ If the user presses enter without typing, use the default. Store the confirmed CLI command as `<command>`.
95
+ </step>
96
+
97
+ <step name="verify-installation">
98
+ Run via Bash:
99
+ ```bash
100
+ which <command> && echo "INSTALLED=true" || echo "INSTALLED=false"
101
+ ```
102
+
103
+ If INSTALLED=false, print:
104
+ ```
105
+ ! <command> is not installed. You can still save it, but data pulling won't work until you install it.
106
+ ```
107
+ Continue (do not stop — user may want to pre-configure).
108
+
109
+ If INSTALLED=true, run:
110
+ ```bash
111
+ <command> --version 2>/dev/null | head -1
112
+ ```
113
+
114
+ Store the output as `<version>`. If the command does not support `--version`, store `<version>` as `unknown`.
115
+ </step>
116
+
117
+ <step name="auth-test">
118
+ For well-known tools, run the appropriate auth test via Bash with a 10-second timeout:
119
+
120
+ - `gh`: `timeout 10 gh api user --jq '.login' 2>&1`
121
+ - `jira`: `timeout 10 jira me 2>&1`
122
+ - `kubectl`: `timeout 10 kubectl auth whoami 2>&1`
123
+ - Other tools: skip auth test, print `ℹ No known auth test for <command>. Verify manually.`
124
+
125
+ On success (exit 0): print `✓ Authenticated as <output>`.
126
+
127
+ On failure (non-zero exit): print `! Authentication failed for <command>. Fix: <tool-specific fix instructions>`.
128
+
129
+ Fix instructions per tool:
130
+ - `gh` → run `gh auth login`
131
+ - `jira` → run `jira init`
132
+ - `kubectl` → check your kubeconfig
133
+
134
+ Continue (do not stop — user may want to save the tool despite auth issues).
135
+ </step>
136
+
137
+ <step name="ask-scope">
138
+ Ask the user to define the scope/context for this tool. Different tools need different scope:
139
+
140
+ - **gh**: Which GitHub orgs or repos to pull from (e.g., `mycompany`, `mycompany/api mycompany/web`)
141
+ - **jira**: Which Jira project(s) or board(s) (e.g., `AUTH`, `AUTH PLAT`)
142
+ - **kubectl**: Which namespace(s) or cluster(s) (e.g., `production`, `staging production`)
143
+ - **Other tools**: Any relevant filtering context
144
+
145
+ Use AskUserQuestion:
146
+ ```
147
+ What scope should Donna use for <tool_name>?
148
+ (e.g., for gh: which orgs/repos; for jira: which projects)
149
+ Leave blank for no filtering.
150
+ ```
151
+
152
+ Store the answer as `<scope>`. If blank, set to empty string.
153
+ </step>
154
+
155
+ <step name="learn-capabilities">
156
+ Determine if the tool is well-known (gh, jira, kubectl) or unknown. For well-known tools, synthesize capabilities from training data. Do NOT parse --help for well-known tools.
157
+
158
+ **gh (GitHub CLI) — training data baseline:**
159
+
160
+ If `<scope>` is set, add `--owner=<org>` to each search command for each org in scope (space-separated). If scope contains specific repos (format `owner/repo`), use `--repo=<owner/repo>` instead. If scope is empty, do not add owner/repo filters.
161
+
162
+ - list-assigned-prs: `gh search prs --assignee=@me --state=open --json number,title,url --limit 20`
163
+ - list-review-requests: `gh search prs --review-requested=@me --state=open --json number,title,url --limit 20`
164
+ - list-assigned-issues: `gh search issues --assignee=@me --state=open --json number,title,url --limit 20`
165
+
166
+ **jira (ankitpokhrel/jira-cli) — training data baseline:**
167
+
168
+ If `<scope>` is set, add `-p<project>` to each command for each project in scope (space-separated). If scope is empty, do not add project filters.
169
+
170
+ - list-sprint-issues: `jira sprint list --current -a$(jira me) --plain`
171
+ - list-my-issues: `jira issue list -a$(jira me) --plain`
172
+
173
+ **kubectl — training data baseline:**
174
+
175
+ If `<scope>` is set, replace `--all-namespaces` with `-n <namespace>` for each namespace in scope. If scope is empty, keep `--all-namespaces`.
176
+
177
+ - list-pods: `kubectl get pods --all-namespaces --field-selector=status.phase!=Succeeded -o wide`
178
+ - list-failing: `kubectl get pods --all-namespaces --field-selector=status.phase=Failed -o wide`
179
+
180
+ For **unknown tools**, run `<command> --help 2>&1 | head -80` via Bash and use Claude's understanding to identify 3–5 capabilities relevant to daily task management. If `<scope>` is set, incorporate the scope into the CLI invocations where appropriate. Format each as `name: <cli invocation>`.
181
+
182
+ Store the full list as `<available_capabilities>`.
183
+ </step>
184
+
185
+ <step name="select-capabilities">
186
+ Present `<available_capabilities>` to the user via AskUserQuestion with multi-select. All capabilities are checked by default. The question text:
187
+
188
+ ```
189
+ Which capabilities should Donna use for <tool_name>? (deselect any you don't want, or type additional ones)
190
+ ```
191
+
192
+ If the user types additional capabilities as free text, parse them into the same `name: command` format.
193
+
194
+ Store the final selections as `<selected_capabilities>`.
195
+ </step>
196
+
197
+ <step name="write-tools-md">
198
+ Read `<storage_repo>/donna/tools.md` with the Read tool. If the file does not exist, create it with:
199
+ ```markdown
200
+ ---
201
+ # tools.md — managed by donna:add-tool
202
+ ---
203
+ ```
204
+
205
+ Upsert (not overwrite) the tool's section. Each tool section has this format:
206
+ ```markdown
207
+ ## <tool_name>
208
+
209
+ - command: <command>
210
+ - version: <version>
211
+ - learned: <today's date YYYY-MM-DD>
212
+ - auth_test: <auth_test_command or "none">
213
+ - scope: <scope or "none">
214
+
215
+ ### Capabilities
216
+ - <capability_name>: <cli_invocation>
217
+ ```
218
+
219
+ If a section for this tool already exists in tools.md, replace it entirely. Write the full file back with the Write tool. Preserve all other tool sections unchanged.
220
+ </step>
221
+
222
+ <step name="git-commit">
223
+ Run via Bash:
224
+ ```bash
225
+ git -C <storage_repo> add -A
226
+ ```
227
+
228
+ Check whether there is anything to commit:
229
+ ```bash
230
+ git -C <storage_repo> status --porcelain
231
+ ```
232
+
233
+ If the output is empty, skip the commit and continue.
234
+
235
+ Otherwise, run:
236
+ ```bash
237
+ git -C <storage_repo> commit -m "donna(add-tool): added <tool_name>"
238
+ ```
239
+
240
+ If `auto_push` is true in config, also run:
241
+ ```bash
242
+ git -C <storage_repo> push
243
+ ```
244
+ </step>
245
+
246
+ <step name="confirm">
247
+ Print:
248
+ ```
249
+ ✓ Added <tool_name> to tools registry
250
+ Command: <command>
251
+ Version: <version>
252
+ Capabilities: <count> configured
253
+
254
+ Run /donna:begin-the-day to see tool data in your daily brief.
255
+ ```
256
+
257
+ If in batch mode, repeat steps ask-tool-name through confirm for each remaining tool in `<noted_tools>`, then print a final summary:
258
+ ```
259
+ ✓ Configured <N> tools: <tool_name_1>, <tool_name_2>, ...
260
+ ```
261
+ </step>
@@ -21,6 +21,47 @@ Extract the `storage_repo`, `daily_folder` (default: `daily`), and `auto_push` (
21
21
  - Otherwise: do nothing.
22
22
  </step>
23
23
 
24
+ <step name="check-pending-migrations">
25
+ Read `~/.donna/state.md` with the Read tool. If the file does not exist or has no `pending_migrations` field in its YAML frontmatter, skip this step.
26
+
27
+ For each entry in `pending_migrations`:
28
+
29
+ **`move-standing-files`:** Move standing files from storage repo root to donna/ subfolder.
30
+
31
+ Run via Bash:
32
+ ```bash
33
+ STORAGE_REPO="<storage_repo>"
34
+ DONNA_DIR="$STORAGE_REPO/donna"
35
+ MOVED=0
36
+
37
+ mkdir -p "$DONNA_DIR"
38
+ for FILE in role.md recurring.md role-research.md; do
39
+ if [ -f "$STORAGE_REPO/$FILE" ] && [ ! -f "$DONNA_DIR/$FILE" ]; then
40
+ mv "$STORAGE_REPO/$FILE" "$DONNA_DIR/$FILE"
41
+ echo "Moved $FILE to donna/$FILE"
42
+ MOVED=$((MOVED + 1))
43
+ fi
44
+ done
45
+
46
+ echo "MOVED=$MOVED"
47
+ ```
48
+
49
+ If MOVED > 0, commit the move:
50
+ ```bash
51
+ git -C <storage_repo> add -A
52
+ git -C <storage_repo> diff --cached --quiet || git -C <storage_repo> commit -m "donna(migrate): move standing files to donna/ subfolder"
53
+ ```
54
+
55
+ If `auto_push` is true in config, also push.
56
+
57
+ After processing all pending migrations, update `~/.donna/state.md` with the Write tool: remove the completed entries from `pending_migrations`. If no entries remain, write:
58
+ ```markdown
59
+ ---
60
+ pending_migrations: []
61
+ ---
62
+ ```
63
+ </step>
64
+
24
65
  <step name="get-today">
25
66
  Run via Bash to get today's date, day-of-week, and day-of-month:
26
67
  ```bash
@@ -66,7 +107,7 @@ Do NOT modify the previous file — it is a historical record and must remain un
66
107
  </step>
67
108
 
68
109
  <step name="check-recurring">
69
- Read `<storage_repo>/recurring.md` with the Read tool. If the file does not exist, set `<recurring_tasks>` to an empty list and continue (recurring tasks are optional — set-role may not have been run yet).
110
+ Read `<storage_repo>/donna/recurring.md` with the Read tool. If the file does not exist, set `<recurring_tasks>` to an empty list and continue (recurring tasks are optional — set-role may not have been run yet).
70
111
 
71
112
  Parse each line matching the pattern `- <description>: <interval>`. For "every other" intervals, also parse the `| last_run: YYYY-MM-DD` suffix.
72
113
 
@@ -86,6 +127,46 @@ For each task, determine if it is due today using this logic:
86
127
  Store the descriptions of all due tasks as `<recurring_tasks>` (just the description text, without the interval suffix).
87
128
  </step>
88
129
 
130
+ <step name="pull-tool-data">
131
+ Read `<storage_repo>/donna/tools.md` with the Read tool.
132
+
133
+ If the file does not exist or has no tool sections (no `## ` headers after the frontmatter), set `<tool_tasks>` to an empty list and `<tool_warnings>` to an empty list. Continue to the next step. Do NOT print any error — tools are optional.
134
+
135
+ For each tool section in tools.md, parse the `command` field and the `### Capabilities` entries. For each capability entry (format: `- <name>: <cli_invocation>`):
136
+
137
+ Run the CLI invocation via Bash with a 10-second timeout:
138
+ ```bash
139
+ timeout 10 <cli_invocation> 2>&1
140
+ ```
141
+
142
+ **On success (exit 0):**
143
+ Parse the output into task entries. Every task line MUST include both a tool tag and a descriptive link.
144
+
145
+ CRITICAL — tool tag format: Every tool task line MUST start with `(<tool_name>)` after the checkbox, where `<tool_name>` is the `## <tool_name>` section header from tools.md (e.g., `gh`, `jira`, `kubectl`). This tag identifies which tool sourced the task. Example: if processing the `## gh` section, every task line starts with `- [ ] (gh) `.
146
+
147
+ For `gh` JSON output (`--json number,title,url`): parse the JSON array. Extract `<owner>/<repo>` from the `url` field (e.g., `https://github.com/acme/api/pull/42` → `acme/api`). For each item, create:
148
+ `- [ ] (gh) <title> [<owner/repo>#<number>](<url>)`
149
+
150
+ For `jira` plain output: parse each row. For each issue, create:
151
+ `- [ ] (jira) <summary> [<key>](https://<jira_host>/browse/<key>)`
152
+ Note: jira plain output may not include URLs — if the URL is not available, use the issue key only: `- [ ] (jira) <summary> [<key>](<key>)`
153
+
154
+ For other tools: use Claude's understanding to extract task-like items from the output. Format with a descriptive identifier as the link text and URL if available:
155
+ `- [ ] (<tool_name>) <description> [<identifier>](<url>)`
156
+
157
+ **On failure (non-zero exit):**
158
+ Determine the failure type from the exit code and output:
159
+ - Exit 124: `! <tool_name>: timed out after 10s — check network connectivity`
160
+ - Exit 1 with "auth" or "login" in output: `! <tool_name>: authentication failed — run \`<auth_fix_command>\``
161
+ - Exit 127 or "not found" in output: `! <tool_name>: command not found — install <command> first`
162
+ - Other: `! <tool_name>: <first line of stderr>`
163
+
164
+ Add the warning to `<tool_warnings>`. Continue to next capability/tool. Never retry. Tool failures must never block manual tasks, carry-forward, or recurring task processing.
165
+
166
+ Collect all task entries as `<tool_tasks>`.
167
+ Collect all warning messages as `<tool_warnings>`.
168
+ </step>
169
+
89
170
  <step name="read-existing-today">
90
171
  If today's daily file already exists, read it with the Read tool. Extract all task lines — both open (`- [ ] ...`) and closed (`- [x] ...`). Store as `<existing_tasks>`.
91
172
 
@@ -95,7 +176,7 @@ If the file does not exist, `<existing_tasks>` is an empty list.
95
176
  <step name="deduplicate">
96
177
  Assemble the full task list using a single-pass deduplication to ensure idempotency:
97
178
 
98
- **Normalization for comparison:** strip `- [ ] ` or `- [x] ` prefix, strip any trailing ` (N times)` suffix (where N is any integer), lowercase all text, trim whitespace.
179
+ **Normalization for comparison:** strip `- [ ] ` or `- [x] ` prefix, strip any leading `(<tool>) ` prefix (tool tag, matching the pattern `\(\w+\) `), strip any trailing ` (N times)` suffix (where N is any integer), strip any trailing ` [<text>](<url>)` suffix (tool source link, matching the pattern ` \[[^\]]+\]\([^\)]+\)`), strip any trailing ` (<reason>)` suffix (e.g. `(merged)`, `(closed)`), lowercase all text, trim whitespace.
99
180
 
100
181
  1. Start with `<existing_tasks>` — both open and closed tasks take priority. Add them all to the final list.
101
182
 
@@ -103,8 +184,12 @@ Assemble the full task list using a single-pass deduplication to ensure idempote
103
184
 
104
185
  3. Add `<recurring_tasks>` as `- [ ] <description>` — for each recurring task, normalize its description and check whether any task already in the final list normalizes to the same value. If no match, add it. If a match exists, skip it.
105
186
 
187
+ 4. Add `<tool_tasks>` as-is — for each tool task, normalize its description and check whether any task already in the final list normalizes to the same value. If no match, add it. If a match exists, skip it.
188
+
106
189
  CRITICAL: A closed task `- [x] Review PRs` must block a recurring `- [ ] Review PRs` from being re-added. Both open AND closed existing tasks count for deduplication.
107
190
 
191
+ CRITICAL: A closed task `- [x] (gh) Review PR #42 [acme/api#42](https://...)` must block a tool task `- [ ] (gh) Review PR #42 [acme/api#42](https://...)` from being re-added. The normalization (strip tool tag + source link) ensures both normalize to "review pr #42".
192
+
108
193
  CRITICAL: If a carried-forward task already exists in today's file (from a previous run of begin-the-day today), do not re-add it or re-increment its counter. The normalization (strip suffix) ensures this.
109
194
 
110
195
  Store the result as `<final_tasks>`.
@@ -126,13 +211,25 @@ date: <today>
126
211
  <existing tasks, preserving their original order and open/closed state>
127
212
  <carried-forward tasks not already in existing>
128
213
  <recurring tasks not already in existing>
214
+
215
+ ## From Tools
216
+ <tool tasks not already in existing — only if there are tool tasks>
217
+
218
+ ## Resolved
219
+ <resolved tool tasks — only if there are resolved items>
220
+
221
+ ## Warnings
222
+ <tool warnings — only if there are warnings>
129
223
  ```
130
224
 
131
225
  Tasks are written in this order: existing tasks first (preserving their original order and state), then carried-forward tasks, then recurring tasks.
226
+
227
+ If `<tool_tasks>` is empty and there are no resolved items, omit the `## From Tools` and `## Resolved` sections entirely.
228
+ If `<tool_warnings>` is empty, omit the `## Warnings` section entirely. Each warning is written as-is (e.g., `! jira: command not found — install jira first`).
132
229
  </step>
133
230
 
134
231
  <step name="update-recurring-last-run">
135
- If any tasks from `<recurring_tasks>` came from "every other" intervals and were added to today's file, update their `last_run` date in `<storage_repo>/recurring.md`.
232
+ If any tasks from `<recurring_tasks>` came from "every other" intervals and were added to today's file, update their `last_run` date in `<storage_repo>/donna/recurring.md`.
136
233
 
137
234
  Read the full recurring.md file. For each such task, find the line and update the `| last_run: <old_date>` suffix to `| last_run: <today>`. If no `last_run` suffix exists, append ` | last_run: <today>` to the line. Write the full file back with the Write tool.
138
235
 
@@ -167,7 +264,7 @@ git -C <storage_repo> push
167
264
  Print the daily brief to the terminal:
168
265
  ```
169
266
  ══════════════════════════════════════
170
- DONNA — Daily Brief for <today>
267
+ Donna — Daily Brief for <today>
171
268
  ══════════════════════════════════════
172
269
  ```
173
270
 
@@ -185,12 +282,25 @@ If there are recurring tasks due today (tasks from `<recurring_tasks>` that were
185
282
  - [ ] Review sprint backlog
186
283
  ```
187
284
 
285
+ If there are tool tasks (tasks from `<tool_tasks>` that were added to the final list), print:
286
+ ```
287
+ ## From Tools
288
+ - [ ] (gh) Review PR #42 [org/repo#42](https://github.com/org/repo/pull/42)
289
+ - [ ] (jira) Implement AUTH-07 [AUTH-07](https://company.atlassian.net/browse/AUTH-07)
290
+ ```
291
+
292
+ If there are tool warnings (`<tool_warnings>` is not empty), print:
293
+ ```
294
+ ## Warnings
295
+ ! gh: authentication failed — run `gh auth login`
296
+ ```
297
+
188
298
  Always end with:
189
299
  ```
190
300
  ══════════════════════════════════════
191
301
  ```
192
302
 
193
- Show ALL tasks — never truncate. If no carried-forward tasks, omit the "## Carried Forward" section. If no recurring tasks due today, omit the "## Due Today" section. If both sections are empty and there are no existing tasks, print:
303
+ Show ALL tasks — never truncate. If no carried-forward tasks, omit the "## Carried Forward" section. If no recurring tasks due today, omit the "## Due Today" section. If no tool tasks, omit the "## From Tools" section. If no tool warnings, omit the "## Warnings" section. If carried-forward, recurring, AND tool tasks are all empty and there are no existing tasks, print:
194
304
  ```
195
305
  No tasks for today — enjoy your day!
196
306
  ```
package/workflows/done.md CHANGED
@@ -21,6 +21,47 @@ Extract the `storage_repo`, `daily_folder` (default: `daily`), and `auto_push` (
21
21
  - Otherwise: do nothing.
22
22
  </step>
23
23
 
24
+ <step name="check-pending-migrations">
25
+ Read `~/.donna/state.md` with the Read tool. If the file does not exist or has no `pending_migrations` field in its YAML frontmatter, skip this step.
26
+
27
+ For each entry in `pending_migrations`:
28
+
29
+ **`move-standing-files`:** Move standing files from storage repo root to donna/ subfolder.
30
+
31
+ Run via Bash:
32
+ ```bash
33
+ STORAGE_REPO="<storage_repo>"
34
+ DONNA_DIR="$STORAGE_REPO/donna"
35
+ MOVED=0
36
+
37
+ mkdir -p "$DONNA_DIR"
38
+ for FILE in role.md recurring.md role-research.md; do
39
+ if [ -f "$STORAGE_REPO/$FILE" ] && [ ! -f "$DONNA_DIR/$FILE" ]; then
40
+ mv "$STORAGE_REPO/$FILE" "$DONNA_DIR/$FILE"
41
+ echo "Moved $FILE to donna/$FILE"
42
+ MOVED=$((MOVED + 1))
43
+ fi
44
+ done
45
+
46
+ echo "MOVED=$MOVED"
47
+ ```
48
+
49
+ If MOVED > 0, commit the move:
50
+ ```bash
51
+ git -C <storage_repo> add -A
52
+ git -C <storage_repo> diff --cached --quiet || git -C <storage_repo> commit -m "donna(migrate): move standing files to donna/ subfolder"
53
+ ```
54
+
55
+ If `auto_push` is true in config, also push.
56
+
57
+ After processing all pending migrations, update `~/.donna/state.md` with the Write tool: remove the completed entries from `pending_migrations`. If no entries remain, write:
58
+ ```markdown
59
+ ---
60
+ pending_migrations: []
61
+ ---
62
+ ```
63
+ </step>
64
+
24
65
  <step name="find-daily-file">
25
66
  Run via Bash to get today's date:
26
67
  ```bash
@@ -41,7 +82,7 @@ Read the daily file with the Read tool.
41
82
 
42
83
  Find all lines matching the pattern `- [ ] <description>` (open tasks). Collect them as `<open_tasks>`. Do not print the list — it will be shown in the next step via AskUserQuestion.
43
84
 
44
- When presenting task descriptions to the user (in the numbered list or match confirmation), strip any trailing ` (N times)` suffix (where N is any integer, e.g., `Follow up with Sarah (3 times)` becomes `Follow up with Sarah`) for cleaner display. Keep the full line internally for file operations.
85
+ When presenting task descriptions to the user (in the numbered list or match confirmation), strip any leading `(<tool>) ` prefix (tool tag, e.g., `(gh) Review PR #42...` becomes `Review PR #42...`), strip any trailing ` (N times)` suffix (where N is any integer, e.g., `Follow up with Sarah (3 times)` becomes `Follow up with Sarah`), and strip any trailing ` [<text>](<url>)` suffix (e.g., `Review PR #42 [org/repo#42](https://github.com/org/repo/pull/42)` becomes `Review PR #42`) for cleaner display. Keep the full line internally for file operations.
45
86
 
46
87
  If no open tasks are found, print:
47
88
  ```
@@ -54,7 +95,7 @@ Stop.
54
95
  Two modes based on whether an argument was provided to this command:
55
96
 
56
97
  **With argument** (e.g., `/donna:done buy milk`):
57
- When fuzzy-matching task descriptions, strip any trailing ` (N times)` suffix (where N is any integer, matching the regex `\(\d+ times\)`, e.g., `Follow up with Sarah (3 times)` becomes `Follow up with Sarah` for matching purposes). This counter is added by begin-the-day's carry-forward and is transparent to task completion.
98
+ When fuzzy-matching task descriptions, strip any trailing ` (N times)` suffix (where N is any integer, matching the regex `\(\d+ times\)`, e.g., `Follow up with Sarah (3 times)` becomes `Follow up with Sarah` for matching purposes). Also strip any leading `(<tool>) ` prefix (tool tag, matching the regex `^\(\w+\) `) and any trailing ` [<text>](<url>)` suffix (source link, matching the regex ` \[[^\]]+\]\([^\)]+\)`, e.g., `(gh) Review PR #42 [org/repo#42](https://github.com/org/repo/pull/42)` becomes `Review PR #42` for matching purposes). These suffixes are added by begin-the-day and are transparent to task completion.
58
99
 
59
100
  Use your natural language understanding to fuzzy-match the argument against the open task descriptions (after stripping the counter suffix). If a match is found, show it and use AskUserQuestion to confirm:
60
101
  ```
@@ -63,7 +104,7 @@ Mark as done: '<task>'? (yes/no)
63
104
  If no match is found, tell the user and list all open tasks.
64
105
 
65
106
  **Without argument**:
66
- When fuzzy-matching task descriptions, strip any trailing ` (N times)` suffix (where N is any integer, matching the regex `\(\d+ times\)`, e.g., `Follow up with Sarah (3 times)` becomes `Follow up with Sarah` for matching purposes). This counter is added by begin-the-day's carry-forward and is transparent to task completion.
107
+ When fuzzy-matching task descriptions, strip any trailing ` (N times)` suffix (where N is any integer, matching the regex `\(\d+ times\)`, e.g., `Follow up with Sarah (3 times)` becomes `Follow up with Sarah` for matching purposes). Also strip any leading `(<tool>) ` prefix (tool tag, matching the regex `^\(\w+\) `) and any trailing ` [<text>](<url>)` suffix (source link, matching the regex ` \[[^\]]+\]\([^\)]+\)`, e.g., `(gh) Review PR #42 [org/repo#42](https://github.com/org/repo/pull/42)` becomes `Review PR #42` for matching purposes). These suffixes are added by begin-the-day and are transparent to task completion.
67
108
 
68
109
  Use AskUserQuestion to ask which task to mark as done. Provide the open task descriptions as options so the user can select from a list. The question text should be:
69
110
  ```
@@ -86,6 +127,8 @@ For each task in `<completed_tasks>`, replace `- [ ] <description>` with `- [x]
86
127
 
87
128
  When marking a carry-forward task as complete, strip the ` (N times)` suffix from the completed line. The completed task should read `- [x] Follow up with Sarah` (clean, no counter).
88
129
 
130
+ When marking a tool-sourced task as complete, keep both the `(<tool>)` prefix and `[<identifier>](<url>)` suffix on the completed line (they serve as provenance). The completed task should read `- [x] (gh) Review PR #42 [org/repo#42](https://github.com/org/repo/pull/42)` (tool tag and source link preserved for traceability).
131
+
89
132
  Write the updated file with the Write tool.
90
133
  </step>
91
134
 
@@ -0,0 +1,180 @@
1
+ # Donna Relearn-Tools Workflow
2
+
3
+ <objective>
4
+ Check each registered tool's installed version against its stored version and re-learn capabilities for tools that have been updated. Tools at the same version are skipped.
5
+ </objective>
6
+
7
+ <step name="read-config">
8
+ Read `~/.config/donna/config.md`.
9
+
10
+ If the file does not exist, print:
11
+ ```
12
+ ✗ Donna is not configured. Run /donna:setup first.
13
+ ```
14
+ Stop.
15
+
16
+ Extract the `storage_repo`, `daily_folder` (default: `daily`), and `auto_push` (default: false) fields from the YAML frontmatter.
17
+
18
+ **Obsidian sync:** Check if `<storage_repo>/.obsidian/daily-notes.json` exists.
19
+ - If it exists and has a `folder` field that differs from `<daily_folder>`: update `<daily_folder>` to match Obsidian's value, and update `~/.config/donna/config.md` with the new `daily_folder`. Print `✓ Synced daily folder with Obsidian: <daily_folder>`.
20
+ - If `<storage_repo>/.obsidian/` exists but `daily-notes.json` does not exist or has no `folder` field: write `<storage_repo>/.obsidian/daily-notes.json` with `{"folder":"<daily_folder>"}`. Print `✓ Configured Obsidian daily notes to use <daily_folder>/`.
21
+ - Otherwise: do nothing.
22
+ </step>
23
+
24
+ <step name="check-pending-migrations">
25
+ Read `~/.donna/state.md` with the Read tool. If the file does not exist or has no `pending_migrations` field in its YAML frontmatter, skip this step.
26
+
27
+ For each entry in `pending_migrations`:
28
+
29
+ **`move-standing-files`:** Move standing files from storage repo root to donna/ subfolder.
30
+
31
+ Run via Bash:
32
+ ```bash
33
+ STORAGE_REPO="<storage_repo>"
34
+ DONNA_DIR="$STORAGE_REPO/donna"
35
+ MOVED=0
36
+
37
+ mkdir -p "$DONNA_DIR"
38
+ for FILE in role.md recurring.md role-research.md; do
39
+ if [ -f "$STORAGE_REPO/$FILE" ] && [ ! -f "$DONNA_DIR/$FILE" ]; then
40
+ mv "$STORAGE_REPO/$FILE" "$DONNA_DIR/$FILE"
41
+ echo "Moved $FILE to donna/$FILE"
42
+ MOVED=$((MOVED + 1))
43
+ fi
44
+ done
45
+
46
+ echo "MOVED=$MOVED"
47
+ ```
48
+
49
+ If MOVED > 0, commit the move:
50
+ ```bash
51
+ git -C <storage_repo> add -A
52
+ git -C <storage_repo> diff --cached --quiet || git -C <storage_repo> commit -m "donna(migrate): move standing files to donna/ subfolder"
53
+ ```
54
+
55
+ If `auto_push` is true in config, also push.
56
+
57
+ After processing all pending migrations, update `~/.donna/state.md` with the Write tool: remove the completed entries from `pending_migrations`. If no entries remain, write:
58
+ ```markdown
59
+ ---
60
+ pending_migrations: []
61
+ ---
62
+ ```
63
+ </step>
64
+
65
+ <step name="read-tools-md">
66
+ Read `<storage_repo>/donna/tools.md` with the Read tool. If the file does not exist, print:
67
+ ```
68
+ ✗ No tools registered. Run /donna:add-tool first.
69
+ ```
70
+ Stop.
71
+
72
+ Parse each tool section (starting with `## <tool_name>`). For each tool, extract:
73
+ - `command` — the CLI command to run
74
+ - `version` — the stored version string
75
+ - `learned` — the date capabilities were last learned
76
+ - capabilities list under `### Capabilities`
77
+
78
+ Store the parsed tools as `<registered_tools>`.
79
+ </step>
80
+
81
+ <step name="check-versions">
82
+ For each tool in `<registered_tools>`, run via Bash:
83
+ ```bash
84
+ <command> --version 2>/dev/null | head -1
85
+ ```
86
+
87
+ Compare the output against the stored `version` field using simple string equality — "is it different?" is sufficient; no semver parsing required.
88
+
89
+ If `<command>` is not found (command fails), treat it as changed with a warning note.
90
+
91
+ Collect tools into two lists:
92
+ - `<changed_tools>` — installed version differs from stored version (or command not found)
93
+ - `<unchanged_tools>` — installed version matches stored version exactly
94
+ </step>
95
+
96
+ <step name="report-unchanged">
97
+ For each tool in `<unchanged_tools>`, print:
98
+ ```
99
+ ⊘ <tool_name>: unchanged at <version> — skipped
100
+ ```
101
+
102
+ If ALL tools are in `<unchanged_tools>` (no changes found), print:
103
+ ```
104
+ ✓ All tools up to date. Nothing to re-learn.
105
+ ```
106
+ Stop.
107
+ </step>
108
+
109
+ <step name="relearn-changed">
110
+ For each tool in `<changed_tools>`, apply the same learn-capabilities logic as add-tool.md's learn-capabilities step.
111
+
112
+ Determine if the tool is well-known (gh, jira, kubectl) or unknown. For well-known tools, synthesize capabilities from training data. Do NOT parse --help for well-known tools.
113
+
114
+ **gh (GitHub CLI) — training data baseline:**
115
+ - list-assigned-prs: `gh search prs --assignee=@me --state=open --json number,title,url --limit 20`
116
+ - list-review-requests: `gh search prs --review-requested=@me --state=open --json number,title,url --limit 20`
117
+ - list-assigned-issues: `gh search issues --assignee=@me --state=open --json number,title,url --limit 20`
118
+
119
+ **jira (ankitpokhrel/jira-cli) — training data baseline:**
120
+ - list-sprint-issues: `jira sprint list --current -a$(jira me) --plain`
121
+ - list-my-issues: `jira issue list -a$(jira me) --plain`
122
+
123
+ **kubectl — training data baseline:**
124
+ - list-pods: `kubectl get pods --all-namespaces --field-selector=status.phase!=Succeeded -o wide`
125
+ - list-failing: `kubectl get pods --all-namespaces --field-selector=status.phase=Failed -o wide`
126
+
127
+ For **unknown tools**, run `<command> --help 2>&1 | head -80` via Bash and use Claude's understanding to identify 3–5 capabilities relevant to daily task management.
128
+
129
+ Do NOT ask the user to re-select capabilities — keep the same capability names, update only the CLI invocations if the training data suggests changes.
130
+
131
+ Get the new installed version:
132
+ ```bash
133
+ <command> --version 2>/dev/null | head -1
134
+ ```
135
+
136
+ Print:
137
+ ```
138
+ ✓ Re-learned <tool_name> (was <old_version>, now <new_version>)
139
+ ```
140
+ </step>
141
+
142
+ <step name="write-tools-md">
143
+ Update `<storage_repo>/donna/tools.md`: for each re-learned tool, update its `version`, `learned` date (today's date YYYY-MM-DD), and capabilities section with the new invocations.
144
+
145
+ Upsert — replace only the re-learned tool sections, do not remove other tool sections. Preserve all unchanged tool sections exactly as they are.
146
+
147
+ Write the full file back with the Write tool.
148
+ </step>
149
+
150
+ <step name="git-commit">
151
+ Run via Bash:
152
+ ```bash
153
+ git -C <storage_repo> add -A
154
+ ```
155
+
156
+ Check whether there is anything to commit:
157
+ ```bash
158
+ git -C <storage_repo> status --porcelain
159
+ ```
160
+
161
+ If the output is empty, skip the commit and continue.
162
+
163
+ Otherwise, run:
164
+ ```bash
165
+ git -C <storage_repo> commit -m "donna(relearn-tools): updated <count> tool(s)"
166
+ ```
167
+
168
+ If `auto_push` is true in config, also run:
169
+ ```bash
170
+ git -C <storage_repo> push
171
+ ```
172
+ </step>
173
+
174
+ <step name="confirm">
175
+ Print summary:
176
+ ```
177
+ ✓ Re-learned <count> tool(s): <tool_names>
178
+ <count> tool(s) unchanged: <tool_names>
179
+ ```
180
+ </step>
@@ -0,0 +1,214 @@
1
+ # Donna Run-Tools Workflow
2
+
3
+ <objective>
4
+ Execute all configured external tool commands, pull fresh data, and smart-merge results into today's daily file. Does not run the full begin-the-day carry-forward or recurring task logic — this is a mid-day update only.
5
+ </objective>
6
+
7
+ <step name="read-config">
8
+ Read `~/.config/donna/config.md`.
9
+
10
+ If the file does not exist, print:
11
+ ```
12
+ ✗ Donna is not configured. Run /donna:setup first.
13
+ ```
14
+ Stop.
15
+
16
+ Extract the `storage_repo`, `daily_folder` (default: `daily`), and `auto_push` (default: false) fields from the YAML frontmatter.
17
+
18
+ **Obsidian sync:** Check if `<storage_repo>/.obsidian/daily-notes.json` exists.
19
+ - If it exists and has a `folder` field that differs from `<daily_folder>`: update `<daily_folder>` to match Obsidian's value, and update `~/.config/donna/config.md` with the new `daily_folder`. Print `✓ Synced daily folder with Obsidian: <daily_folder>`.
20
+ - If `<storage_repo>/.obsidian/` exists but `daily-notes.json` does not exist or has no `folder` field: write `<storage_repo>/.obsidian/daily-notes.json` with `{"folder":"<daily_folder>"}`. Print `✓ Configured Obsidian daily notes to use <daily_folder>/`.
21
+ - Otherwise: do nothing.
22
+ </step>
23
+
24
+ <step name="check-pending-migrations">
25
+ Read `~/.donna/state.md` with the Read tool. If the file does not exist or has no `pending_migrations` field in its YAML frontmatter, skip this step.
26
+
27
+ For each entry in `pending_migrations`:
28
+
29
+ **`move-standing-files`:** Move standing files from storage repo root to donna/ subfolder.
30
+
31
+ Run via Bash:
32
+ ```bash
33
+ STORAGE_REPO="<storage_repo>"
34
+ DONNA_DIR="$STORAGE_REPO/donna"
35
+ MOVED=0
36
+
37
+ mkdir -p "$DONNA_DIR"
38
+ for FILE in role.md recurring.md role-research.md; do
39
+ if [ -f "$STORAGE_REPO/$FILE" ] && [ ! -f "$DONNA_DIR/$FILE" ]; then
40
+ mv "$STORAGE_REPO/$FILE" "$DONNA_DIR/$FILE"
41
+ echo "Moved $FILE to donna/$FILE"
42
+ MOVED=$((MOVED + 1))
43
+ fi
44
+ done
45
+
46
+ echo "MOVED=$MOVED"
47
+ ```
48
+
49
+ If MOVED > 0, commit the move:
50
+ ```bash
51
+ git -C <storage_repo> add -A
52
+ git -C <storage_repo> diff --cached --quiet || git -C <storage_repo> commit -m "donna(migrate): move standing files to donna/ subfolder"
53
+ ```
54
+
55
+ If `auto_push` is true in config, also push.
56
+
57
+ After processing all pending migrations, update `~/.donna/state.md` with the Write tool: remove the completed entries from `pending_migrations`. If no entries remain, write:
58
+ ```markdown
59
+ ---
60
+ pending_migrations: []
61
+ ---
62
+ ```
63
+ </step>
64
+
65
+ <step name="read-tools-md">
66
+ Read `<storage_repo>/donna/tools.md` with the Read tool. If the file does not exist, print:
67
+ ```
68
+ ✗ No tools registered. Run /donna:add-tool first.
69
+ ```
70
+ Stop.
71
+
72
+ Parse each tool section (starting with `## <tool_name>`). For each tool, extract the `command` field and the capabilities list under `### Capabilities`.
73
+
74
+ Store the parsed tools and their capabilities as `<registered_tools>`.
75
+ </step>
76
+
77
+ <step name="find-daily-file">
78
+ Get today's date via Bash:
79
+ ```bash
80
+ date +%Y-%m-%d
81
+ ```
82
+ Store as `<today>`.
83
+
84
+ Construct the path: `<storage_repo>/<daily_folder>/<today>.md`.
85
+
86
+ If the file does not exist, print:
87
+ ```
88
+ ✗ No daily file for today. Run /donna:begin-the-day first.
89
+ ```
90
+ Stop.
91
+ </step>
92
+
93
+ <step name="read-existing-tool-tasks">
94
+ Read today's daily file with the Read tool.
95
+
96
+ Extract all lines from the `## From Tools` section and the `## Resolved` section.
97
+
98
+ For each line, extract the embedded URL from the `[<text>](<url>)` pattern — this URL is the stable identifier for matching. Store as `<existing_tool_lines>`: a map of URL to full line (including its checkbox state `[ ]` or `[x]` and tool tag).
99
+
100
+ If the `## From Tools` section does not exist in today's file, set `<existing_tool_lines>` to an empty map. The section will be created during smart-merge.
101
+ </step>
102
+
103
+ <step name="pull-fresh-data">
104
+ For each tool in `<registered_tools>`, for each capability:
105
+
106
+ Run the capability command via Bash with a 10-second timeout:
107
+ ```bash
108
+ timeout 10 <capability_command> 2>&1
109
+ ```
110
+
111
+ **On success (exit 0):** Parse the output into task entries. Every task line MUST include both a tool tag and a descriptive link.
112
+
113
+ CRITICAL — tool tag format: Every tool task line MUST start with `(<tool_name>)` after the checkbox, where `<tool_name>` is the `## <tool_name>` section header from tools.md (e.g., `gh`, `jira`, `kubectl`). This tag identifies which tool sourced the task. Example: if processing the `## gh` section, every task line starts with `- [ ] (gh) `.
114
+
115
+ For gh JSON output, extract `number`, `title`, `url` fields from each JSON object. Use the `url` field as the stable identifier. Extract `<owner>/<repo>` from the URL (e.g., `https://github.com/acme/api/pull/42` → `acme/api`). Format:
116
+ ```
117
+ - [ ] (gh) <title> [<owner/repo>#<number>](<url>)
118
+ ```
119
+
120
+ For jira plain output, extract the issue key and summary from each line. Format:
121
+ ```
122
+ - [ ] (jira) <summary> [<issue_key>](https://jira.company.com/browse/<issue_key>)
123
+ ```
124
+
125
+ For other tools: use Claude's understanding to extract task-like items from the output. Format with a descriptive identifier as the link text and URL if available:
126
+ ```
127
+ - [ ] (<tool_name>) <description> [<identifier>](<url>)
128
+ ```
129
+
130
+ **On failure (exit non-zero, including exit 124 for timeout):** Add a warning:
131
+ ```
132
+ ! <tool_name>: <error_description>
133
+ ```
134
+ Continue to the next capability. Never retry.
135
+
136
+ Store all fresh tasks as `<fresh_tool_tasks>`: a map of URL to task line.
137
+ Collect all warning messages as `<tool_warnings>`.
138
+ </step>
139
+
140
+ <step name="smart-merge">
141
+ Apply three-way merge rules in priority order. This is a read-modify-write operation — the entire daily file is rewritten atomically with all sections updated.
142
+
143
+ Read the full current content of today's daily file.
144
+
145
+ **Process existing tool lines** (from `<existing_tool_lines>`):
146
+
147
+ For each URL in `<existing_tool_lines>`:
148
+ 1. If the line is `[x]` (user manually checked): **KEEP AS-IS** — user intent wins (Rule 1). Do not change regardless of tool state.
149
+ 2. If the line is `[ ]` AND the URL exists in `<fresh_tool_tasks>`: **KEEP OPEN** — item is still active (Rule 2).
150
+ 3. If the line is `[ ]` AND the URL is NOT in `<fresh_tool_tasks>`: the item was removed from the tool (reassigned, deleted, or closed).
151
+ - For gh items: check if the PR/issue was closed or merged via Bash: `timeout 10 gh pr view <number> --json state --jq '.state' 2>/dev/null || timeout 10 gh issue view <number> --json state --jq '.state' 2>/dev/null`
152
+ - If closed or merged: change to `- [x] (<tool>) <description> [<identifier>](<url>) (<reason>)` and move to `## Resolved` (Rule 3a).
153
+ - If status check fails or item simply no longer appears: move to `## Resolved` with `(removed)` (Rule 3b).
154
+
155
+ **Add new items** (from `<fresh_tool_tasks>`):
156
+
157
+ For each URL in `<fresh_tool_tasks>` that is NOT in `<existing_tool_lines>`:
158
+ - **ADD** the task line to the `## From Tools` section (Rule 4 — new item).
159
+
160
+ **Rebuild the file:**
161
+
162
+ Rewrite today's daily file with the Write tool, preserving all non-tool sections exactly. The `## From Tools` section contains only currently open tool tasks. The `## Resolved` section contains closed/removed items appended to any existing resolved items.
163
+
164
+ CRITICAL: Remove items from `## From Tools` when moving them to `## Resolved`. Do NOT just append. The entire file is rewritten in one Write operation.
165
+
166
+ If `## From Tools` does not yet exist in the file, add it after the `## Tasks` section. If `## Resolved` does not yet exist, add it at the end of the file when needed.
167
+ </step>
168
+
169
+ <step name="git-commit">
170
+ Run via Bash:
171
+ ```bash
172
+ git -C <storage_repo> add -A
173
+ ```
174
+
175
+ Check whether there is anything to commit:
176
+ ```bash
177
+ git -C <storage_repo> status --porcelain
178
+ ```
179
+
180
+ If the output is empty, skip the commit and continue.
181
+
182
+ Otherwise, run:
183
+ ```bash
184
+ git -C <storage_repo> commit -m "donna(run-tools): refreshed tool data for <today>"
185
+ ```
186
+
187
+ If `auto_push` is true in config, also run:
188
+ ```bash
189
+ git -C <storage_repo> push
190
+ ```
191
+ </step>
192
+
193
+ <step name="print-summary">
194
+ Print:
195
+ ```
196
+ ══════════════════════════════════════
197
+ Donna — Run Tools for <today>
198
+ ══════════════════════════════════════
199
+ ```
200
+
201
+ If new tasks were added (Rule 4), print each new task line.
202
+ If tasks were auto-closed (Rule 3a), print each with reason.
203
+ If tasks were moved to Resolved (Rule 3b), print each with reason.
204
+ If there are warnings from `<tool_warnings>`, print each warning line.
205
+ If nothing changed, print:
206
+ ```
207
+ No changes from tools.
208
+ ```
209
+
210
+ End with:
211
+ ```
212
+ ══════════════════════════════════════
213
+ ```
214
+ </step>
@@ -16,10 +16,51 @@ Stop.
16
16
  Extract the `storage_repo` and `auto_push` (default: false) fields from the YAML frontmatter.
17
17
  </step>
18
18
 
19
+ <step name="check-pending-migrations">
20
+ Read `~/.donna/state.md` with the Read tool. If the file does not exist or has no `pending_migrations` field in its YAML frontmatter, skip this step.
21
+
22
+ For each entry in `pending_migrations`:
23
+
24
+ **`move-standing-files`:** Move standing files from storage repo root to donna/ subfolder.
25
+
26
+ Run via Bash:
27
+ ```bash
28
+ STORAGE_REPO="<storage_repo>"
29
+ DONNA_DIR="$STORAGE_REPO/donna"
30
+ MOVED=0
31
+
32
+ mkdir -p "$DONNA_DIR"
33
+ for FILE in role.md recurring.md role-research.md; do
34
+ if [ -f "$STORAGE_REPO/$FILE" ] && [ ! -f "$DONNA_DIR/$FILE" ]; then
35
+ mv "$STORAGE_REPO/$FILE" "$DONNA_DIR/$FILE"
36
+ echo "Moved $FILE to donna/$FILE"
37
+ MOVED=$((MOVED + 1))
38
+ fi
39
+ done
40
+
41
+ echo "MOVED=$MOVED"
42
+ ```
43
+
44
+ If MOVED > 0, commit the move:
45
+ ```bash
46
+ git -C <storage_repo> add -A
47
+ git -C <storage_repo> diff --cached --quiet || git -C <storage_repo> commit -m "donna(migrate): move standing files to donna/ subfolder"
48
+ ```
49
+
50
+ If `auto_push` is true in config, also push.
51
+
52
+ After processing all pending migrations, update `~/.donna/state.md` with the Write tool: remove the completed entries from `pending_migrations`. If no entries remain, write:
53
+ ```markdown
54
+ ---
55
+ pending_migrations: []
56
+ ---
57
+ ```
58
+ </step>
59
+
19
60
  <step name="check-existing-role">
20
61
  Run via Bash:
21
62
  ```bash
22
- test -f <storage_repo>/role.md && echo "exists" || echo "missing"
63
+ test -f <storage_repo>/donna/role.md && echo "exists" || echo "missing"
23
64
  ```
24
65
 
25
66
  If the output is "exists", proceed to the rerun-menu step.
@@ -39,8 +80,8 @@ A role definition already exists. What would you like to do?
39
80
  ```
40
81
 
41
82
  - On "reset" (option 1): proceed to ask-role (fresh start, will overwrite role.md and recurring.md).
42
- - On "diff-update" (option 2): read current role.md with the Read tool, proceed to ask-role but pre-fill with current values and note this is an update. After research, show delta (added/removed recurring tasks vs current recurring.md). Preserve any manually-added recurring tasks (tasks in recurring.md not in the research suggestions).
43
- - On "re-research" (option 3): read current role.md with the Read tool to get the existing role data, skip to the research step.
83
+ - On "diff-update" (option 2): read current `<storage_repo>/donna/role.md` with the Read tool, proceed to ask-role but pre-fill with current values and note this is an update. After research, show delta (added/removed recurring tasks vs current `<storage_repo>/donna/recurring.md`). Preserve any manually-added recurring tasks (tasks in recurring.md not in the research suggestions).
84
+ - On "re-research" (option 3): read current `<storage_repo>/donna/role.md` with the Read tool to get the existing role data, skip to the research step.
44
85
  - On "Cancel" (option 4): print "Cancelled." and stop.
45
86
  </step>
46
87
 
@@ -132,7 +173,7 @@ Do NOT create tools.md or configure anything — only note the user's interest.
132
173
  </step>
133
174
 
134
175
  <step name="save-role">
135
- Write `<storage_repo>/role.md` with the Write tool.
176
+ Write `<storage_repo>/donna/role.md` with the Write tool.
136
177
 
137
178
  Use this format (substituting actual values):
138
179
  ```markdown
@@ -151,7 +192,7 @@ updated: <today's date in YYYY-MM-DD format>
151
192
  [Write a 2–3 sentence prose summary of the role as described by the user, incorporating their key responsibilities and team context.]
152
193
  ```
153
194
 
154
- Write `<storage_repo>/role-research.md` with the Write tool.
195
+ Write `<storage_repo>/donna/role-research.md` with the Write tool.
155
196
 
156
197
  Use this format:
157
198
  ```markdown
@@ -185,7 +226,7 @@ role: <job_title>
185
226
  </step>
186
227
 
187
228
  <step name="save-recurring">
188
- Write `<storage_repo>/recurring.md` with the Write tool.
229
+ Write `<storage_repo>/donna/recurring.md` with the Write tool.
189
230
 
190
231
  Format: one approved recurring task per line as `- Task description: interval`.
191
232
  For "every other" intervals (biweekly, every other Monday, etc.), append ` | last_run: <today's date>` suffix.
@@ -202,7 +243,7 @@ Use this format:
202
243
  ```
203
244
 
204
245
  If this is a diff-update (user chose option 2 in rerun-menu):
205
- 1. Read the existing `<storage_repo>/recurring.md` with the Read tool.
246
+ 1. Read the existing `<storage_repo>/donna/recurring.md` with the Read tool.
206
247
  2. Identify manually-added tasks: tasks in recurring.md that were NOT in the research suggestions.
207
248
  3. Merge: keep manually-added tasks, add new approved tasks, remove tasks the user rejected.
208
249
  4. Write the merged result.
@@ -103,16 +103,17 @@ Set `<daily_folder>` to `daily`. Print:
103
103
  </step>
104
104
 
105
105
  <step name="create-storage-structure">
106
- Create the daily directory:
106
+ Create the daily and donna directories:
107
107
 
108
108
  Run via Bash:
109
109
  ```bash
110
110
  mkdir -p <repo>/<daily_folder>
111
+ mkdir -p <repo>/donna
111
112
  ```
112
113
 
113
114
  Print:
114
115
  ```
115
- ✓ Created <daily_folder>/ directory
116
+ ✓ Created <daily_folder>/ and donna/ directories
116
117
  ```
117
118
  </step>
118
119