@draht/coding-agent 2026.3.11-1 → 2026.3.14
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/CHANGELOG.md +45 -0
- package/bin/draht-tools.cjs +187 -32
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +1 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/gsd/domain.d.ts +5 -1
- package/dist/gsd/domain.d.ts.map +1 -1
- package/dist/gsd/domain.js +71 -1
- package/dist/gsd/domain.js.map +1 -1
- package/dist/gsd/git.d.ts.map +1 -1
- package/dist/gsd/git.js +18 -0
- package/dist/gsd/git.js.map +1 -1
- package/dist/gsd/index.d.ts +1 -0
- package/dist/gsd/index.d.ts.map +1 -1
- package/dist/gsd/index.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +2 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/prompts/commands/execute-phase.md +2 -2
- package/dist/prompts/commands/fix.md +2 -2
- package/dist/prompts/commands/plan-phase.md +5 -1
- package/dist/prompts/commands/quick.md +5 -1
- package/dist/utils/changelog.d.ts +12 -0
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +25 -14
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/notify.d.ts +12 -0
- package/dist/utils/notify.d.ts.map +1 -0
- package/dist/utils/notify.js +41 -0
- package/dist/utils/notify.js.map +1 -0
- package/examples/extensions/antigravity-image-gen.ts +5 -4
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +2 -2
- package/examples/extensions/notify.ts +9 -2
- package/examples/extensions/preset.ts +2 -3
- package/examples/extensions/sandbox/index.ts +2 -3
- package/examples/extensions/tool-override.ts +2 -3
- package/package.json +4 -4
- package/prompts/commands/execute-phase.md +2 -2
- package/prompts/commands/fix.md +2 -2
- package/prompts/commands/plan-phase.md +5 -1
- package/prompts/commands/quick.md +5 -1
|
@@ -41,8 +41,8 @@ Execute this plan. Here is the full plan content:
|
|
|
41
41
|
<paste full plan XML here>
|
|
42
42
|
|
|
43
43
|
For each <task> in the plan, follow this TDD cycle:
|
|
44
|
-
1. RED — Write failing tests from <test>. Run the test runner, confirm they FAIL. Commit with: git add <test-files> && git commit -m "
|
|
45
|
-
2. GREEN — Write minimal implementation from <action> to make tests pass. Run tests, confirm PASS. Commit with: git add <files> && git commit -m "
|
|
44
|
+
1. RED — Write failing tests from <test>. Run the test runner, confirm they FAIL. Commit with: git add <test-files> && git commit -m "test: <description>"
|
|
45
|
+
2. GREEN — Write minimal implementation from <action> to make tests pass. Run tests, confirm PASS. Commit with: git add <files> && git commit -m "feat: <task name>"
|
|
46
46
|
3. REFACTOR — Apply <refactor> improvements if any. Tests must stay green after each change. Commit with: git add <files> && git commit -m "refactor: <description>"
|
|
47
47
|
4. VERIFY — Run the <verify> step, confirm <done> criteria are met.
|
|
48
48
|
|
|
@@ -18,12 +18,12 @@ Issue: $ARGUMENTS
|
|
|
18
18
|
"Diagnose this issue: $ARGUMENTS. Reproduce the bug by running the relevant test or command. Trace the root cause by reading the code. Identify the exact files and lines involved. Do NOT fix it yet — only report the diagnosis with: root cause, affected files, and a recommended fix approach. Do NOT run draht, draht-tools, or pi commands."
|
|
19
19
|
|
|
20
20
|
2. **Write a reproducing test**: Based on the diagnosis, write a test that demonstrates the bug (it must fail)
|
|
21
|
-
- Commit: `draht-tools commit-docs "
|
|
21
|
+
- Commit: `draht-tools commit-docs "test: reproduce bug"`
|
|
22
22
|
|
|
23
23
|
3. **Minimal fix**: Write the smallest change that makes the test pass
|
|
24
24
|
- Do not refactor or add features — just fix the bug
|
|
25
25
|
- Run the full test suite to check for regressions
|
|
26
|
-
- Commit: `draht-tools commit-docs "
|
|
26
|
+
- Commit: `draht-tools commit-docs "fix: fix description"`
|
|
27
27
|
|
|
28
28
|
4. **Refactor** (if needed): Clean up without changing behavior
|
|
29
29
|
- Tests must stay green after every change
|
|
@@ -34,7 +34,11 @@ Phase: $1
|
|
|
34
34
|
- Instruction to output the plan as XML (you will save it via `draht-tools create-plan`)
|
|
35
35
|
|
|
36
36
|
6. Collect all plan outputs from subagents
|
|
37
|
-
7. Save
|
|
37
|
+
7. Save each plan by piping the subagent's output into `draht-tools create-plan`:
|
|
38
|
+
```
|
|
39
|
+
echo 'plan content from subagent' | draht-tools create-plan $1 P [title]
|
|
40
|
+
```
|
|
41
|
+
The content must contain real task details (files, actions, tests) — NOT placeholder brackets. If `create-plan` is called without stdin, it writes a useless template.
|
|
38
42
|
8. Validate: `draht-tools validate-plans $1`
|
|
39
43
|
9. Commit: `draht-tools commit-docs "create phase $1 plans"`
|
|
40
44
|
|
|
@@ -15,7 +15,11 @@ Task: $ARGUMENTS
|
|
|
15
15
|
|
|
16
16
|
## Steps
|
|
17
17
|
1. Run `draht-tools next-quick-number` to get task number
|
|
18
|
-
2.
|
|
18
|
+
2. Analyze the task and write a concrete plan with actual task details (files, actions, verification). Pipe it into `draht-tools create-quick-plan`:
|
|
19
|
+
```
|
|
20
|
+
echo 'plan content here' | draht-tools create-quick-plan NNN "$ARGUMENTS"
|
|
21
|
+
```
|
|
22
|
+
The plan content must include: a `# Quick Task NNN: title` heading, a `## Tasks` section with one or more `<task>` XML blocks containing real file paths, real actions, and real verification steps — NOT placeholders like `[files]`.
|
|
19
23
|
3. **Delegate execution to subagent**: Use the `subagent` tool in **single mode** with the `implementer` agent:
|
|
20
24
|
"Execute this task: $ARGUMENTS
|
|
21
25
|
|
|
@@ -2,8 +2,20 @@ export interface ChangelogEntry {
|
|
|
2
2
|
major: number;
|
|
3
3
|
minor: number;
|
|
4
4
|
patch: number;
|
|
5
|
+
/** Intraday release suffix for date-based versions (e.g., -1, -2) */
|
|
6
|
+
suffix: number;
|
|
5
7
|
content: string;
|
|
6
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Parse a version string into components.
|
|
11
|
+
* Handles both semver (x.y.z) and date-based versions (YYYY.M.D or YYYY.M.D-N).
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseVersion(version: string): {
|
|
14
|
+
major: number;
|
|
15
|
+
minor: number;
|
|
16
|
+
patch: number;
|
|
17
|
+
suffix: number;
|
|
18
|
+
};
|
|
7
19
|
/**
|
|
8
20
|
* Parse changelog entries from CHANGELOG.md
|
|
9
21
|
* Scans for ## lines and collects content until next ## or EOF
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/utils/changelog.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/utils/changelog.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAY7G;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,cAAc,EAAE,CAqDtE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,GAAG,MAAM,CAK9E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,cAAc,EAAE,CAO9F;AAGD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC","sourcesContent":["import { existsSync, readFileSync } from \"fs\";\n\nexport interface ChangelogEntry {\n\tmajor: number;\n\tminor: number;\n\tpatch: number;\n\t/** Intraday release suffix for date-based versions (e.g., -1, -2) */\n\tsuffix: number;\n\tcontent: string;\n}\n\n/**\n * Parse a version string into components.\n * Handles both semver (x.y.z) and date-based versions (YYYY.M.D or YYYY.M.D-N).\n */\nexport function parseVersion(version: string): { major: number; minor: number; patch: number; suffix: number } {\n\t// Match YYYY.M.D-N or YYYY.M.D or x.y.z\n\tconst match = version.match(/^(\\d+)\\.(\\d+)\\.(\\d+)(?:-(\\d+))?/);\n\tif (!match) {\n\t\treturn { major: 0, minor: 0, patch: 0, suffix: 0 };\n\t}\n\treturn {\n\t\tmajor: Number.parseInt(match[1], 10),\n\t\tminor: Number.parseInt(match[2], 10),\n\t\tpatch: Number.parseInt(match[3], 10),\n\t\tsuffix: match[4] ? Number.parseInt(match[4], 10) : 0,\n\t};\n}\n\n/**\n * Parse changelog entries from CHANGELOG.md\n * Scans for ## lines and collects content until next ## or EOF\n */\nexport function parseChangelog(changelogPath: string): ChangelogEntry[] {\n\tif (!existsSync(changelogPath)) {\n\t\treturn [];\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(changelogPath, \"utf-8\");\n\t\tconst lines = content.split(\"\\n\");\n\t\tconst entries: ChangelogEntry[] = [];\n\n\t\tlet currentLines: string[] = [];\n\t\tlet currentVersion: { major: number; minor: number; patch: number; suffix: number } | null = null;\n\n\t\tfor (const line of lines) {\n\t\t\t// Check if this is a version header (## [x.y.z] or ## [YYYY.M.D-N] ...)\n\t\t\tif (line.startsWith(\"## \")) {\n\t\t\t\t// Save previous entry if exists\n\t\t\t\tif (currentVersion && currentLines.length > 0) {\n\t\t\t\t\tentries.push({\n\t\t\t\t\t\t...currentVersion,\n\t\t\t\t\t\tcontent: currentLines.join(\"\\n\").trim(),\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Try to parse version from this line (supports YYYY.M.D-N format)\n\t\t\t\tconst versionMatch = line.match(/##\\s+\\[?(\\d+\\.\\d+\\.\\d+(?:-\\d+)?)\\]?/);\n\t\t\t\tif (versionMatch) {\n\t\t\t\t\tcurrentVersion = parseVersion(versionMatch[1]);\n\t\t\t\t\tcurrentLines = [line];\n\t\t\t\t} else {\n\t\t\t\t\t// Reset if we can't parse version\n\t\t\t\t\tcurrentVersion = null;\n\t\t\t\t\tcurrentLines = [];\n\t\t\t\t}\n\t\t\t} else if (currentVersion) {\n\t\t\t\t// Collect lines for current version\n\t\t\t\tcurrentLines.push(line);\n\t\t\t}\n\t\t}\n\n\t\t// Save last entry\n\t\tif (currentVersion && currentLines.length > 0) {\n\t\t\tentries.push({\n\t\t\t\t...currentVersion,\n\t\t\t\tcontent: currentLines.join(\"\\n\").trim(),\n\t\t\t});\n\t\t}\n\n\t\treturn entries;\n\t} catch (error) {\n\t\tconsole.error(`Warning: Could not parse changelog: ${error}`);\n\t\treturn [];\n\t}\n}\n\n/**\n * Compare versions. Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2\n */\nexport function compareVersions(v1: ChangelogEntry, v2: ChangelogEntry): number {\n\tif (v1.major !== v2.major) return v1.major - v2.major;\n\tif (v1.minor !== v2.minor) return v1.minor - v2.minor;\n\tif (v1.patch !== v2.patch) return v1.patch - v2.patch;\n\treturn v1.suffix - v2.suffix;\n}\n\n/**\n * Get entries newer than lastVersion\n */\nexport function getNewEntries(entries: ChangelogEntry[], lastVersion: string): ChangelogEntry[] {\n\tconst last: ChangelogEntry = {\n\t\t...parseVersion(lastVersion),\n\t\tcontent: \"\",\n\t};\n\n\treturn entries.filter((entry) => compareVersions(entry, last) > 0);\n}\n\n// Re-export getChangelogPath from paths.ts for convenience\nexport { getChangelogPath } from \"../config.js\";\n"]}
|
package/dist/utils/changelog.js
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "fs";
|
|
2
|
+
/**
|
|
3
|
+
* Parse a version string into components.
|
|
4
|
+
* Handles both semver (x.y.z) and date-based versions (YYYY.M.D or YYYY.M.D-N).
|
|
5
|
+
*/
|
|
6
|
+
export function parseVersion(version) {
|
|
7
|
+
// Match YYYY.M.D-N or YYYY.M.D or x.y.z
|
|
8
|
+
const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(\d+))?/);
|
|
9
|
+
if (!match) {
|
|
10
|
+
return { major: 0, minor: 0, patch: 0, suffix: 0 };
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
major: Number.parseInt(match[1], 10),
|
|
14
|
+
minor: Number.parseInt(match[2], 10),
|
|
15
|
+
patch: Number.parseInt(match[3], 10),
|
|
16
|
+
suffix: match[4] ? Number.parseInt(match[4], 10) : 0,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
2
19
|
/**
|
|
3
20
|
* Parse changelog entries from CHANGELOG.md
|
|
4
21
|
* Scans for ## lines and collects content until next ## or EOF
|
|
@@ -14,7 +31,7 @@ export function parseChangelog(changelogPath) {
|
|
|
14
31
|
let currentLines = [];
|
|
15
32
|
let currentVersion = null;
|
|
16
33
|
for (const line of lines) {
|
|
17
|
-
// Check if this is a version header (## [x.y.z] ...)
|
|
34
|
+
// Check if this is a version header (## [x.y.z] or ## [YYYY.M.D-N] ...)
|
|
18
35
|
if (line.startsWith("## ")) {
|
|
19
36
|
// Save previous entry if exists
|
|
20
37
|
if (currentVersion && currentLines.length > 0) {
|
|
@@ -23,14 +40,10 @@ export function parseChangelog(changelogPath) {
|
|
|
23
40
|
content: currentLines.join("\n").trim(),
|
|
24
41
|
});
|
|
25
42
|
}
|
|
26
|
-
// Try to parse version from this line
|
|
27
|
-
const versionMatch = line.match(/##\s+\[?(\d
|
|
43
|
+
// Try to parse version from this line (supports YYYY.M.D-N format)
|
|
44
|
+
const versionMatch = line.match(/##\s+\[?(\d+\.\d+\.\d+(?:-\d+)?)\]?/);
|
|
28
45
|
if (versionMatch) {
|
|
29
|
-
currentVersion =
|
|
30
|
-
major: Number.parseInt(versionMatch[1], 10),
|
|
31
|
-
minor: Number.parseInt(versionMatch[2], 10),
|
|
32
|
-
patch: Number.parseInt(versionMatch[3], 10),
|
|
33
|
-
};
|
|
46
|
+
currentVersion = parseVersion(versionMatch[1]);
|
|
34
47
|
currentLines = [line];
|
|
35
48
|
}
|
|
36
49
|
else {
|
|
@@ -66,18 +79,16 @@ export function compareVersions(v1, v2) {
|
|
|
66
79
|
return v1.major - v2.major;
|
|
67
80
|
if (v1.minor !== v2.minor)
|
|
68
81
|
return v1.minor - v2.minor;
|
|
69
|
-
|
|
82
|
+
if (v1.patch !== v2.patch)
|
|
83
|
+
return v1.patch - v2.patch;
|
|
84
|
+
return v1.suffix - v2.suffix;
|
|
70
85
|
}
|
|
71
86
|
/**
|
|
72
87
|
* Get entries newer than lastVersion
|
|
73
88
|
*/
|
|
74
89
|
export function getNewEntries(entries, lastVersion) {
|
|
75
|
-
// Parse lastVersion
|
|
76
|
-
const parts = lastVersion.split(".").map(Number);
|
|
77
90
|
const last = {
|
|
78
|
-
|
|
79
|
-
minor: parts[1] || 0,
|
|
80
|
-
patch: parts[2] || 0,
|
|
91
|
+
...parseVersion(lastVersion),
|
|
81
92
|
content: "",
|
|
82
93
|
};
|
|
83
94
|
return entries.filter((entry) => compareVersions(entry, last) > 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../src/utils/changelog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../src/utils/changelog.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from \"fs\";\n\nexport interface ChangelogEntry {\n\tmajor: number;\n\tminor: number;\n\tpatch: number;\n\t/** Intraday release suffix for date-based versions (e.g., -1, -2) */\n\tsuffix: number;\n\tcontent: string;\n}\n\n/**\n * Parse a version string into components.\n * Handles both semver (x.y.z) and date-based versions (YYYY.M.D or YYYY.M.D-N).\n */\nexport function parseVersion(version: string): { major: number; minor: number; patch: number; suffix: number } {\n\t// Match YYYY.M.D-N or YYYY.M.D or x.y.z\n\tconst match = version.match(/^(\\d+)\\.(\\d+)\\.(\\d+)(?:-(\\d+))?/);\n\tif (!match) {\n\t\treturn { major: 0, minor: 0, patch: 0, suffix: 0 };\n\t}\n\treturn {\n\t\tmajor: Number.parseInt(match[1], 10),\n\t\tminor: Number.parseInt(match[2], 10),\n\t\tpatch: Number.parseInt(match[3], 10),\n\t\tsuffix: match[4] ? Number.parseInt(match[4], 10) : 0,\n\t};\n}\n\n/**\n * Parse changelog entries from CHANGELOG.md\n * Scans for ## lines and collects content until next ## or EOF\n */\nexport function parseChangelog(changelogPath: string): ChangelogEntry[] {\n\tif (!existsSync(changelogPath)) {\n\t\treturn [];\n\t}\n\n\ttry {\n\t\tconst content = readFileSync(changelogPath, \"utf-8\");\n\t\tconst lines = content.split(\"\\n\");\n\t\tconst entries: ChangelogEntry[] = [];\n\n\t\tlet currentLines: string[] = [];\n\t\tlet currentVersion: { major: number; minor: number; patch: number; suffix: number } | null = null;\n\n\t\tfor (const line of lines) {\n\t\t\t// Check if this is a version header (## [x.y.z] or ## [YYYY.M.D-N] ...)\n\t\t\tif (line.startsWith(\"## \")) {\n\t\t\t\t// Save previous entry if exists\n\t\t\t\tif (currentVersion && currentLines.length > 0) {\n\t\t\t\t\tentries.push({\n\t\t\t\t\t\t...currentVersion,\n\t\t\t\t\t\tcontent: currentLines.join(\"\\n\").trim(),\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Try to parse version from this line (supports YYYY.M.D-N format)\n\t\t\t\tconst versionMatch = line.match(/##\\s+\\[?(\\d+\\.\\d+\\.\\d+(?:-\\d+)?)\\]?/);\n\t\t\t\tif (versionMatch) {\n\t\t\t\t\tcurrentVersion = parseVersion(versionMatch[1]);\n\t\t\t\t\tcurrentLines = [line];\n\t\t\t\t} else {\n\t\t\t\t\t// Reset if we can't parse version\n\t\t\t\t\tcurrentVersion = null;\n\t\t\t\t\tcurrentLines = [];\n\t\t\t\t}\n\t\t\t} else if (currentVersion) {\n\t\t\t\t// Collect lines for current version\n\t\t\t\tcurrentLines.push(line);\n\t\t\t}\n\t\t}\n\n\t\t// Save last entry\n\t\tif (currentVersion && currentLines.length > 0) {\n\t\t\tentries.push({\n\t\t\t\t...currentVersion,\n\t\t\t\tcontent: currentLines.join(\"\\n\").trim(),\n\t\t\t});\n\t\t}\n\n\t\treturn entries;\n\t} catch (error) {\n\t\tconsole.error(`Warning: Could not parse changelog: ${error}`);\n\t\treturn [];\n\t}\n}\n\n/**\n * Compare versions. Returns: -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2\n */\nexport function compareVersions(v1: ChangelogEntry, v2: ChangelogEntry): number {\n\tif (v1.major !== v2.major) return v1.major - v2.major;\n\tif (v1.minor !== v2.minor) return v1.minor - v2.minor;\n\tif (v1.patch !== v2.patch) return v1.patch - v2.patch;\n\treturn v1.suffix - v2.suffix;\n}\n\n/**\n * Get entries newer than lastVersion\n */\nexport function getNewEntries(entries: ChangelogEntry[], lastVersion: string): ChangelogEntry[] {\n\tconst last: ChangelogEntry = {\n\t\t...parseVersion(lastVersion),\n\t\tcontent: \"\",\n\t};\n\n\treturn entries.filter((entry) => compareVersions(entry, last) > 0);\n}\n\n// Re-export getChangelogPath from paths.ts for convenience\nexport { getChangelogPath } from \"../config.js\";\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal notification utility.
|
|
3
|
+
*
|
|
4
|
+
* Sends a native notification when the agent finishes its turn.
|
|
5
|
+
* Detection order:
|
|
6
|
+
* - cmux: `cmux notify` CLI
|
|
7
|
+
* - Windows Terminal (WSL): PowerShell toast
|
|
8
|
+
* - Kitty: OSC 99
|
|
9
|
+
* - Fallback: OSC 777 (Ghostty, iTerm2, WezTerm, rxvt-unicode)
|
|
10
|
+
*/
|
|
11
|
+
export declare function sendTerminalNotification(title: string, body: string): void;
|
|
12
|
+
//# sourceMappingURL=notify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/utils/notify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAiBH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAa1E","sourcesContent":["/**\n * Terminal notification utility.\n *\n * Sends a native notification when the agent finishes its turn.\n * Detection order:\n * - cmux: `cmux notify` CLI\n * - Windows Terminal (WSL): PowerShell toast\n * - Kitty: OSC 99\n * - Fallback: OSC 777 (Ghostty, iTerm2, WezTerm, rxvt-unicode)\n */\n\nimport { execFile } from \"child_process\";\n\nfunction windowsToastScript(title: string, body: string): string {\n\tconst type = \"Windows.UI.Notifications\";\n\tconst mgr = `[${type}.ToastNotificationManager, ${type}, ContentType = WindowsRuntime]`;\n\tconst template = `[${type}.ToastTemplateType]::ToastText01`;\n\tconst toast = `[${type}.ToastNotification]::new($xml)`;\n\treturn [\n\t\t`${mgr} > $null`,\n\t\t`$xml = [${type}.ToastNotificationManager]::GetTemplateContent(${template})`,\n\t\t`$xml.GetElementsByTagName('text')[0].AppendChild($xml.CreateTextNode('${body}')) > $null`,\n\t\t`[${type}.ToastNotificationManager]::CreateToastNotifier('${title}').Show(${toast})`,\n\t].join(\"; \");\n}\n\nexport function sendTerminalNotification(title: string, body: string): void {\n\tif (process.env.CMUX_BUNDLE_ID) {\n\t\texecFile(\"cmux\", [\"notify\", \"--title\", title, \"--body\", body]);\n\t} else if (process.env.WT_SESSION) {\n\t\texecFile(\"powershell.exe\", [\"-NoProfile\", \"-Command\", windowsToastScript(title, body)]);\n\t} else if (process.env.KITTY_WINDOW_ID) {\n\t\t// Kitty OSC 99\n\t\tprocess.stdout.write(`\\x1b]99;i=1:d=0;${title}\\x1b\\\\`);\n\t\tprocess.stdout.write(`\\x1b]99;i=1:p=body;${body}\\x1b\\\\`);\n\t} else {\n\t\t// OSC 777 (Ghostty, iTerm2, WezTerm, rxvt-unicode)\n\t\tprocess.stdout.write(`\\x1b]777;notify;${title};${body}\\x07`);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal notification utility.
|
|
3
|
+
*
|
|
4
|
+
* Sends a native notification when the agent finishes its turn.
|
|
5
|
+
* Detection order:
|
|
6
|
+
* - cmux: `cmux notify` CLI
|
|
7
|
+
* - Windows Terminal (WSL): PowerShell toast
|
|
8
|
+
* - Kitty: OSC 99
|
|
9
|
+
* - Fallback: OSC 777 (Ghostty, iTerm2, WezTerm, rxvt-unicode)
|
|
10
|
+
*/
|
|
11
|
+
import { execFile } from "child_process";
|
|
12
|
+
function windowsToastScript(title, body) {
|
|
13
|
+
const type = "Windows.UI.Notifications";
|
|
14
|
+
const mgr = `[${type}.ToastNotificationManager, ${type}, ContentType = WindowsRuntime]`;
|
|
15
|
+
const template = `[${type}.ToastTemplateType]::ToastText01`;
|
|
16
|
+
const toast = `[${type}.ToastNotification]::new($xml)`;
|
|
17
|
+
return [
|
|
18
|
+
`${mgr} > $null`,
|
|
19
|
+
`$xml = [${type}.ToastNotificationManager]::GetTemplateContent(${template})`,
|
|
20
|
+
`$xml.GetElementsByTagName('text')[0].AppendChild($xml.CreateTextNode('${body}')) > $null`,
|
|
21
|
+
`[${type}.ToastNotificationManager]::CreateToastNotifier('${title}').Show(${toast})`,
|
|
22
|
+
].join("; ");
|
|
23
|
+
}
|
|
24
|
+
export function sendTerminalNotification(title, body) {
|
|
25
|
+
if (process.env.CMUX_BUNDLE_ID) {
|
|
26
|
+
execFile("cmux", ["notify", "--title", title, "--body", body]);
|
|
27
|
+
}
|
|
28
|
+
else if (process.env.WT_SESSION) {
|
|
29
|
+
execFile("powershell.exe", ["-NoProfile", "-Command", windowsToastScript(title, body)]);
|
|
30
|
+
}
|
|
31
|
+
else if (process.env.KITTY_WINDOW_ID) {
|
|
32
|
+
// Kitty OSC 99
|
|
33
|
+
process.stdout.write(`\x1b]99;i=1:d=0;${title}\x1b\\`);
|
|
34
|
+
process.stdout.write(`\x1b]99;i=1:p=body;${body}\x1b\\`);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// OSC 777 (Ghostty, iTerm2, WezTerm, rxvt-unicode)
|
|
38
|
+
process.stdout.write(`\x1b]777;notify;${title};${body}\x07`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/utils/notify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,SAAS,kBAAkB,CAAC,KAAa,EAAE,IAAY,EAAU;IAChE,MAAM,IAAI,GAAG,0BAA0B,CAAC;IACxC,MAAM,GAAG,GAAG,IAAI,IAAI,8BAA8B,IAAI,iCAAiC,CAAC;IACxF,MAAM,QAAQ,GAAG,IAAI,IAAI,kCAAkC,CAAC;IAC5D,MAAM,KAAK,GAAG,IAAI,IAAI,gCAAgC,CAAC;IACvD,OAAO;QACN,GAAG,GAAG,UAAU;QAChB,WAAW,IAAI,kDAAkD,QAAQ,GAAG;QAC5E,yEAAyE,IAAI,aAAa;QAC1F,IAAI,IAAI,oDAAoD,KAAK,WAAW,KAAK,GAAG;KACpF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CACb;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAa,EAAE,IAAY,EAAQ;IAC3E,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC;QAChC,QAAQ,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;IAChE,CAAC;SAAM,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;QACnC,QAAQ,CAAC,gBAAgB,EAAE,CAAC,YAAY,EAAE,UAAU,EAAE,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACzF,CAAC;SAAM,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC;QACxC,eAAe;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,KAAK,QAAQ,CAAC,CAAC;QACvD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,IAAI,QAAQ,CAAC,CAAC;IAC1D,CAAC;SAAM,CAAC;QACP,mDAAmD;QACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,KAAK,IAAI,IAAI,MAAM,CAAC,CAAC;IAC9D,CAAC;AAAA,CACD","sourcesContent":["/**\n * Terminal notification utility.\n *\n * Sends a native notification when the agent finishes its turn.\n * Detection order:\n * - cmux: `cmux notify` CLI\n * - Windows Terminal (WSL): PowerShell toast\n * - Kitty: OSC 99\n * - Fallback: OSC 777 (Ghostty, iTerm2, WezTerm, rxvt-unicode)\n */\n\nimport { execFile } from \"child_process\";\n\nfunction windowsToastScript(title: string, body: string): string {\n\tconst type = \"Windows.UI.Notifications\";\n\tconst mgr = `[${type}.ToastNotificationManager, ${type}, ContentType = WindowsRuntime]`;\n\tconst template = `[${type}.ToastTemplateType]::ToastText01`;\n\tconst toast = `[${type}.ToastNotification]::new($xml)`;\n\treturn [\n\t\t`${mgr} > $null`,\n\t\t`$xml = [${type}.ToastNotificationManager]::GetTemplateContent(${template})`,\n\t\t`$xml.GetElementsByTagName('text')[0].AppendChild($xml.CreateTextNode('${body}')) > $null`,\n\t\t`[${type}.ToastNotificationManager]::CreateToastNotifier('${title}').Show(${toast})`,\n\t].join(\"; \");\n}\n\nexport function sendTerminalNotification(title: string, body: string): void {\n\tif (process.env.CMUX_BUNDLE_ID) {\n\t\texecFile(\"cmux\", [\"notify\", \"--title\", title, \"--body\", body]);\n\t} else if (process.env.WT_SESSION) {\n\t\texecFile(\"powershell.exe\", [\"-NoProfile\", \"-Command\", windowsToastScript(title, body)]);\n\t} else if (process.env.KITTY_WINDOW_ID) {\n\t\t// Kitty OSC 99\n\t\tprocess.stdout.write(`\\x1b]99;i=1:d=0;${title}\\x1b\\\\`);\n\t\tprocess.stdout.write(`\\x1b]99;i=1:p=body;${body}\\x1b\\\\`);\n\t} else {\n\t\t// OSC 777 (Ghostty, iTerm2, WezTerm, rxvt-unicode)\n\t\tprocess.stdout.write(`\\x1b]777;notify;${title};${body}\\x07`);\n\t}\n}\n"]}
|
|
@@ -28,10 +28,9 @@
|
|
|
28
28
|
import { randomUUID } from "node:crypto";
|
|
29
29
|
import { existsSync, readFileSync } from "node:fs";
|
|
30
30
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
31
|
-
import { homedir } from "node:os";
|
|
32
31
|
import { join } from "node:path";
|
|
33
32
|
import { StringEnum } from "@draht/ai";
|
|
34
|
-
import type
|
|
33
|
+
import { type ExtensionAPI, getAgentDir } from "@draht/coding-agent";
|
|
35
34
|
import { type Static, Type } from "@sinclair/typebox";
|
|
36
35
|
|
|
37
36
|
const PROVIDER = "google-antigravity";
|
|
@@ -184,7 +183,8 @@ function readConfigFile(path: string): ExtensionConfig {
|
|
|
184
183
|
}
|
|
185
184
|
|
|
186
185
|
function loadConfig(cwd: string): ExtensionConfig {
|
|
187
|
-
const
|
|
186
|
+
const globalPath = join(getAgentDir(), "extensions", "antigravity-image-gen.json");
|
|
187
|
+
const globalConfig = readConfigFile(globalPath);
|
|
188
188
|
const projectConfig = readConfigFile(join(cwd, ".pi", "extensions", "antigravity-image-gen.json"));
|
|
189
189
|
return { ...globalConfig, ...projectConfig };
|
|
190
190
|
}
|
|
@@ -204,7 +204,8 @@ function resolveSaveConfig(params: ToolParams, cwd: string): SaveConfig {
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
if (mode === "global") {
|
|
207
|
-
|
|
207
|
+
const outputDir = join(getAgentDir(), "generated-images");
|
|
208
|
+
return { mode, outputDir };
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
if (mode === "custom") {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { type Api, type Context, type Model, registerApiProvider, streamSimple } from "@draht/ai";
|
|
12
12
|
import { readFileSync } from "fs";
|
|
13
|
-
import {
|
|
13
|
+
import { getAgentDir } from "packages/coding-agent/src/config.js";
|
|
14
14
|
import { join } from "path";
|
|
15
15
|
import { MODELS, streamGitLabDuo } from "./index.js";
|
|
16
16
|
|
|
@@ -28,7 +28,7 @@ async function main() {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// Read auth
|
|
31
|
-
const authPath = join(
|
|
31
|
+
const authPath = join(getAgentDir(), "extensions", "auth.json");
|
|
32
32
|
const authData = JSON.parse(readFileSync(authPath, "utf-8"));
|
|
33
33
|
const gitlabCred = authData["gitlab-duo"];
|
|
34
34
|
if (!gitlabCred?.access) {
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Sends a native terminal notification when Pi agent is done and waiting for input.
|
|
5
5
|
* Supports multiple terminal protocols:
|
|
6
|
+
* - cmux: uses `cmux notify` CLI
|
|
6
7
|
* - OSC 777: Ghostty, iTerm2, WezTerm, rxvt-unicode
|
|
7
8
|
* - OSC 99: Kitty
|
|
8
9
|
* - Windows toast: Windows Terminal (WSL)
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import type { ExtensionAPI } from "@draht/coding-agent";
|
|
13
|
+
import { execFile } from "child_process";
|
|
12
14
|
|
|
13
15
|
function windowsToastScript(title: string, body: string): string {
|
|
14
16
|
const type = "Windows.UI.Notifications";
|
|
@@ -33,13 +35,18 @@ function notifyOSC99(title: string, body: string): void {
|
|
|
33
35
|
process.stdout.write(`\x1b]99;i=1:p=body;${body}\x1b\\`);
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
function notifyCmux(title: string, body: string): void {
|
|
39
|
+
execFile("cmux", ["notify", "--title", title, "--body", body]);
|
|
40
|
+
}
|
|
41
|
+
|
|
36
42
|
function notifyWindows(title: string, body: string): void {
|
|
37
|
-
const { execFile } = require("child_process");
|
|
38
43
|
execFile("powershell.exe", ["-NoProfile", "-Command", windowsToastScript(title, body)]);
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
function notify(title: string, body: string): void {
|
|
42
|
-
if (process.env.
|
|
47
|
+
if (process.env.CMUX_BUNDLE_ID) {
|
|
48
|
+
notifyCmux(title, body);
|
|
49
|
+
} else if (process.env.WT_SESSION) {
|
|
43
50
|
notifyWindows(title, body);
|
|
44
51
|
} else if (process.env.KITTY_WINDOW_ID) {
|
|
45
52
|
notifyOSC99(title, body);
|
|
@@ -39,10 +39,9 @@
|
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
import { existsSync, readFileSync } from "node:fs";
|
|
42
|
-
import { homedir } from "node:os";
|
|
43
42
|
import { join } from "node:path";
|
|
44
43
|
import type { ExtensionAPI, ExtensionContext } from "@draht/coding-agent";
|
|
45
|
-
import { DynamicBorder } from "@draht/coding-agent";
|
|
44
|
+
import { DynamicBorder, getAgentDir } from "@draht/coding-agent";
|
|
46
45
|
import { Container, Key, type SelectItem, SelectList, Text } from "@draht/tui";
|
|
47
46
|
|
|
48
47
|
// Preset configuration
|
|
@@ -68,7 +67,7 @@ interface PresetsConfig {
|
|
|
68
67
|
* Project-local presets override global presets with the same name.
|
|
69
68
|
*/
|
|
70
69
|
function loadPresets(cwd: string): PresetsConfig {
|
|
71
|
-
const globalPath = join(
|
|
70
|
+
const globalPath = join(getAgentDir(), "presets.json");
|
|
72
71
|
const projectPath = join(cwd, ".pi", "presets.json");
|
|
73
72
|
|
|
74
73
|
let globalPresets: PresetsConfig = {};
|
|
@@ -39,11 +39,10 @@
|
|
|
39
39
|
|
|
40
40
|
import { spawn } from "node:child_process";
|
|
41
41
|
import { existsSync, readFileSync } from "node:fs";
|
|
42
|
-
import { homedir } from "node:os";
|
|
43
42
|
import { join } from "node:path";
|
|
44
43
|
import { SandboxManager, type SandboxRuntimeConfig } from "@anthropic-ai/sandbox-runtime";
|
|
45
44
|
import type { ExtensionAPI } from "@draht/coding-agent";
|
|
46
|
-
import { type BashOperations, createBashTool } from "@draht/coding-agent";
|
|
45
|
+
import { type BashOperations, createBashTool, getAgentDir } from "@draht/coding-agent";
|
|
47
46
|
|
|
48
47
|
interface SandboxConfig extends SandboxRuntimeConfig {
|
|
49
48
|
enabled?: boolean;
|
|
@@ -75,7 +74,7 @@ const DEFAULT_CONFIG: SandboxConfig = {
|
|
|
75
74
|
|
|
76
75
|
function loadConfig(cwd: string): SandboxConfig {
|
|
77
76
|
const projectConfigPath = join(cwd, ".pi", "sandbox.json");
|
|
78
|
-
const globalConfigPath = join(
|
|
77
|
+
const globalConfigPath = join(getAgentDir(), "extensions", "sandbox.json");
|
|
79
78
|
|
|
80
79
|
let globalConfig: Partial<SandboxConfig> = {};
|
|
81
80
|
let projectConfig: Partial<SandboxConfig> = {};
|
|
@@ -21,14 +21,13 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
import type { TextContent } from "@draht/ai";
|
|
24
|
-
import type
|
|
24
|
+
import { type ExtensionAPI, getAgentDir } from "@draht/coding-agent";
|
|
25
25
|
import { Type } from "@sinclair/typebox";
|
|
26
26
|
import { appendFileSync, constants, readFileSync } from "fs";
|
|
27
27
|
import { access, readFile } from "fs/promises";
|
|
28
|
-
import { homedir } from "os";
|
|
29
28
|
import { join, resolve } from "path";
|
|
30
29
|
|
|
31
|
-
const LOG_FILE = join(
|
|
30
|
+
const LOG_FILE = join(getAgentDir(), "read-access.log");
|
|
32
31
|
|
|
33
32
|
// Paths that are blocked from reading
|
|
34
33
|
const BLOCKED_PATTERNS = [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@draht/coding-agent",
|
|
3
|
-
"version": "2026.3.
|
|
3
|
+
"version": "2026.3.14",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"drahtConfig": {
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
"@mariozechner/jiti": "^2.6.2",
|
|
49
49
|
"@sinclair/typebox": "^0.34.41",
|
|
50
50
|
"ajv": "^8.17.1",
|
|
51
|
-
"@draht/agent-core": "2026.3.
|
|
52
|
-
"@draht/ai": "2026.3.
|
|
53
|
-
"@draht/tui": "2026.3.
|
|
51
|
+
"@draht/agent-core": "2026.3.14",
|
|
52
|
+
"@draht/ai": "2026.3.14",
|
|
53
|
+
"@draht/tui": "2026.3.14",
|
|
54
54
|
"@silvia-odwyer/photon-node": "^0.3.4",
|
|
55
55
|
"chalk": "^5.5.0",
|
|
56
56
|
"cli-highlight": "^2.1.11",
|
|
@@ -41,8 +41,8 @@ Execute this plan. Here is the full plan content:
|
|
|
41
41
|
<paste full plan XML here>
|
|
42
42
|
|
|
43
43
|
For each <task> in the plan, follow this TDD cycle:
|
|
44
|
-
1. RED — Write failing tests from <test>. Run the test runner, confirm they FAIL. Commit with: git add <test-files> && git commit -m "
|
|
45
|
-
2. GREEN — Write minimal implementation from <action> to make tests pass. Run tests, confirm PASS. Commit with: git add <files> && git commit -m "
|
|
44
|
+
1. RED — Write failing tests from <test>. Run the test runner, confirm they FAIL. Commit with: git add <test-files> && git commit -m "test: <description>"
|
|
45
|
+
2. GREEN — Write minimal implementation from <action> to make tests pass. Run tests, confirm PASS. Commit with: git add <files> && git commit -m "feat: <task name>"
|
|
46
46
|
3. REFACTOR — Apply <refactor> improvements if any. Tests must stay green after each change. Commit with: git add <files> && git commit -m "refactor: <description>"
|
|
47
47
|
4. VERIFY — Run the <verify> step, confirm <done> criteria are met.
|
|
48
48
|
|
package/prompts/commands/fix.md
CHANGED
|
@@ -18,12 +18,12 @@ Issue: $ARGUMENTS
|
|
|
18
18
|
"Diagnose this issue: $ARGUMENTS. Reproduce the bug by running the relevant test or command. Trace the root cause by reading the code. Identify the exact files and lines involved. Do NOT fix it yet — only report the diagnosis with: root cause, affected files, and a recommended fix approach. Do NOT run draht, draht-tools, or pi commands."
|
|
19
19
|
|
|
20
20
|
2. **Write a reproducing test**: Based on the diagnosis, write a test that demonstrates the bug (it must fail)
|
|
21
|
-
- Commit: `draht-tools commit-docs "
|
|
21
|
+
- Commit: `draht-tools commit-docs "test: reproduce bug"`
|
|
22
22
|
|
|
23
23
|
3. **Minimal fix**: Write the smallest change that makes the test pass
|
|
24
24
|
- Do not refactor or add features — just fix the bug
|
|
25
25
|
- Run the full test suite to check for regressions
|
|
26
|
-
- Commit: `draht-tools commit-docs "
|
|
26
|
+
- Commit: `draht-tools commit-docs "fix: fix description"`
|
|
27
27
|
|
|
28
28
|
4. **Refactor** (if needed): Clean up without changing behavior
|
|
29
29
|
- Tests must stay green after every change
|
|
@@ -34,7 +34,11 @@ Phase: $1
|
|
|
34
34
|
- Instruction to output the plan as XML (you will save it via `draht-tools create-plan`)
|
|
35
35
|
|
|
36
36
|
6. Collect all plan outputs from subagents
|
|
37
|
-
7. Save
|
|
37
|
+
7. Save each plan by piping the subagent's output into `draht-tools create-plan`:
|
|
38
|
+
```
|
|
39
|
+
echo 'plan content from subagent' | draht-tools create-plan $1 P [title]
|
|
40
|
+
```
|
|
41
|
+
The content must contain real task details (files, actions, tests) — NOT placeholder brackets. If `create-plan` is called without stdin, it writes a useless template.
|
|
38
42
|
8. Validate: `draht-tools validate-plans $1`
|
|
39
43
|
9. Commit: `draht-tools commit-docs "create phase $1 plans"`
|
|
40
44
|
|
|
@@ -15,7 +15,11 @@ Task: $ARGUMENTS
|
|
|
15
15
|
|
|
16
16
|
## Steps
|
|
17
17
|
1. Run `draht-tools next-quick-number` to get task number
|
|
18
|
-
2.
|
|
18
|
+
2. Analyze the task and write a concrete plan with actual task details (files, actions, verification). Pipe it into `draht-tools create-quick-plan`:
|
|
19
|
+
```
|
|
20
|
+
echo 'plan content here' | draht-tools create-quick-plan NNN "$ARGUMENTS"
|
|
21
|
+
```
|
|
22
|
+
The plan content must include: a `# Quick Task NNN: title` heading, a `## Tasks` section with one or more `<task>` XML blocks containing real file paths, real actions, and real verification steps — NOT placeholders like `[files]`.
|
|
19
23
|
3. **Delegate execution to subagent**: Use the `subagent` tool in **single mode** with the `implementer` agent:
|
|
20
24
|
"Execute this task: $ARGUMENTS
|
|
21
25
|
|