@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,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>
@@ -0,0 +1,308 @@
1
+ # Donna Begin-the-Day Workflow
2
+
3
+ <objective>
4
+ Carry forward open tasks from the previous day, surface recurring tasks due today, deduplicate, and present a concise daily brief.
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="get-today">
66
+ Run via Bash to get today's date, day-of-week, and day-of-month:
67
+ ```bash
68
+ date +%Y-%m-%d
69
+ ```
70
+ Store the result as `<today>`.
71
+
72
+ ```bash
73
+ date +%A
74
+ ```
75
+ Store the result as `<day_of_week>` (e.g., "Monday").
76
+
77
+ ```bash
78
+ date +%-d
79
+ ```
80
+ Store the result as `<day_of_month>` (e.g., "1" for the 1st).
81
+
82
+ Construct the daily file path: `<storage_repo>/<daily_folder>/<today>.md`.
83
+ </step>
84
+
85
+ <step name="find-previous-file">
86
+ Find the most recent previous daily file. Run via Bash:
87
+ ```bash
88
+ TODAY=$(date +%Y-%m-%d)
89
+ DAILY_DIR="<storage_repo>/<daily_folder>"
90
+ PREV_FILE=$(ls "$DAILY_DIR"/*.md 2>/dev/null | sort | grep -v "$TODAY" | tail -1)
91
+ echo "${PREV_FILE:-NONE}"
92
+ ```
93
+
94
+ CRITICAL: The `grep -v "$TODAY"` exclusion prevents a self-referencing loop. If the result is "NONE", there is no previous file — skip the carry-forward step. This reads a directory listing only (satisfies STORE-03), then reads at most ONE previous file.
95
+ </step>
96
+
97
+ <step name="carry-forward">
98
+ If the previous file result is "NONE", set `<carried_tasks>` to an empty list and continue to the next step.
99
+
100
+ If a previous file was found, read it with the Read tool. Extract all open tasks — every line matching the pattern `- [ ] <description>`. For each open task:
101
+ - If the description ends with ` (N times)` (where N is any integer): extract N, increment to N+1, replace the suffix with ` (N+1 times)`. For example: `- [ ] Follow up with Sarah (2 times)` becomes `- [ ] Follow up with Sarah (3 times)`.
102
+ - If the description has no such suffix: append ` (1 times)`. For example: `- [ ] Follow up with Sarah` becomes `- [ ] Follow up with Sarah (1 times)`.
103
+
104
+ Store the resulting list as `<carried_tasks>`.
105
+
106
+ Do NOT modify the previous file — it is a historical record and must remain unchanged.
107
+ </step>
108
+
109
+ <step name="check-recurring">
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).
111
+
112
+ Parse each line matching the pattern `- <description>: <interval>`. For "every other" intervals, also parse the `| last_run: YYYY-MM-DD` suffix.
113
+
114
+ For each task, determine if it is due today using this logic:
115
+ - `every <DayName>` (e.g., "every Monday"): due if `<day_of_week>` matches DayName
116
+ - `every weekday`: due if `<day_of_week>` is Monday, Tuesday, Wednesday, Thursday, or Friday
117
+ - `first <DayName> of month`: due if `<day_of_week>` matches DayName AND `<day_of_month>` <= 7
118
+ - `every other <DayName>` with `| last_run: YYYY-MM-DD`: due if `<day_of_week>` matches DayName AND days since last_run >= 14. Use macOS-compatible date arithmetic:
119
+ ```bash
120
+ LAST_RUN="<last_run_date>"
121
+ last_run_epoch=$(date -j -f "%Y-%m-%d" "$LAST_RUN" "+%s")
122
+ today_epoch=$(date +%s)
123
+ days=$(( (today_epoch - last_run_epoch) / 86400 ))
124
+ echo "$days"
125
+ ```
126
+
127
+ Store the descriptions of all due tasks as `<recurring_tasks>` (just the description text, without the interval suffix).
128
+ </step>
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
+
170
+ <step name="read-existing-today">
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>`.
172
+
173
+ If the file does not exist, `<existing_tasks>` is an empty list.
174
+ </step>
175
+
176
+ <step name="deduplicate">
177
+ Assemble the full task list using a single-pass deduplication to ensure idempotency:
178
+
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.
180
+
181
+ 1. Start with `<existing_tasks>` — both open and closed tasks take priority. Add them all to the final list.
182
+
183
+ 2. Add `<carried_tasks>` — for each carried task, normalize its description and check whether any task already in the final list (from existing_tasks) normalizes to the same value. If no match, add it. If a match exists, skip it.
184
+
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.
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
+
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.
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
+
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.
194
+
195
+ Store the result as `<final_tasks>`.
196
+ </step>
197
+
198
+ <step name="write-daily-file">
199
+ Ensure the daily folder exists:
200
+ ```bash
201
+ mkdir -p <storage_repo>/<daily_folder>
202
+ ```
203
+
204
+ Write today's daily file with the Write tool. Content format:
205
+ ```markdown
206
+ ---
207
+ date: <today>
208
+ ---
209
+
210
+ ## Tasks
211
+ <existing tasks, preserving their original order and open/closed state>
212
+ <carried-forward tasks not already in existing>
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>
223
+ ```
224
+
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`).
229
+ </step>
230
+
231
+ <step name="update-recurring-last-run">
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`.
233
+
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.
235
+
236
+ If no "every other" recurring tasks were added, skip this step.
237
+ </step>
238
+
239
+ <step name="git-commit">
240
+ Run via Bash:
241
+ ```bash
242
+ git -C <storage_repo> add -A
243
+ ```
244
+
245
+ Check whether there is anything to commit:
246
+ ```bash
247
+ git -C <storage_repo> status --porcelain
248
+ ```
249
+
250
+ If the output is empty, skip the commit and continue.
251
+
252
+ Otherwise, run:
253
+ ```bash
254
+ git -C <storage_repo> commit -m "donna(begin-the-day): daily brief for <today>"
255
+ ```
256
+
257
+ If `auto_push` is true in config, also run:
258
+ ```bash
259
+ git -C <storage_repo> push
260
+ ```
261
+ </step>
262
+
263
+ <step name="print-brief">
264
+ Print the daily brief to the terminal:
265
+ ```
266
+ ══════════════════════════════════════
267
+ Donna — Daily Brief for <today>
268
+ ══════════════════════════════════════
269
+ ```
270
+
271
+ If there are carried-forward tasks (tasks from `<carried_tasks>` that were added to the final list), print:
272
+ ```
273
+ ## Carried Forward
274
+ - [ ] Follow up with Sarah (3 times)
275
+ - [ ] Review design doc (1 times)
276
+ ```
277
+
278
+ If there are recurring tasks due today (tasks from `<recurring_tasks>` that were added to the final list), print:
279
+ ```
280
+ ## Due Today
281
+ - [ ] Check team Slack
282
+ - [ ] Review sprint backlog
283
+ ```
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
+
298
+ Always end with:
299
+ ```
300
+ ══════════════════════════════════════
301
+ ```
302
+
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:
304
+ ```
305
+ No tasks for today — enjoy your day!
306
+ ```
307
+ in place of both sections.
308
+ </step>