@pingvinen/donna-assistant 0.3.2 → 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.
@@ -0,0 +1,169 @@
1
+ # Donna Done Workflow
2
+
3
+ <objective>
4
+ Mark one or more tasks as complete in today's daily journal file and commit the change to git.
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="find-daily-file">
66
+ Run via Bash to get today's date:
67
+ ```bash
68
+ date +%Y-%m-%d
69
+ ```
70
+
71
+ Store the result as `<date>`. Construct the daily file path: `<storage_repo>/<daily_folder>/<date>.md`.
72
+
73
+ If the file does not exist, print:
74
+ ```
75
+ ✗ No daily file for today. Add a task first with /donna:add-task
76
+ ```
77
+ Stop.
78
+ </step>
79
+
80
+ <step name="read-tasks">
81
+ Read the daily file with the Read tool.
82
+
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.
84
+
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.
86
+
87
+ If no open tasks are found, print:
88
+ ```
89
+ ✓ All tasks already complete for today!
90
+ ```
91
+ Stop.
92
+ </step>
93
+
94
+ <step name="select-tasks">
95
+ Two modes based on whether an argument was provided to this command:
96
+
97
+ **With argument** (e.g., `/donna:done buy milk`):
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.
99
+
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:
101
+ ```
102
+ Mark as done: '<task>'? (yes/no)
103
+ ```
104
+ If no match is found, tell the user and list all open tasks.
105
+
106
+ **Without argument**:
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.
108
+
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:
110
+ ```
111
+ Which task(s) did you finish? (or type something you did that's not on the list)
112
+ ```
113
+
114
+ If the user submits without selecting anything or typing anything, print:
115
+ ```
116
+ Nothing selected — nothing to do.
117
+ ```
118
+ Stop.
119
+
120
+ If the user selects from the list, store the selected task(s) as `<completed_tasks>`.
121
+
122
+ If the user types free text instead of selecting an option, treat it as a task that was already done: append `- [x] <typed text>` to the daily file (creating it if needed, same as add-task's ensure-daily-file step). Store it as `<completed_tasks>` so the git-commit and confirm steps handle it normally.
123
+ </step>
124
+
125
+ <step name="mark-complete">
126
+ For each task in `<completed_tasks>`, replace `- [ ] <description>` with `- [x] <description>` in the daily file.
127
+
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).
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
+
132
+ Write the updated file with the Write tool.
133
+ </step>
134
+
135
+ <step name="git-commit">
136
+ Run via Bash:
137
+ ```bash
138
+ git -C <storage_repo> add -A
139
+ ```
140
+
141
+ Check whether there is anything to commit:
142
+ ```bash
143
+ git -C <storage_repo> status --porcelain
144
+ ```
145
+
146
+ If the output is empty, skip the commit and continue.
147
+
148
+ If one task was completed, run:
149
+ ```bash
150
+ git -C <storage_repo> commit -m "donna(done): <description>"
151
+ ```
152
+
153
+ If multiple tasks were completed, run:
154
+ ```bash
155
+ git -C <storage_repo> commit -m "donna(done): <N> tasks completed"
156
+ ```
157
+
158
+ If `auto_push` is true in config, also run:
159
+ ```bash
160
+ git -C <storage_repo> push
161
+ ```
162
+ </step>
163
+
164
+ <step name="confirm">
165
+ For each completed task, print:
166
+ ```
167
+ ✓ Done: <description>
168
+ ```
169
+ </step>
@@ -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>