@pingvinen/donna-assistant 0.0.0 → 0.4.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 +1,2 @@
1
- Placeholder release for trusted publishing setup. Real content will be published via CI.
1
+ # donna
2
+ A Claude skill based personal assistant
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { run } = require("../src/installer.cjs");
5
+
6
+ const force = process.argv.includes("--force");
7
+
8
+ run({ force }).catch((err) => {
9
+ console.error(`\nInstallation failed: ${err.message}`);
10
+ process.exit(1);
11
+ });
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ version: "0.1.0",
5
+ description: "Initial directory structure",
6
+ up(ctx) {
7
+ const dirs = ["workflows", "templates", "references"];
8
+ for (const dir of dirs) {
9
+ ctx.fs.mkdirSync(ctx.path.join(ctx.donnaDir, dir), { recursive: true });
10
+ }
11
+ },
12
+ };
package/package.json CHANGED
@@ -1,8 +1,44 @@
1
1
  {
2
- "name": "@pingvinen/donna-assistant",
3
- "version": "0.0.0",
4
- "description": "Donna - your AI powered personal assistant (placeholder for trusted publishing setup)",
5
- "publishConfig": {
6
- "access": "public"
7
- }
2
+ "name": "@pingvinen/donna-assistant",
3
+ "version": "0.4.0",
4
+ "description": "Donna - your AI powered personal assistant",
5
+ "bin": {
6
+ "donna-assistant": "./bin/donna-assistant"
7
+ },
8
+ "files": [
9
+ "bin/",
10
+ "src/",
11
+ "stubs/",
12
+ "workflows/",
13
+ "migrations/",
14
+ "templates/",
15
+ "references/"
16
+ ],
17
+ "engines": {
18
+ "node": ">=18"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public",
22
+ "provenance": true
23
+ },
24
+ "scripts": {
25
+ "lint": "biome check .",
26
+ "lint:fix": "biome check --write .",
27
+ "test": "node --test 'test/*.test.cjs'",
28
+ "prepublishOnly": "chmod +x bin/donna-assistant"
29
+ },
30
+ "devDependencies": {
31
+ "@biomejs/biome": "^1.9.0"
32
+ },
33
+ "keywords": [
34
+ "claude-code",
35
+ "assistant",
36
+ "productivity",
37
+ "ai"
38
+ ],
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/pingvinen/donna"
43
+ }
8
44
  }
File without changes
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
+ const os = require("node:os");
6
+
7
+ const output = require("./output.cjs");
8
+ const version = require("./version.cjs");
9
+ const migrator = require("./migrator.cjs");
10
+ const providers = require("./providers/index.cjs");
11
+
12
+ /**
13
+ * Main installer orchestration.
14
+ *
15
+ * @param {object} [options]
16
+ * @param {string} [options.homeDir] - Override home directory (for testing)
17
+ * @param {boolean} [options.force] - Force re-install even if version matches
18
+ * @returns {Promise<void>}
19
+ */
20
+ async function run(options = {}) {
21
+ const homeDir = options.homeDir || os.homedir();
22
+ const force = options.force || false;
23
+ const donnaDir = path.join(homeDir, ".donna");
24
+ const migrationsDir = path.join(__dirname, "..", "migrations");
25
+ const workflowsSource = path.join(__dirname, "..", "workflows");
26
+ const pkg = require("../package.json");
27
+ const packageVersion = pkg.version;
28
+
29
+ // Print banner
30
+ output.banner();
31
+
32
+ // Create donnaDir if it doesn't exist
33
+ fs.mkdirSync(donnaDir, { recursive: true });
34
+
35
+ // Read current version
36
+ const current = version.readVersion(donnaDir);
37
+ const currentVersion = current?.version || null;
38
+ const lastMigration = current?.lastMigration || 0;
39
+
40
+ // Check if already up to date
41
+ if (currentVersion === packageVersion && !force) {
42
+ // Check for pending migrations too
43
+ const pendingResults = migrator.runMigrations(migrationsDir, donnaDir, lastMigration);
44
+ if (pendingResults.length === 0) {
45
+ output.info(`Already up to date at ${packageVersion}`);
46
+ return;
47
+ }
48
+ }
49
+
50
+ // If upgrading (current version exists but differs)
51
+ if (currentVersion && currentVersion !== packageVersion) {
52
+ output.upgradeHeader(currentVersion, packageVersion);
53
+ }
54
+
55
+ // Run migrations
56
+ const results = migrator.runMigrations(migrationsDir, donnaDir, lastMigration);
57
+
58
+ // Track last successful migration number
59
+ let lastSuccessful = lastMigration;
60
+
61
+ for (const result of results) {
62
+ if (result.ok) {
63
+ output.migrationLine(result.description);
64
+ lastSuccessful = result.num;
65
+ } else {
66
+ output.fail(`Migration ${result.num} failed: ${result.error.message}`);
67
+ // Write version.md with last successful migration
68
+ version.writeVersion(donnaDir, packageVersion, lastSuccessful);
69
+ throw result.error;
70
+ }
71
+ }
72
+
73
+ // Detect providers and copy stubs
74
+ const detected = providers.detectProviders(homeDir);
75
+
76
+ if (detected.length > 0) {
77
+ for (const provider of detected) {
78
+ fs.cpSync(provider.stubSource, provider.stubTarget, { recursive: true });
79
+ output.success(
80
+ `Copied donna skills (setup, add-task, done, set-role, begin-the-day) to ${provider.stubTarget}`,
81
+ );
82
+ }
83
+ } else {
84
+ output.info("No supported AI providers detected");
85
+ output.info("Install Claude Code and re-run to add donna skills");
86
+ }
87
+
88
+ // Copy workflows to donnaDir/workflows/
89
+ const workflowsTarget = path.join(donnaDir, "workflows");
90
+ fs.mkdirSync(workflowsTarget, { recursive: true });
91
+ fs.cpSync(workflowsSource, workflowsTarget, { recursive: true });
92
+ output.success("Installed workflows to ~/.donna/workflows/");
93
+
94
+ // Write version.md
95
+ version.writeVersion(donnaDir, packageVersion, lastSuccessful);
96
+ output.success(`Version ${packageVersion} installed`);
97
+
98
+ // Final message
99
+ console.log("");
100
+ output.info("Run /donna:setup in Claude Code to get started.");
101
+ }
102
+
103
+ module.exports = { run };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
+ const os = require("node:os");
6
+
7
+ /**
8
+ * Run pending migrations from migrationsDir against donnaDir.
9
+ * Skips migrations with numeric prefix <= lastMigration.
10
+ * Stops on first failure.
11
+ *
12
+ * @param {string} migrationsDir - Directory containing numbered .cjs migration files
13
+ * @param {string} donnaDir - The ~/.donna/ directory
14
+ * @param {number} lastMigration - Last successfully applied migration number
15
+ * @returns {Array<{num: number, description: string, ok: boolean, error?: Error}>}
16
+ */
17
+ function runMigrations(migrationsDir, donnaDir, lastMigration) {
18
+ if (!fs.existsSync(migrationsDir)) return [];
19
+
20
+ const files = fs
21
+ .readdirSync(migrationsDir)
22
+ .filter((f) => f.endsWith(".cjs"))
23
+ .sort((a, b) => Number.parseInt(a, 10) - Number.parseInt(b, 10));
24
+
25
+ const pending = files.filter((f) => Number.parseInt(f, 10) > lastMigration);
26
+ const results = [];
27
+
28
+ for (const file of pending) {
29
+ const migration = require(path.join(migrationsDir, file));
30
+ const num = Number.parseInt(file, 10);
31
+
32
+ try {
33
+ migration.up({ donnaDir, fs, path, os });
34
+ results.push({ num, description: migration.description, ok: true });
35
+ } catch (err) {
36
+ results.push({ num, description: migration.description, ok: false, error: err });
37
+ break;
38
+ }
39
+ }
40
+
41
+ return results;
42
+ }
43
+
44
+ module.exports = { runMigrations };
package/src/output.cjs ADDED
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ function banner() {
4
+ console.log("");
5
+ console.log("━━━ Donna ━━━");
6
+ console.log("");
7
+ }
8
+
9
+ function success(msg) {
10
+ console.log(` \u2713 ${msg}`);
11
+ }
12
+
13
+ function fail(msg) {
14
+ console.log(` \u2717 ${msg}`);
15
+ }
16
+
17
+ function info(msg) {
18
+ console.log(` ${msg}`);
19
+ }
20
+
21
+ function upgradeHeader(from, to) {
22
+ console.log(` Upgrading ${from} \u2192 ${to}:`);
23
+ }
24
+
25
+ function migrationLine(desc) {
26
+ console.log(` \u2713 ${desc}`);
27
+ }
28
+
29
+ module.exports = { banner, success, fail, info, upgradeHeader, migrationLine };
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
+
6
+ module.exports = {
7
+ name: "Claude Code",
8
+
9
+ detect(homeDir) {
10
+ return fs.existsSync(path.join(homeDir, ".claude"));
11
+ },
12
+
13
+ stubSource: path.join(__dirname, "..", "..", "stubs", "claude-code"),
14
+
15
+ getStubTarget(homeDir) {
16
+ return path.join(homeDir, ".claude", "commands");
17
+ },
18
+ };
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ const claudeCode = require("./claude-code.cjs");
4
+
5
+ const PROVIDERS = [claudeCode];
6
+
7
+ /**
8
+ * Detect which AI coding assistant providers are installed.
9
+ *
10
+ * @param {string} homeDir - The user's home directory
11
+ * @returns {Array<{name: string, stubSource: string, stubTarget: string}>}
12
+ */
13
+ function detectProviders(homeDir) {
14
+ return PROVIDERS.filter((p) => p.detect(homeDir)).map((p) => ({
15
+ name: p.name,
16
+ stubSource: p.stubSource,
17
+ stubTarget: p.getStubTarget(homeDir),
18
+ }));
19
+ }
20
+
21
+ module.exports = { detectProviders };
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
+
6
+ function readVersion(donnaDir) {
7
+ const versionPath = path.join(donnaDir, "version.md");
8
+ if (!fs.existsSync(versionPath)) return null;
9
+
10
+ const content = fs.readFileSync(versionPath, "utf8");
11
+ const version = content.match(/\*\*Version:\*\* (.+)/)?.[1] || "0.0.0";
12
+ const lastMigration = Number.parseInt(
13
+ content.match(/\*\*Last migration:\*\* (\d+)/)?.[1] || "0",
14
+ 10,
15
+ );
16
+ const installed = content.match(/\*\*Installed:\*\* (.+)/)?.[1] || null;
17
+ const updated = content.match(/\*\*Updated:\*\* (.+)/)?.[1] || null;
18
+
19
+ return { version, lastMigration, installed, updated };
20
+ }
21
+
22
+ function writeVersion(donnaDir, version, lastMigration) {
23
+ const versionPath = path.join(donnaDir, "version.md");
24
+ const now = new Date().toISOString();
25
+
26
+ let installed = now;
27
+
28
+ // Preserve the original "Installed" timestamp if file already exists
29
+ if (fs.existsSync(versionPath)) {
30
+ const existing = readVersion(donnaDir);
31
+ if (existing?.installed) {
32
+ installed = existing.installed;
33
+ }
34
+ }
35
+
36
+ const content = [
37
+ "# Donna",
38
+ "",
39
+ `- **Version:** ${version}`,
40
+ `- **Last migration:** ${String(lastMigration).padStart(3, "0")}`,
41
+ `- **Installed:** ${installed}`,
42
+ `- **Updated:** ${now}`,
43
+ "",
44
+ ].join("\n");
45
+
46
+ fs.writeFileSync(versionPath, content);
47
+ }
48
+
49
+ module.exports = { readVersion, writeVersion };
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: donna:add-task
3
+ description: Capture a task to today's daily journal
4
+ allowed-tools:
5
+ - Read
6
+ - Write
7
+ - Bash
8
+ - AskUserQuestion
9
+ ---
10
+
11
+ <objective>
12
+ Run the Donna add-task workflow.
13
+ </objective>
14
+
15
+ <execution_context>
16
+ @~/.donna/workflows/add-task.md
17
+ </execution_context>
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: donna:begin-the-day
3
+ description: Start your day — carry forward open tasks, surface recurring tasks due today, get your daily brief
4
+ allowed-tools:
5
+ - Read
6
+ - Write
7
+ - Bash
8
+ ---
9
+
10
+ <objective>
11
+ Run the Donna begin-the-day workflow.
12
+ </objective>
13
+
14
+ <execution_context>
15
+ @~/.donna/workflows/begin-the-day.md
16
+ </execution_context>
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: donna:done
3
+ description: Mark tasks as complete in today's daily journal
4
+ allowed-tools:
5
+ - Read
6
+ - Write
7
+ - Bash
8
+ - AskUserQuestion
9
+ ---
10
+
11
+ <objective>
12
+ Run the Donna done workflow.
13
+ </objective>
14
+
15
+ <execution_context>
16
+ @~/.donna/workflows/done.md
17
+ </execution_context>
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: donna:set-role
3
+ description: Define your job role, research recurring tasks, and build your daily rhythm
4
+ allowed-tools:
5
+ - Read
6
+ - Write
7
+ - Bash
8
+ - AskUserQuestion
9
+ - WebSearch
10
+ ---
11
+
12
+ <objective>
13
+ Run the Donna set-role workflow. This command guides the user through defining their job role: collecting role details, researching recurring tasks and tools, presenting findings for approval, and persisting everything to the storage repo.
14
+ </objective>
15
+
16
+ <execution_context>
17
+ @~/.donna/workflows/set-role.md
18
+ </execution_context>
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: donna:setup
3
+ description: Set up Donna — configure storage repo, initialize file structure, create bootstrap config
4
+ allowed-tools:
5
+ - Read
6
+ - Write
7
+ - Bash
8
+ - AskUserQuestion
9
+ ---
10
+
11
+ <objective>
12
+ Run the Donna setup workflow. This command guides the user through configuring Donna: setting the storage repo path, initializing the file structure, and writing the bootstrap config at ~/.config/donna/config.md.
13
+ </objective>
14
+
15
+ <execution_context>
16
+ @~/.donna/workflows/setup.md
17
+ </execution_context>
File without changes
@@ -0,0 +1,97 @@
1
+ # Donna Add-Task Workflow
2
+
3
+ <objective>
4
+ Capture a new task to today's daily journal file in the storage repo and commit it 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="get-description">
25
+ The task description is provided as the argument to this command (e.g., `/donna:add-task buy milk`).
26
+
27
+ If no argument was provided, use AskUserQuestion to ask:
28
+ ```
29
+ What task would you like to add?
30
+ ```
31
+
32
+ Store the response as `<description>`.
33
+ </step>
34
+
35
+ <step name="ensure-daily-file">
36
+ Run via Bash to get today's date:
37
+ ```bash
38
+ date +%Y-%m-%d
39
+ ```
40
+
41
+ Store the result as `<date>`. Construct the daily file path: `<storage_repo>/<daily_folder>/<date>.md`.
42
+
43
+ Run via Bash to ensure the daily folder exists:
44
+ ```bash
45
+ mkdir -p <storage_repo>/<daily_folder>
46
+ ```
47
+
48
+ If the daily file does not exist, create it with the Write tool using this content (substituting the actual date):
49
+ ```markdown
50
+ ---
51
+ date: <date>
52
+ ---
53
+
54
+ ## Tasks
55
+ ```
56
+ </step>
57
+
58
+ <step name="append-task">
59
+ Read the daily file with the Read tool.
60
+
61
+ Append `- [ ] <description>` on a new line at the end of the file.
62
+
63
+ Write the updated file with the Write tool.
64
+ </step>
65
+
66
+ <step name="git-commit">
67
+ Run via Bash:
68
+ ```bash
69
+ git -C <storage_repo> add -A
70
+ ```
71
+
72
+ Check whether there is anything to commit:
73
+ ```bash
74
+ git -C <storage_repo> status --porcelain
75
+ ```
76
+
77
+ If the output is empty, skip the commit and continue.
78
+
79
+ Otherwise, run:
80
+ ```bash
81
+ git -C <storage_repo> commit -m "donna(add-task): <description>"
82
+ ```
83
+
84
+ If `auto_push` is true in config, also run:
85
+ ```bash
86
+ git -C <storage_repo> push
87
+ ```
88
+ </step>
89
+
90
+ <step name="confirm">
91
+ Print:
92
+ ```
93
+ ✓ Added: <description>
94
+ ```
95
+
96
+ Also print the path to the daily file: `<storage_repo>/<daily_folder>/<date>.md`
97
+ </step>
@@ -0,0 +1,198 @@
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="get-today">
25
+ Run via Bash to get today's date, day-of-week, and day-of-month:
26
+ ```bash
27
+ date +%Y-%m-%d
28
+ ```
29
+ Store the result as `<today>`.
30
+
31
+ ```bash
32
+ date +%A
33
+ ```
34
+ Store the result as `<day_of_week>` (e.g., "Monday").
35
+
36
+ ```bash
37
+ date +%-d
38
+ ```
39
+ Store the result as `<day_of_month>` (e.g., "1" for the 1st).
40
+
41
+ Construct the daily file path: `<storage_repo>/<daily_folder>/<today>.md`.
42
+ </step>
43
+
44
+ <step name="find-previous-file">
45
+ Find the most recent previous daily file. Run via Bash:
46
+ ```bash
47
+ TODAY=$(date +%Y-%m-%d)
48
+ DAILY_DIR="<storage_repo>/<daily_folder>"
49
+ PREV_FILE=$(ls "$DAILY_DIR"/*.md 2>/dev/null | sort | grep -v "$TODAY" | tail -1)
50
+ echo "${PREV_FILE:-NONE}"
51
+ ```
52
+
53
+ 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.
54
+ </step>
55
+
56
+ <step name="carry-forward">
57
+ If the previous file result is "NONE", set `<carried_tasks>` to an empty list and continue to the next step.
58
+
59
+ 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:
60
+ - 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)`.
61
+ - If the description has no such suffix: append ` (1 times)`. For example: `- [ ] Follow up with Sarah` becomes `- [ ] Follow up with Sarah (1 times)`.
62
+
63
+ Store the resulting list as `<carried_tasks>`.
64
+
65
+ Do NOT modify the previous file — it is a historical record and must remain unchanged.
66
+ </step>
67
+
68
+ <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).
70
+
71
+ Parse each line matching the pattern `- <description>: <interval>`. For "every other" intervals, also parse the `| last_run: YYYY-MM-DD` suffix.
72
+
73
+ For each task, determine if it is due today using this logic:
74
+ - `every <DayName>` (e.g., "every Monday"): due if `<day_of_week>` matches DayName
75
+ - `every weekday`: due if `<day_of_week>` is Monday, Tuesday, Wednesday, Thursday, or Friday
76
+ - `first <DayName> of month`: due if `<day_of_week>` matches DayName AND `<day_of_month>` <= 7
77
+ - `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:
78
+ ```bash
79
+ LAST_RUN="<last_run_date>"
80
+ last_run_epoch=$(date -j -f "%Y-%m-%d" "$LAST_RUN" "+%s")
81
+ today_epoch=$(date +%s)
82
+ days=$(( (today_epoch - last_run_epoch) / 86400 ))
83
+ echo "$days"
84
+ ```
85
+
86
+ Store the descriptions of all due tasks as `<recurring_tasks>` (just the description text, without the interval suffix).
87
+ </step>
88
+
89
+ <step name="read-existing-today">
90
+ 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
+
92
+ If the file does not exist, `<existing_tasks>` is an empty list.
93
+ </step>
94
+
95
+ <step name="deduplicate">
96
+ Assemble the full task list using a single-pass deduplication to ensure idempotency:
97
+
98
+ **Normalization for comparison:** strip `- [ ] ` or `- [x] ` prefix, strip any trailing ` (N times)` suffix (where N is any integer), lowercase all text, trim whitespace.
99
+
100
+ 1. Start with `<existing_tasks>` — both open and closed tasks take priority. Add them all to the final list.
101
+
102
+ 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.
103
+
104
+ 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
+
106
+ 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
+
108
+ 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
+
110
+ Store the result as `<final_tasks>`.
111
+ </step>
112
+
113
+ <step name="write-daily-file">
114
+ Ensure the daily folder exists:
115
+ ```bash
116
+ mkdir -p <storage_repo>/<daily_folder>
117
+ ```
118
+
119
+ Write today's daily file with the Write tool. Content format:
120
+ ```markdown
121
+ ---
122
+ date: <today>
123
+ ---
124
+
125
+ ## Tasks
126
+ <existing tasks, preserving their original order and open/closed state>
127
+ <carried-forward tasks not already in existing>
128
+ <recurring tasks not already in existing>
129
+ ```
130
+
131
+ Tasks are written in this order: existing tasks first (preserving their original order and state), then carried-forward tasks, then recurring tasks.
132
+ </step>
133
+
134
+ <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`.
136
+
137
+ 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
+
139
+ If no "every other" recurring tasks were added, skip this step.
140
+ </step>
141
+
142
+ <step name="git-commit">
143
+ Run via Bash:
144
+ ```bash
145
+ git -C <storage_repo> add -A
146
+ ```
147
+
148
+ Check whether there is anything to commit:
149
+ ```bash
150
+ git -C <storage_repo> status --porcelain
151
+ ```
152
+
153
+ If the output is empty, skip the commit and continue.
154
+
155
+ Otherwise, run:
156
+ ```bash
157
+ git -C <storage_repo> commit -m "donna(begin-the-day): daily brief for <today>"
158
+ ```
159
+
160
+ If `auto_push` is true in config, also run:
161
+ ```bash
162
+ git -C <storage_repo> push
163
+ ```
164
+ </step>
165
+
166
+ <step name="print-brief">
167
+ Print the daily brief to the terminal:
168
+ ```
169
+ ══════════════════════════════════════
170
+ DONNA — Daily Brief for <today>
171
+ ══════════════════════════════════════
172
+ ```
173
+
174
+ If there are carried-forward tasks (tasks from `<carried_tasks>` that were added to the final list), print:
175
+ ```
176
+ ## Carried Forward
177
+ - [ ] Follow up with Sarah (3 times)
178
+ - [ ] Review design doc (1 times)
179
+ ```
180
+
181
+ If there are recurring tasks due today (tasks from `<recurring_tasks>` that were added to the final list), print:
182
+ ```
183
+ ## Due Today
184
+ - [ ] Check team Slack
185
+ - [ ] Review sprint backlog
186
+ ```
187
+
188
+ Always end with:
189
+ ```
190
+ ══════════════════════════════════════
191
+ ```
192
+
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:
194
+ ```
195
+ No tasks for today — enjoy your day!
196
+ ```
197
+ in place of both sections.
198
+ </step>
@@ -0,0 +1,126 @@
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="find-daily-file">
25
+ Run via Bash to get today's date:
26
+ ```bash
27
+ date +%Y-%m-%d
28
+ ```
29
+
30
+ Store the result as `<date>`. Construct the daily file path: `<storage_repo>/<daily_folder>/<date>.md`.
31
+
32
+ If the file does not exist, print:
33
+ ```
34
+ ✗ No daily file for today. Add a task first with /donna:add-task
35
+ ```
36
+ Stop.
37
+ </step>
38
+
39
+ <step name="read-tasks">
40
+ Read the daily file with the Read tool.
41
+
42
+ 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
+
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.
45
+
46
+ If no open tasks are found, print:
47
+ ```
48
+ ✓ All tasks already complete for today!
49
+ ```
50
+ Stop.
51
+ </step>
52
+
53
+ <step name="select-tasks">
54
+ Two modes based on whether an argument was provided to this command:
55
+
56
+ **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.
58
+
59
+ 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
+ ```
61
+ Mark as done: '<task>'? (yes/no)
62
+ ```
63
+ If no match is found, tell the user and list all open tasks.
64
+
65
+ **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.
67
+
68
+ 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
+ ```
70
+ Which task(s) did you finish? (or type something you did that's not on the list)
71
+ ```
72
+
73
+ If the user submits without selecting anything or typing anything, print:
74
+ ```
75
+ Nothing selected — nothing to do.
76
+ ```
77
+ Stop.
78
+
79
+ If the user selects from the list, store the selected task(s) as `<completed_tasks>`.
80
+
81
+ 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.
82
+ </step>
83
+
84
+ <step name="mark-complete">
85
+ For each task in `<completed_tasks>`, replace `- [ ] <description>` with `- [x] <description>` in the daily file.
86
+
87
+ 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
+
89
+ Write the updated file with the Write tool.
90
+ </step>
91
+
92
+ <step name="git-commit">
93
+ Run via Bash:
94
+ ```bash
95
+ git -C <storage_repo> add -A
96
+ ```
97
+
98
+ Check whether there is anything to commit:
99
+ ```bash
100
+ git -C <storage_repo> status --porcelain
101
+ ```
102
+
103
+ If the output is empty, skip the commit and continue.
104
+
105
+ If one task was completed, run:
106
+ ```bash
107
+ git -C <storage_repo> commit -m "donna(done): <description>"
108
+ ```
109
+
110
+ If multiple tasks were completed, run:
111
+ ```bash
112
+ git -C <storage_repo> commit -m "donna(done): <N> tasks completed"
113
+ ```
114
+
115
+ If `auto_push` is true in config, also run:
116
+ ```bash
117
+ git -C <storage_repo> push
118
+ ```
119
+ </step>
120
+
121
+ <step name="confirm">
122
+ For each completed task, print:
123
+ ```
124
+ ✓ Done: <description>
125
+ ```
126
+ </step>
@@ -0,0 +1,247 @@
1
+ # Donna Set-Role Workflow
2
+
3
+ <objective>
4
+ Define the user's job role, research recurring tasks and tools for that role using WebSearch, present findings for approval, and persist role definition and recurring tasks to 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` and `auto_push` (default: false) fields from the YAML frontmatter.
17
+ </step>
18
+
19
+ <step name="check-existing-role">
20
+ Run via Bash:
21
+ ```bash
22
+ test -f <storage_repo>/role.md && echo "exists" || echo "missing"
23
+ ```
24
+
25
+ If the output is "exists", proceed to the rerun-menu step.
26
+ If the output is "missing", proceed to the ask-role step.
27
+ </step>
28
+
29
+ <step name="rerun-menu">
30
+ Use AskUserQuestion to present the re-run menu:
31
+
32
+ ```
33
+ A role definition already exists. What would you like to do?
34
+
35
+ 1. Something got messed up — start fresh (reset)
36
+ 2. Got promoted or changed roles — update (diff-update)
37
+ 3. Just want to refresh the research — re-research current role
38
+ 4. Cancel
39
+ ```
40
+
41
+ - 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.
44
+ - On "Cancel" (option 4): print "Cancelled." and stop.
45
+ </step>
46
+
47
+ <step name="ask-role">
48
+ Stage 1: Collect role details interactively.
49
+
50
+ Use AskUserQuestion to ask the following questions in sequence:
51
+
52
+ 1. "What is your job title?" — store as `<job_title>`.
53
+ 2. "How large is your team? How many direct reports do you have? (e.g. team of 8, 3 direct reports)" — store as `<team_info>`.
54
+ 3. "What are 2–3 things you focus on most in your role? (e.g. sprint planning, hiring, technical architecture)" — store as `<key_responsibilities>`.
55
+
56
+ Parse `<team_info>` into `<team_size>` (total team) and `<direct_reports>` (direct reports count) as best you can.
57
+ Parse `<key_responsibilities>` into an array of 2–3 items.
58
+
59
+ Store all values for use in subsequent steps.
60
+ </step>
61
+
62
+ <step name="research">
63
+ Stage 2: Research the role using WebSearch.
64
+
65
+ Construct targeted queries using ALL collected data from Stage 1. Run 2–3 focused searches:
66
+
67
+ 1. `<job_title> daily recurring tasks responsibilities`
68
+ 2. `<job_title> <key_responsibilities[0]> common tools workflows`
69
+ 3. (optional) `<job_title> weekly monthly recurring tasks best practices`
70
+
71
+ Use WebSearch for each query. Synthesize findings into a structured summary matching the role-research.md format:
72
+
73
+ - **Daily tasks**: recurring things done every workday
74
+ - **Weekly tasks**: recurring things done each week
75
+ - **Monthly tasks**: recurring things done each month
76
+ - **Tool suggestions**: tools commonly used for this role (with brief descriptions)
77
+
78
+ Tailor suggestions to the specific responsibilities the user described — avoid generic results that don't reflect their actual focus areas.
79
+ </step>
80
+
81
+ <step name="present-summary">
82
+ Print a concise summary of research findings (2–3 sentences covering the key patterns found).
83
+
84
+ Then use AskUserQuestion:
85
+ ```
86
+ Which category would you like to review?
87
+ 1. Recurring tasks
88
+ 2. Tool suggestions
89
+ 3. Both
90
+ 4. Skip (accept all recurring tasks as-is)
91
+ ```
92
+
93
+ Store the choice for subsequent steps.
94
+ </step>
95
+
96
+ <step name="approve-recurring">
97
+ If the user chose to review recurring tasks (options 1 or 3):
98
+
99
+ Display each category in turn (daily, weekly, monthly) and use AskUserQuestion for each task:
100
+ ```
101
+ [Daily] Check team Slack and unblock blockers
102
+ → Approve, reject, or modify? (approve / reject / [type modification)
103
+ ```
104
+
105
+ Interpret modifications naturally:
106
+ - "make this biweekly" → change interval to "every other week"
107
+ - "every other Monday" → interval becomes "every other Monday"
108
+ - "first Monday of the month" → interval becomes "first Monday of month"
109
+
110
+ After reviewing all categories, ask: "Would you like to add any recurring tasks I missed?" Accept free-text additions.
111
+
112
+ Collect the final approved list with their intervals.
113
+
114
+ If the user chose to skip (option 4): accept all suggested recurring tasks with their default intervals.
115
+ </step>
116
+
117
+ <step name="approve-tools">
118
+ If the user chose to review tool suggestions (options 2 or 3):
119
+
120
+ Display each tool suggestion and use AskUserQuestion:
121
+ ```
122
+ Tool: Jira (sprint management)
123
+ → Note this for future configuration? (yes / no)
124
+ ```
125
+
126
+ For noted tools, print:
127
+ ```
128
+ ✓ Noted: <tool name>. Run /donna:add-tool to configure <tool name> (available in a future update).
129
+ ```
130
+
131
+ Do NOT create tools.md or configure anything — only note the user's interest.
132
+ </step>
133
+
134
+ <step name="save-role">
135
+ Write `<storage_repo>/role.md` with the Write tool.
136
+
137
+ Use this format (substituting actual values):
138
+ ```markdown
139
+ ---
140
+ job_title: <job_title>
141
+ team_size: <team_size>
142
+ direct_reports: <direct_reports>
143
+ key_responsibilities:
144
+ - <responsibility 1>
145
+ - <responsibility 2>
146
+ updated: <today's date in YYYY-MM-DD format>
147
+ ---
148
+
149
+ # Role: <job_title>
150
+
151
+ [Write a 2–3 sentence prose summary of the role as described by the user, incorporating their key responsibilities and team context.]
152
+ ```
153
+
154
+ Write `<storage_repo>/role-research.md` with the Write tool.
155
+
156
+ Use this format:
157
+ ```markdown
158
+ ---
159
+ researched: <today's date in YYYY-MM-DD format>
160
+ role: <job_title>
161
+ ---
162
+
163
+ # Role Research: <job_title>
164
+
165
+ ## Summary
166
+ [2–3 sentence overview of what the research found for this role]
167
+
168
+ ## Recurring Task Suggestions
169
+
170
+ ### Daily
171
+ [List daily task suggestions from research]
172
+
173
+ ### Weekly
174
+ [List weekly task suggestions from research]
175
+
176
+ ### Monthly
177
+ [List monthly task suggestions from research]
178
+
179
+ ## Tool Suggestions
180
+ [List tool suggestions with brief descriptions]
181
+
182
+ ## Notes
183
+ [Any additional context from research relevant to this specific role]
184
+ ```
185
+ </step>
186
+
187
+ <step name="save-recurring">
188
+ Write `<storage_repo>/recurring.md` with the Write tool.
189
+
190
+ Format: one approved recurring task per line as `- Task description: interval`.
191
+ For "every other" intervals (biweekly, every other Monday, etc.), append ` | last_run: <today's date>` suffix.
192
+
193
+ Use this format:
194
+ ```markdown
195
+ ---
196
+ # Recurring tasks — managed by donna:set-role
197
+ ---
198
+
199
+ - <task 1>: <interval 1>
200
+ - <task 2>: <interval 2>
201
+ - <task 3>: <interval 3> | last_run: <today's date>
202
+ ```
203
+
204
+ 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.
206
+ 2. Identify manually-added tasks: tasks in recurring.md that were NOT in the research suggestions.
207
+ 3. Merge: keep manually-added tasks, add new approved tasks, remove tasks the user rejected.
208
+ 4. Write the merged result.
209
+ </step>
210
+
211
+ <step name="git-commit">
212
+ Run via Bash:
213
+ ```bash
214
+ git -C <storage_repo> add -A
215
+ ```
216
+
217
+ Check whether there is anything to commit:
218
+ ```bash
219
+ git -C <storage_repo> status --porcelain
220
+ ```
221
+
222
+ If the output is empty, skip the commit and continue.
223
+
224
+ Otherwise, run:
225
+ ```bash
226
+ git -C <storage_repo> commit -m "donna(set-role): define role as <job_title>"
227
+ ```
228
+
229
+ If `auto_push` is true in config, also run:
230
+ ```bash
231
+ git -C <storage_repo> push
232
+ ```
233
+ </step>
234
+
235
+ <step name="confirm">
236
+ Print:
237
+ ```
238
+ ✓ Role defined: <job_title>
239
+ ✓ <N> recurring tasks saved to recurring.md
240
+ ✓ Research saved to role-research.md
241
+ ```
242
+
243
+ If any tools were noted during approve-tools, remind:
244
+ ```
245
+ → Run /donna:add-tool to configure your noted tools (available in a future update).
246
+ ```
247
+ </step>
@@ -0,0 +1,184 @@
1
+ # Donna Setup Workflow
2
+
3
+ <objective>
4
+ Guide the user through configuring Donna: set the storage repo path, initialize the file structure, and write the bootstrap config at ~/.config/donna/config.md.
5
+ </objective>
6
+
7
+ <step name="banner">
8
+ Print the Donna banner:
9
+ ```
10
+ ━━━ Donna ▸ Setup ━━━
11
+ ```
12
+ </step>
13
+
14
+ <step name="check-existing-config">
15
+ Read `~/.config/donna/config.md`.
16
+
17
+ If the file exists, proceed to the re-run menu (step: rerun-menu).
18
+ If the file does not exist, proceed to first-run setup (step: ask-storage-path).
19
+ </step>
20
+
21
+ <step name="rerun-menu">
22
+ An existing Donna configuration was found. Use AskUserQuestion to present the user with this menu:
23
+
24
+ ```
25
+ Donna is already configured. What would you like to do?
26
+
27
+ 1. Change storage repo path
28
+ 2. View current config
29
+ 3. Reset (start over — deletes config and re-runs full setup)
30
+ 4. Cancel
31
+ ```
32
+
33
+ Handle each option:
34
+
35
+ - **Option 1:** Ask the user for the new storage repo path using AskUserQuestion. Validate and expand the path (run `echo <path>` via Bash to expand ~). Update `~/.config/donna/config.md` with the new storage_repo value. Print `✓ Storage repo updated.` then print the summary (step: summary).
36
+
37
+ - **Option 2:** Display the contents of `~/.config/donna/config.md`. Then stop.
38
+
39
+ - **Option 3:** Run `rm ~/.config/donna/config.md` via Bash to delete the config. Proceed to first-run setup (step: ask-storage-path).
40
+
41
+ - **Option 4:** Print `Setup cancelled.` and stop.
42
+ </step>
43
+
44
+ <step name="ask-storage-path">
45
+ Use AskUserQuestion to ask the user:
46
+
47
+ ```
48
+ Where is your Donna storage repo?
49
+
50
+ This is a local git repository where Donna stores your daily journal, tasks, and configuration. You can point to an existing git repo, an empty directory (Donna will initialize it), or a path that doesn't exist yet (Donna will create it).
51
+
52
+ Enter the path (e.g. ~/Documents/donna-notes):
53
+ ```
54
+
55
+ Wait for the user's response. Store the path as `<storage_path>`.
56
+ </step>
57
+
58
+ <step name="expand-and-validate-path">
59
+ Expand the path provided by the user:
60
+
61
+ Run via Bash:
62
+ ```bash
63
+ echo <storage_path>
64
+ ```
65
+
66
+ Use the expanded absolute path for all subsequent steps. Store as `<repo>`.
67
+
68
+ Then check the path status:
69
+
70
+ - If `<repo>` is an existing git repo (test: `git -C <repo> rev-parse --is-inside-work-tree 2>/dev/null`): use it as-is. Print `✓ Using existing git repo at <repo>`.
71
+
72
+ - If `<repo>` exists as a directory but is not a git repo: run `git -C <repo> init`. Print `✓ Initialized git repo at <repo>`.
73
+
74
+ - If `<repo>` does not exist: run `mkdir -p <repo>` then `git -C <repo> init`. Print `✓ Created and initialized git repo at <repo>`.
75
+ </step>
76
+
77
+ <step name="detect-daily-folder">
78
+ Determine where daily files should live.
79
+
80
+ **If `<repo>/.obsidian/daily-notes.json` exists:**
81
+ Read it. If it has a `folder` field, use that value as `<daily_folder>`. Print:
82
+ ```
83
+ ✓ Found Obsidian daily notes folder: <daily_folder>
84
+ ```
85
+
86
+ **If `<repo>/.obsidian/` exists but `.obsidian/daily-notes.json` does not exist or has no `folder` field:**
87
+ Set `<daily_folder>` to `daily`. Write `<repo>/.obsidian/daily-notes.json` with:
88
+ ```json
89
+ {
90
+ "folder": "daily"
91
+ }
92
+ ```
93
+ Print:
94
+ ```
95
+ ✓ Configured Obsidian daily notes to use daily/
96
+ ```
97
+
98
+ **If `<repo>/.obsidian/` does not exist:**
99
+ Set `<daily_folder>` to `daily`. Print:
100
+ ```
101
+ ✓ Using daily/ for daily files
102
+ ```
103
+ </step>
104
+
105
+ <step name="create-storage-structure">
106
+ Create the daily directory:
107
+
108
+ Run via Bash:
109
+ ```bash
110
+ mkdir -p <repo>/<daily_folder>
111
+ ```
112
+
113
+ Print:
114
+ ```
115
+ ✓ Created <daily_folder>/ directory
116
+ ```
117
+ </step>
118
+
119
+ <step name="write-bootstrap-config">
120
+ Create the config directory if it does not exist:
121
+
122
+ Run via Bash:
123
+ ```bash
124
+ mkdir -p ~/.config/donna
125
+ ```
126
+
127
+ Write `~/.config/donna/config.md` with the following content (substituting the actual expanded path for `<repo>`):
128
+
129
+ ```markdown
130
+ ---
131
+ storage_repo: <repo>
132
+ daily_folder: <daily_folder>
133
+ auto_push: false
134
+ ---
135
+
136
+ # Donna Configuration
137
+
138
+ This file is managed by `/donna:setup`. All Donna skills read this file to find your storage repo.
139
+ ```
140
+
141
+ Print:
142
+ ```
143
+ ✓ Wrote config at ~/.config/donna/config.md
144
+ ```
145
+ </step>
146
+
147
+ <step name="initial-commit">
148
+ Check whether there is anything to commit:
149
+
150
+ Run via Bash:
151
+ ```bash
152
+ git -C <repo> status --porcelain
153
+ ```
154
+
155
+ If the output is non-empty (there are changes to commit):
156
+
157
+ Run:
158
+ ```bash
159
+ git -C <repo> add -A
160
+ git -C <repo> commit -m "donna(setup): initialize storage"
161
+ ```
162
+
163
+ Print:
164
+ ```
165
+ ✓ Committed initial structure
166
+ ```
167
+
168
+ If the output is empty (nothing to commit), skip the commit and continue.
169
+ </step>
170
+
171
+ <step name="summary">
172
+ Print the completion summary:
173
+
174
+ ```
175
+ ✓ Donna is ready!
176
+
177
+ Storage repo: <repo>
178
+ Config: ~/.config/donna/config.md
179
+
180
+ Next steps:
181
+ - Run /donna:add-task to capture your first task
182
+ - Run /donna:done to mark tasks complete
183
+ ```
184
+ </step>