@pingvinen/donna-assistant 0.9.1 → 0.11.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 +33 -3
- package/package.json +1 -1
- package/src/donna-tools.cjs +429 -0
- package/src/installer.cjs +26 -4
- package/stubs/claude-code/donna/add-follow-up-task.md +17 -0
- package/stubs/claude-code/donna/remove-tool.md +17 -0
- package/workflows/add-task.md +15 -75
- package/workflows/add-tool.md +58 -91
- package/workflows/adjust-tool.md +22 -132
- package/workflows/begin-the-day.md +59 -92
- package/workflows/done.md +16 -74
- package/workflows/focus.md +21 -95
- package/workflows/follow-up.md +163 -0
- package/workflows/relearn-tools.md +60 -91
- package/workflows/remove-tool.md +93 -0
- package/workflows/run-tools.md +26 -99
- package/workflows/set-role.md +12 -61
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Donna Follow-Up Workflow
|
|
2
|
+
|
|
3
|
+
<objective>
|
|
4
|
+
Capture a follow-up task with a future due date, resolve relative time expressions to concrete dates, store in donna/follow-ups.md, and commit via donna-tools.cjs.
|
|
5
|
+
</objective>
|
|
6
|
+
|
|
7
|
+
<step name="init">
|
|
8
|
+
Run via Bash:
|
|
9
|
+
```bash
|
|
10
|
+
INIT=$(node ~/.donna/donna-tools.cjs init)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Parse the JSON response. If the `error` field is `"not_configured"`, print:
|
|
14
|
+
```
|
|
15
|
+
x Donna is not configured. Run /donna:setup first.
|
|
16
|
+
```
|
|
17
|
+
Stop.
|
|
18
|
+
|
|
19
|
+
Extract `storage_repo`, `daily_folder`, `auto_push` from the JSON.
|
|
20
|
+
|
|
21
|
+
If `update_available` is non-null, print:
|
|
22
|
+
```
|
|
23
|
+
Donna v<update_available> available -- run npx @pingvinen/donna-assistant to update
|
|
24
|
+
```
|
|
25
|
+
Continue normally.
|
|
26
|
+
</step>
|
|
27
|
+
|
|
28
|
+
<step name="parse-input">
|
|
29
|
+
The argument is the full user text (e.g., "/donna:add-follow-up-task remind team about Q3 planning in 2 months").
|
|
30
|
+
|
|
31
|
+
**If an argument was provided:**
|
|
32
|
+
Use language understanding to extract two things from the argument:
|
|
33
|
+
1. `<description>`: The task description (everything that is not a time expression)
|
|
34
|
+
2. `<due_expression>`: A time expression (e.g., "in 2 months", "on 2026-09-15", "next Tuesday", "in 3 weeks") — or null if no time expression is found
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
- "remind team about Q3 planning in 2 months" → description: "remind team about Q3 planning", due_expression: "in 2 months"
|
|
38
|
+
- "call dentist on 2026-09-15" → description: "call dentist", due_expression: "on 2026-09-15"
|
|
39
|
+
- "review the design doc" → description: "review the design doc", due_expression: null
|
|
40
|
+
- "/donna:add-follow-up-task schedule 1:1 with Sarah next Tuesday at 2pm" → description: "schedule 1:1 with Sarah", due_expression: "next Tuesday"
|
|
41
|
+
|
|
42
|
+
**If no argument was provided:**
|
|
43
|
+
CRITICAL: Both AskUserQuestion prompts must use free-text input mode — do NOT use a picker with predefined options.
|
|
44
|
+
|
|
45
|
+
Print the following as prose (before asking any questions):
|
|
46
|
+
```
|
|
47
|
+
Time expression examples: "in 2 months", "on 2026-09-15", "next Tuesday", "in 3 weeks", or leave blank for today.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Use AskUserQuestion to ask two questions:
|
|
51
|
+
|
|
52
|
+
First question:
|
|
53
|
+
```
|
|
54
|
+
What task would you like to schedule?
|
|
55
|
+
```
|
|
56
|
+
Store the free-text response as `<description>`.
|
|
57
|
+
|
|
58
|
+
Second question:
|
|
59
|
+
```
|
|
60
|
+
When is it due?
|
|
61
|
+
```
|
|
62
|
+
Store the free-text response as `<due_expression>`. If the user leaves it blank or says "today", set `<due_expression>` to null.
|
|
63
|
+
</step>
|
|
64
|
+
|
|
65
|
+
<step name="resolve-date">
|
|
66
|
+
Three cases for resolving the due date:
|
|
67
|
+
|
|
68
|
+
**Case 1: due_expression is null**
|
|
69
|
+
Use today's date. Run via Bash:
|
|
70
|
+
```bash
|
|
71
|
+
node -e "const d=new Date();const y=d.getFullYear();const m=String(d.getMonth()+1).padStart(2,'0');const dd=String(d.getDate()).padStart(2,'0');console.log(y+'-'+m+'-'+dd)"
|
|
72
|
+
```
|
|
73
|
+
Store the output as `<due_date>`.
|
|
74
|
+
|
|
75
|
+
**Case 2: due_expression matches YYYY-MM-DD format**
|
|
76
|
+
Extract the YYYY-MM-DD part from the expression (strip any leading words like "on"). Store directly as `<due_date>`.
|
|
77
|
+
|
|
78
|
+
**Case 3: due_expression is a relative expression**
|
|
79
|
+
The agent parses the natural language expression into structured {value, unit} pairs:
|
|
80
|
+
- "in 2 months" → months: 2
|
|
81
|
+
- "in 3 weeks" → weeks: 3
|
|
82
|
+
- "in 2 weeks and 3 days" → weeks: 2, days: 3
|
|
83
|
+
- "in 1 month and 2 weeks" → months: 1, weeks: 2
|
|
84
|
+
- "in 2 months and 5 days" → months: 2, days: 5
|
|
85
|
+
- "next Tuesday" → next occurrence of Tuesday from today (count days until the next Tuesday)
|
|
86
|
+
- "tomorrow" → days: 1
|
|
87
|
+
- "in 5 days" → days: 5
|
|
88
|
+
|
|
89
|
+
Inject the arithmetic into a Bash node -e command. Command template:
|
|
90
|
+
```bash
|
|
91
|
+
node -e "
|
|
92
|
+
const d = new Date();
|
|
93
|
+
<apply setMonth first: d.setMonth(d.getMonth() + N)>
|
|
94
|
+
<apply setDate next: d.setDate(d.getDate() + N)>
|
|
95
|
+
<apply setFullYear last: d.setFullYear(d.getFullYear() + N)>
|
|
96
|
+
const yyyy = d.getFullYear();
|
|
97
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
98
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
99
|
+
console.log(yyyy + '-' + mm + '-' + dd);
|
|
100
|
+
"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Example for "in 2 months and 5 days":
|
|
104
|
+
```bash
|
|
105
|
+
node -e "const d=new Date();d.setMonth(d.getMonth()+2);d.setDate(d.getDate()+5);const y=d.getFullYear();const m=String(d.getMonth()+1).padStart(2,'0');const dd=String(d.getDate()).padStart(2,'0');console.log(y+'-'+m+'-'+dd)"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Example for "in 3 weeks":
|
|
109
|
+
```bash
|
|
110
|
+
node -e "const d=new Date();d.setDate(d.getDate()+21);const y=d.getFullYear();const m=String(d.getMonth()+1).padStart(2,'0');const dd=String(d.getDate()).padStart(2,'0');console.log(y+'-'+m+'-'+dd)"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
CRITICAL: Always use local date component extraction (`getFullYear()`, `getMonth() + 1`, `getDate()`) with padding. NEVER use `toISOString()` — it returns UTC which can produce off-by-one dates for users in timezones ahead of UTC.
|
|
114
|
+
|
|
115
|
+
If the resolved date is NaN or invalid (check by verifying the output matches YYYY-MM-DD format), print:
|
|
116
|
+
```
|
|
117
|
+
x Invalid date expression: <due_expression>. Use formats like "tomorrow", "next friday", "in 3 days", or YYYY-MM-DD.
|
|
118
|
+
```
|
|
119
|
+
Then stop the workflow. Do NOT fall back to today — the user must correct their date expression.
|
|
120
|
+
|
|
121
|
+
If the date resolved successfully, store the output as `<due_date>`.
|
|
122
|
+
</step>
|
|
123
|
+
|
|
124
|
+
<step name="ensure-file">
|
|
125
|
+
Check if `<storage_repo>/donna/follow-ups.md` exists. Use the Read tool to attempt reading it.
|
|
126
|
+
|
|
127
|
+
If the file does not exist (Read tool returns an error), create it with the Write tool using this content:
|
|
128
|
+
```markdown
|
|
129
|
+
---
|
|
130
|
+
created: <today>
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Follow-ups
|
|
134
|
+
```
|
|
135
|
+
Where `<today>` is today's date in YYYY-MM-DD format.
|
|
136
|
+
|
|
137
|
+
If the file already exists, continue to the next step.
|
|
138
|
+
</step>
|
|
139
|
+
|
|
140
|
+
<step name="append-entry">
|
|
141
|
+
Read `<storage_repo>/donna/follow-ups.md` with the Read tool.
|
|
142
|
+
|
|
143
|
+
Append the follow-up entry on a new line at the end of the file:
|
|
144
|
+
```
|
|
145
|
+
- [ ] <description> | due: <due_date>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Write the updated file back with the Write tool.
|
|
149
|
+
</step>
|
|
150
|
+
|
|
151
|
+
<step name="git-commit">
|
|
152
|
+
Run via Bash:
|
|
153
|
+
```bash
|
|
154
|
+
node ~/.donna/donna-tools.cjs commit "donna(follow-up): <description>" --files donna/follow-ups.md
|
|
155
|
+
```
|
|
156
|
+
</step>
|
|
157
|
+
|
|
158
|
+
<step name="confirm">
|
|
159
|
+
Print:
|
|
160
|
+
```
|
|
161
|
+
✓ Follow-up scheduled: <description> (due: <due_date>)
|
|
162
|
+
```
|
|
163
|
+
</step>
|
|
@@ -4,82 +4,25 @@
|
|
|
4
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
5
|
</objective>
|
|
6
6
|
|
|
7
|
-
<step name="
|
|
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
|
-
|
|
7
|
+
<step name="init">
|
|
31
8
|
Run via Bash:
|
|
32
9
|
```bash
|
|
33
|
-
|
|
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"
|
|
10
|
+
INIT=$(node ~/.donna/donna-tools.cjs init)
|
|
47
11
|
```
|
|
48
12
|
|
|
49
|
-
|
|
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"
|
|
13
|
+
Parse the JSON response. If the `error` field is `"not_configured"`, print:
|
|
53
14
|
```
|
|
54
|
-
|
|
55
|
-
If `auto_push` is true in config, also push.
|
|
56
|
-
|
|
57
|
-
**`backfill-tool-type`:** Backfill `type` on existing tool sections in tools.md using heuristic detection.
|
|
58
|
-
|
|
59
|
-
Read `<storage_repo>/donna/tools.md` with the Read tool. If the file does not exist or has no tool sections, skip this handler.
|
|
60
|
-
|
|
61
|
-
For each tool section (starting with `## <tool_name>`), check if a `- type:` line already exists. If the `- type:` line is missing, detect the correct type:
|
|
62
|
-
|
|
63
|
-
1. If the tool section contains a `- command:` line where the value starts with `mcp:` (e.g., `- command: mcp:linear`), insert `- type: mcp` immediately after the `- command:` line.
|
|
64
|
-
2. Else, if the tool section contains a `- base_url:` line:
|
|
65
|
-
- If the capabilities section contains entries that look like GraphQL queries (contain `query {` or `mutation {`), insert `- type: graphql` immediately after `## <tool_name>` (REST/GraphQL tools have no `- command:` line).
|
|
66
|
-
- Otherwise, insert `- type: rest` immediately after `## <tool_name>`.
|
|
67
|
-
3. Else (no `mcp:` prefix, no `base_url` field), insert `- type: cli` immediately after the `- command:` line.
|
|
68
|
-
|
|
69
|
-
Write the updated file back with the Write tool. If any changes were made, commit:
|
|
70
|
-
```bash
|
|
71
|
-
git -C <storage_repo> add -A
|
|
72
|
-
git -C <storage_repo> diff --cached --quiet || git -C <storage_repo> commit -m "donna(migrate): backfill tool types on existing tools"
|
|
15
|
+
x Donna is not configured. Run /donna:setup first.
|
|
73
16
|
```
|
|
17
|
+
Stop.
|
|
74
18
|
|
|
75
|
-
|
|
19
|
+
Extract `storage_repo`, `daily_folder`, `auto_push` from the JSON.
|
|
76
20
|
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
pending_migrations: []
|
|
81
|
-
---
|
|
21
|
+
If `update_available` is non-null, print:
|
|
22
|
+
```
|
|
23
|
+
Donna v<update_available> available -- run npx @pingvinen/donna-assistant to update
|
|
82
24
|
```
|
|
25
|
+
Continue normally.
|
|
83
26
|
</step>
|
|
84
27
|
|
|
85
28
|
<step name="read-tools-md">
|
|
@@ -114,12 +57,16 @@ If `<type>` is `graphql`:
|
|
|
114
57
|
|
|
115
58
|
**IMPORTANT: Auth is OPTIONAL for GraphQL tools. Public APIs work without any secret. You MUST always attempt introspection regardless of whether auth is configured. NEVER skip a GraphQL tool just because it has no auth_secret.**
|
|
116
59
|
|
|
117
|
-
1.
|
|
60
|
+
1. Resolve the auth secret via Bash:
|
|
61
|
+
```bash
|
|
62
|
+
node ~/.donna/donna-tools.cjs resolve-secret <auth_secret>
|
|
63
|
+
```
|
|
64
|
+
Parse the JSON response. If `error` is `"key_not_found"` or `"placeholder_value"`, set `<resolved_secret>` to empty (no auth). Otherwise extract the `value` field as `<resolved_secret>`. **An empty resolved_secret is perfectly valid — proceed to step 2 regardless.**
|
|
118
65
|
|
|
119
|
-
2. Run via Bash with a 15-second timeout. Include the auth header only when a real secret was resolved:
|
|
66
|
+
2. Run via Bash with a 15-second timeout (set the Bash tool's `timeout` parameter to `15000`). Include the auth header only when a real secret was resolved:
|
|
120
67
|
- If `<resolved_secret>` is non-empty:
|
|
121
68
|
```bash
|
|
122
|
-
|
|
69
|
+
curl -s -X POST \
|
|
123
70
|
-H "<auth_header>: <resolved_secret>" \
|
|
124
71
|
-H "Content-Type: application/json" \
|
|
125
72
|
-d '{"query":"{ __schema { types { name fields { name type { name } } } } }"}' \
|
|
@@ -127,7 +74,7 @@ If `<type>` is `graphql`:
|
|
|
127
74
|
```
|
|
128
75
|
- If `<resolved_secret>` is empty (public API or no secret configured):
|
|
129
76
|
```bash
|
|
130
|
-
|
|
77
|
+
curl -s -X POST \
|
|
131
78
|
-H "Content-Type: application/json" \
|
|
132
79
|
-d '{"query":"{ __schema { types { name fields { name type { name } } } } }"}' \
|
|
133
80
|
"<base_url>" 2>&1
|
|
@@ -232,9 +179,48 @@ Determine if the tool is well-known (gh, jira, kubectl) or unknown. For well-kno
|
|
|
232
179
|
- list-pods: `kubectl get pods --all-namespaces --field-selector=status.phase!=Succeeded -o wide`
|
|
233
180
|
- list-failing: `kubectl get pods --all-namespaces --field-selector=status.phase=Failed -o wide`
|
|
234
181
|
|
|
235
|
-
For **unknown tools**,
|
|
182
|
+
For **unknown tools**, use a cascading approach to re-learn capabilities. Each stage builds on the previous. The goal is to update CLI invocations for existing capability names — do NOT ask the user to re-select capabilities.
|
|
183
|
+
|
|
184
|
+
**Stage 1 — Local docs:**
|
|
185
|
+
Attempt to find local documentation for the tool:
|
|
186
|
+
```bash
|
|
187
|
+
TOOL_PATH=$(which <command> 2>/dev/null)
|
|
188
|
+
```
|
|
189
|
+
If found, check for README or docs in the tool's package directory:
|
|
190
|
+
```bash
|
|
191
|
+
TOOL_DIR=$(dirname "$(dirname "$TOOL_PATH")")
|
|
192
|
+
ls "$TOOL_DIR"/README* "$TOOL_DIR"/doc* "$TOOL_DIR"/docs* 2>/dev/null | head -5
|
|
193
|
+
```
|
|
194
|
+
If doc files exist, read the entry-point doc (prefer README, then docs/index or equivalent). Then follow internal links, references, or "see also" pointers to sections relevant to the existing capabilities. Use Claude's understanding to navigate — don't stop at a fixed line count.
|
|
195
|
+
|
|
196
|
+
**Stage 2 — CLI help (baseline):**
|
|
197
|
+
Run `<command> --help 2>&1 | head -80` via Bash. If the help output lists subcommands, also run `<command> <subcommand> --help` for subcommands relevant to existing capabilities. Combine with Stage 1 findings. Update invocations for existing capability names based on any changes to flags, subcommands, or output formats.
|
|
198
|
+
|
|
199
|
+
**Stage 3 — Web docs (if invocations could not be updated from stages 1-2):**
|
|
200
|
+
If any existing capability's invocation could not be validated from local docs or help output, attempt to fetch the tool's web documentation. Use WebFetch on common doc URLs:
|
|
201
|
+
- `https://<command>.dev` or `https://<command>.io`
|
|
202
|
+
- The homepage URL from `<command> --help` output if one was printed
|
|
203
|
+
|
|
204
|
+
If a docs page is found, follow links to CLI reference, commands, or API sections rather than reading only the landing page.
|
|
205
|
+
|
|
206
|
+
**Stage 4 — Source code analysis (user opt-in per D-09):**
|
|
207
|
+
After stages 1-3, if the tool path was found, ask the user:
|
|
208
|
+
|
|
209
|
+
Use AskUserQuestion:
|
|
210
|
+
```
|
|
211
|
+
Updated <N> capability invocations from docs and help output. Want me to analyze <command>'s source code for additional capabilities?
|
|
212
|
+
```
|
|
236
213
|
|
|
237
|
-
|
|
214
|
+
If the user says yes:
|
|
215
|
+
- Read the main entry point (e.g., bin/<command>, cli.js, main.py)
|
|
216
|
+
- Follow imports/requires to command registration, subcommand definitions, or handler modules
|
|
217
|
+
- Navigate into the files that define actual commands and actions — don't stop at the entry point
|
|
218
|
+
- Identify new capabilities from function names, subcommands, or API surface
|
|
219
|
+
- Add any new relevant capabilities to the list
|
|
220
|
+
|
|
221
|
+
If no: continue with updated invocations.
|
|
222
|
+
|
|
223
|
+
Do NOT ask the user to re-select capabilities — keep the same capability names, update only the CLI invocations. The cascade's Stage 4 is the only place where NEW capabilities might be added (with user opt-in).
|
|
238
224
|
|
|
239
225
|
Get the new installed version:
|
|
240
226
|
```bash
|
|
@@ -258,24 +244,7 @@ Write the full file back with the Write tool.
|
|
|
258
244
|
<step name="git-commit">
|
|
259
245
|
Run via Bash:
|
|
260
246
|
```bash
|
|
261
|
-
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
Check whether there is anything to commit:
|
|
265
|
-
```bash
|
|
266
|
-
git -C <storage_repo> status --porcelain
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
If the output is empty, skip the commit and continue.
|
|
270
|
-
|
|
271
|
-
Otherwise, run:
|
|
272
|
-
```bash
|
|
273
|
-
git -C <storage_repo> commit -m "donna(relearn-tools): updated <count> tool(s)"
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
If `auto_push` is true in config, also run:
|
|
277
|
-
```bash
|
|
278
|
-
git -C <storage_repo> push
|
|
247
|
+
node ~/.donna/donna-tools.cjs commit "donna(relearn-tools): updated <count> tool(s)" --files donna/tools.md
|
|
279
248
|
```
|
|
280
249
|
</step>
|
|
281
250
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Donna Remove-Tool Workflow
|
|
2
|
+
|
|
3
|
+
<objective>
|
|
4
|
+
Remove a registered tool from tools.md cleanly — confirm with the user, delete the tool's section, and commit.
|
|
5
|
+
</objective>
|
|
6
|
+
|
|
7
|
+
<step name="init">
|
|
8
|
+
Run via Bash:
|
|
9
|
+
```bash
|
|
10
|
+
INIT=$(node ~/.donna/donna-tools.cjs init)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Parse the JSON response. If the `error` field is `"not_configured"`, print:
|
|
14
|
+
```
|
|
15
|
+
x Donna is not configured. Run /donna:setup first.
|
|
16
|
+
```
|
|
17
|
+
Stop.
|
|
18
|
+
|
|
19
|
+
Extract `storage_repo`, `daily_folder`, `auto_push` from the JSON.
|
|
20
|
+
|
|
21
|
+
If `update_available` is non-null, print:
|
|
22
|
+
```
|
|
23
|
+
Donna v<update_available> available -- run npx @pingvinen/donna-assistant to update
|
|
24
|
+
```
|
|
25
|
+
Continue normally.
|
|
26
|
+
</step>
|
|
27
|
+
|
|
28
|
+
<step name="read-tools-md">
|
|
29
|
+
Read `<storage_repo>/donna/tools.md` with the Read tool. If the file does not exist or has no tool sections, print:
|
|
30
|
+
```
|
|
31
|
+
! No tools registered. Nothing to remove.
|
|
32
|
+
```
|
|
33
|
+
Stop.
|
|
34
|
+
|
|
35
|
+
Parse each tool section (starting with `## <tool_name>`). For each tool, extract `type`, `command`, `scope`, and the capabilities list.
|
|
36
|
+
|
|
37
|
+
Store as `<registered_tools>`.
|
|
38
|
+
</step>
|
|
39
|
+
|
|
40
|
+
<step name="select-tool">
|
|
41
|
+
If a tool name was provided as argument (e.g., `/donna:remove-tool gh`), look it up in `<registered_tools>`. If not found, print `! Tool "<name>" not found in tools.md` and stop.
|
|
42
|
+
|
|
43
|
+
If no argument was provided, list all registered tools via AskUserQuestion:
|
|
44
|
+
```
|
|
45
|
+
Which tool would you like to remove?
|
|
46
|
+
|
|
47
|
+
<numbered list of tool names with their type>
|
|
48
|
+
```
|
|
49
|
+
Store the selected tool as `<selected_tool>`.
|
|
50
|
+
</step>
|
|
51
|
+
|
|
52
|
+
<step name="confirm-removal">
|
|
53
|
+
Display the tool's current configuration summary:
|
|
54
|
+
```
|
|
55
|
+
About to remove <tool_name>:
|
|
56
|
+
|
|
57
|
+
type: <type>
|
|
58
|
+
command: <command>
|
|
59
|
+
scope: <scope>
|
|
60
|
+
capabilities: <count> registered
|
|
61
|
+
|
|
62
|
+
This will delete the tool's entire section from tools.md.
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Use AskUserQuestion:
|
|
66
|
+
```
|
|
67
|
+
Remove <tool_name>? (yes/no)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
If no, print `Cancelled.` and stop.
|
|
71
|
+
</step>
|
|
72
|
+
|
|
73
|
+
<step name="remove-from-tools-md">
|
|
74
|
+
Read `<storage_repo>/donna/tools.md`. Remove the entire `## <tool_name>` section (from the `## <tool_name>` heading up to but not including the next `## ` heading, or end of file). Preserve all other tool sections unchanged. Write the full file back with the Write tool.
|
|
75
|
+
|
|
76
|
+
If no tool sections remain after removal, write the file with just the frontmatter header (preserve any existing frontmatter).
|
|
77
|
+
</step>
|
|
78
|
+
|
|
79
|
+
<step name="git-commit">
|
|
80
|
+
Run via Bash:
|
|
81
|
+
```bash
|
|
82
|
+
node ~/.donna/donna-tools.cjs commit "donna(remove-tool): removed <tool_name>" --files donna/tools.md
|
|
83
|
+
```
|
|
84
|
+
</step>
|
|
85
|
+
|
|
86
|
+
<step name="confirm">
|
|
87
|
+
Print:
|
|
88
|
+
```
|
|
89
|
+
! Removed <tool_name> from tools.md
|
|
90
|
+
|
|
91
|
+
To re-add with a different configuration: /donna:add-tool <tool_name>
|
|
92
|
+
```
|
|
93
|
+
</step>
|
package/workflows/run-tools.md
CHANGED
|
@@ -4,82 +4,25 @@
|
|
|
4
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
5
|
</objective>
|
|
6
6
|
|
|
7
|
-
<step name="
|
|
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
|
-
|
|
7
|
+
<step name="init">
|
|
31
8
|
Run via Bash:
|
|
32
9
|
```bash
|
|
33
|
-
|
|
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"
|
|
10
|
+
INIT=$(node ~/.donna/donna-tools.cjs init)
|
|
47
11
|
```
|
|
48
12
|
|
|
49
|
-
|
|
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"
|
|
13
|
+
Parse the JSON response. If the `error` field is `"not_configured"`, print:
|
|
53
14
|
```
|
|
54
|
-
|
|
55
|
-
If `auto_push` is true in config, also push.
|
|
56
|
-
|
|
57
|
-
**`backfill-tool-type`:** Backfill `type` on existing tool sections in tools.md using heuristic detection.
|
|
58
|
-
|
|
59
|
-
Read `<storage_repo>/donna/tools.md` with the Read tool. If the file does not exist or has no tool sections, skip this handler.
|
|
60
|
-
|
|
61
|
-
For each tool section (starting with `## <tool_name>`), check if a `- type:` line already exists. If the `- type:` line is missing, detect the correct type:
|
|
62
|
-
|
|
63
|
-
1. If the tool section contains a `- command:` line where the value starts with `mcp:` (e.g., `- command: mcp:linear`), insert `- type: mcp` immediately after the `- command:` line.
|
|
64
|
-
2. Else, if the tool section contains a `- base_url:` line:
|
|
65
|
-
- If the capabilities section contains entries that look like GraphQL queries (contain `query {` or `mutation {`), insert `- type: graphql` immediately after `## <tool_name>` (REST/GraphQL tools have no `- command:` line).
|
|
66
|
-
- Otherwise, insert `- type: rest` immediately after `## <tool_name>`.
|
|
67
|
-
3. Else (no `mcp:` prefix, no `base_url` field), insert `- type: cli` immediately after the `- command:` line.
|
|
68
|
-
|
|
69
|
-
Write the updated file back with the Write tool. If any changes were made, commit:
|
|
70
|
-
```bash
|
|
71
|
-
git -C <storage_repo> add -A
|
|
72
|
-
git -C <storage_repo> diff --cached --quiet || git -C <storage_repo> commit -m "donna(migrate): backfill tool types on existing tools"
|
|
15
|
+
x Donna is not configured. Run /donna:setup first.
|
|
73
16
|
```
|
|
17
|
+
Stop.
|
|
74
18
|
|
|
75
|
-
|
|
19
|
+
Extract `storage_repo`, `daily_folder`, `auto_push` from the JSON.
|
|
76
20
|
|
|
77
|
-
|
|
78
|
-
```markdown
|
|
79
|
-
---
|
|
80
|
-
pending_migrations: []
|
|
81
|
-
---
|
|
21
|
+
If `update_available` is non-null, print:
|
|
82
22
|
```
|
|
23
|
+
Donna v<update_available> available -- run npx @pingvinen/donna-assistant to update
|
|
24
|
+
```
|
|
25
|
+
Continue normally.
|
|
83
26
|
</step>
|
|
84
27
|
|
|
85
28
|
<step name="read-tools-md">
|
|
@@ -95,13 +38,11 @@ Store the parsed tools and their capabilities as `<registered_tools>`.
|
|
|
95
38
|
</step>
|
|
96
39
|
|
|
97
40
|
<step name="find-daily-file">
|
|
98
|
-
Get
|
|
41
|
+
Get the daily file path via donna-tools:
|
|
99
42
|
```bash
|
|
100
|
-
|
|
43
|
+
DAILY_PATH=$(node ~/.donna/donna-tools.cjs daily-path | node -e "process.stdin.resume();let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>console.log(JSON.parse(d).path))")
|
|
101
44
|
```
|
|
102
|
-
Store as `<today
|
|
103
|
-
|
|
104
|
-
Construct the path: `<storage_repo>/<daily_folder>/<today>.md`.
|
|
45
|
+
Store the result as `<daily_file_path>`. Extract `<today>` from the filename (last path component without `.md`).
|
|
105
46
|
|
|
106
47
|
If the file does not exist, print:
|
|
107
48
|
```
|
|
@@ -143,9 +84,9 @@ If the `## From Tools` section does not exist in today's file, set `<existing_to
|
|
|
143
84
|
|
|
144
85
|
For `type: cli` capabilities (format: `<name>: <cli_invocation>`):
|
|
145
86
|
|
|
146
|
-
Run the capability command via Bash
|
|
87
|
+
Run the capability command via Bash (set the Bash tool's `timeout` parameter to `10000`):
|
|
147
88
|
```bash
|
|
148
|
-
|
|
89
|
+
<capability_command> 2>&1
|
|
149
90
|
```
|
|
150
91
|
|
|
151
92
|
**On success (exit 0):** Parse the output into task entries. Every task line MUST include both a tool tag and a descriptive link.
|
|
@@ -174,11 +115,14 @@ For other tools: use Claude's understanding to extract task-like items from the
|
|
|
174
115
|
Continue to the next capability. Never retry.
|
|
175
116
|
|
|
176
117
|
For `type: rest` capabilities (format: `<name>: <METHOD> /path`):
|
|
177
|
-
1.
|
|
178
|
-
2. Resolve the `auth_secret` key to get the actual secret value. If the key is not found in secrets.md or the value is `REPLACE_WITH_YOUR_SECRET`, add warning `! <tool_name>: missing secret <auth_secret> — edit donna/secrets.md` and skip this tool.
|
|
179
|
-
3. For each capability, run via Bash with a 10-second timeout:
|
|
118
|
+
1. Resolve the auth secret via Bash:
|
|
180
119
|
```bash
|
|
181
|
-
|
|
120
|
+
node ~/.donna/donna-tools.cjs resolve-secret <auth_secret>
|
|
121
|
+
```
|
|
122
|
+
2. Parse the JSON response. If `error` is `"key_not_found"` or `"placeholder_value"`, add warning `! <tool_name>: missing secret <auth_secret> — edit donna/secrets.md` and skip this tool. Otherwise extract the `value` field as `<resolved_secret>`.
|
|
123
|
+
3. For each capability, run via Bash (set the Bash tool's `timeout` parameter to `10000`):
|
|
124
|
+
```bash
|
|
125
|
+
curl -s -H "<auth_header>: <resolved_secret>" "<base_url><path>" 2>&1
|
|
182
126
|
```
|
|
183
127
|
4. Parse the JSON response using Claude's understanding to extract task-like items. Format:
|
|
184
128
|
`- [ ] (<tool_name>) <description> [<identifier>](<url>)`
|
|
@@ -189,9 +133,9 @@ Continue. Never retry.
|
|
|
189
133
|
|
|
190
134
|
For `type: graphql` capabilities (format: `<name>: <graphql_query>`):
|
|
191
135
|
1. Same secrets resolution as REST.
|
|
192
|
-
2. For each capability, run via Bash
|
|
136
|
+
2. For each capability, run via Bash (set the Bash tool's `timeout` parameter to `10000`):
|
|
193
137
|
```bash
|
|
194
|
-
|
|
138
|
+
curl -s -X POST -H "<auth_header>: <resolved_secret>" -H "Content-Type: application/json" -d '{"query":"<graphql_query>"}' "<base_url>" 2>&1
|
|
195
139
|
```
|
|
196
140
|
3. Parse the JSON response to extract task-like items. Same format as REST.
|
|
197
141
|
|
|
@@ -223,7 +167,7 @@ For each URL in `<existing_tool_lines>`:
|
|
|
223
167
|
1. If the line is `[x]` (user manually checked): **KEEP AS-IS** — user intent wins (Rule 1). Do not change regardless of tool state.
|
|
224
168
|
2. If the line is `[ ]` AND the URL exists in `<fresh_tool_tasks>`: **KEEP OPEN** — item is still active (Rule 2).
|
|
225
169
|
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).
|
|
226
|
-
- For gh items: check if the PR/issue was closed or merged via Bash
|
|
170
|
+
- For gh items: check if the PR/issue was closed or merged via Bash (set the Bash tool's `timeout` parameter to `10000`): `gh pr view <number> --json state --jq '.state' 2>/dev/null || gh issue view <number> --json state --jq '.state' 2>/dev/null`
|
|
227
171
|
- If closed or merged: change to `- [x] (<tool>) <description> [<identifier>](<url>) (<reason>)` and move to `## Resolved` (Rule 3a).
|
|
228
172
|
- If status check fails or item simply no longer appears: move to `## Resolved` with `(removed)` (Rule 3b).
|
|
229
173
|
|
|
@@ -244,24 +188,7 @@ If `## From Tools` does not yet exist in the file, add it after the `## Tasks` s
|
|
|
244
188
|
<step name="git-commit">
|
|
245
189
|
Run via Bash:
|
|
246
190
|
```bash
|
|
247
|
-
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
Check whether there is anything to commit:
|
|
251
|
-
```bash
|
|
252
|
-
git -C <storage_repo> status --porcelain
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
If the output is empty, skip the commit and continue.
|
|
256
|
-
|
|
257
|
-
Otherwise, run:
|
|
258
|
-
```bash
|
|
259
|
-
git -C <storage_repo> commit -m "donna(run-tools): refreshed tool data for <today>"
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
If `auto_push` is true in config, also run:
|
|
263
|
-
```bash
|
|
264
|
-
git -C <storage_repo> push
|
|
191
|
+
node ~/.donna/donna-tools.cjs commit "donna(tools): refreshed tool data" --files <daily_folder>/<today>.md
|
|
265
192
|
```
|
|
266
193
|
</step>
|
|
267
194
|
|