gsd-pi 2.27.0 → 2.28.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 +16 -12
- package/dist/headless-query.d.ts +36 -0
- package/dist/headless-query.js +59 -0
- package/dist/headless.js +7 -4
- package/dist/help-text.js +2 -0
- package/dist/resources/extensions/gsd/auto.ts +11 -3
- package/dist/resources/extensions/gsd/commands.ts +55 -3
- package/dist/resources/extensions/gsd/crash-recovery.ts +5 -2
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +83 -0
- package/dist/resources/extensions/gsd/export.ts +90 -27
- package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/system.md +4 -1
- package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +42 -15
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +7 -2
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/multi-session.md +4 -13
- package/dist/resources/extensions/gsd/templates/preferences.md +34 -0
- package/dist/resources/extensions/gsd/tests/export-html-all.test.ts +105 -0
- package/dist/resources/extensions/gsd/tests/headless-query.test.ts +162 -0
- package/dist/resources/extensions/gsd/tests/update-command.test.ts +67 -0
- package/dist/resources/extensions/subagent/index.ts +1 -1
- package/dist/update-check.js +1 -1
- package/package.json +2 -1
- package/packages/pi-ai/dist/utils/oauth/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -0
- package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js +5 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js +4 -0
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js +6 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/index.js +2 -2
- package/packages/pi-ai/dist/utils/oauth/index.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/openai-codex.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/openai-codex.js +2 -0
- package/packages/pi-ai/dist/utils/oauth/openai-codex.js.map +1 -1
- package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -0
- package/packages/pi-ai/src/utils/oauth/github-copilot.ts +5 -1
- package/packages/pi-ai/src/utils/oauth/google-antigravity.ts +4 -0
- package/packages/pi-ai/src/utils/oauth/google-gemini-cli.ts +6 -0
- package/packages/pi-ai/src/utils/oauth/index.ts +2 -2
- package/packages/pi-ai/src/utils/oauth/openai-codex.ts +2 -0
- package/packages/pi-coding-agent/dist/core/bash-executor.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.js +23 -1
- package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/blob-store.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/blob-store.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/blob-store.js +50 -1
- package/packages/pi-coding-agent/dist/core/blob-store.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +32 -10
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +77 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/fs-utils.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/fs-utils.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/fs-utils.js +12 -0
- package/packages/pi-coding-agent/dist/core/fs-utils.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/fs-utils.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/fs-utils.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js +67 -0
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +22 -2
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +83 -12
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +2 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +3 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +6 -0
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +8 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +11 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/pi-coding-agent/src/core/bash-executor.ts +23 -1
- package/packages/pi-coding-agent/src/core/blob-store.ts +46 -1
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +84 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +31 -10
- package/packages/pi-coding-agent/src/core/fs-utils.test.ts +66 -0
- package/packages/pi-coding-agent/src/core/fs-utils.ts +12 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +22 -2
- package/packages/pi-coding-agent/src/core/session-manager.ts +84 -12
- package/packages/pi-coding-agent/src/index.ts +9 -0
- package/packages/pi-coding-agent/src/main.ts +7 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +9 -1
- package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +12 -0
- package/src/resources/extensions/gsd/auto.ts +11 -3
- package/src/resources/extensions/gsd/commands.ts +55 -3
- package/src/resources/extensions/gsd/crash-recovery.ts +5 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +83 -0
- package/src/resources/extensions/gsd/export.ts +90 -27
- package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/system.md +4 -1
- package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +42 -15
- package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +7 -2
- package/src/resources/extensions/gsd/skills/gsd-headless/references/multi-session.md +4 -13
- package/src/resources/extensions/gsd/templates/preferences.md +34 -0
- package/src/resources/extensions/gsd/tests/export-html-all.test.ts +105 -0
- package/src/resources/extensions/gsd/tests/headless-query.test.ts +162 -0
- package/src/resources/extensions/gsd/tests/update-command.test.ts +67 -0
- package/src/resources/extensions/subagent/index.ts +1 -1
- package/dist/resources/extensions/gsd/mcp-server.ts +0 -108
- package/dist/resources/extensions/gsd/tests/marketplace-discovery.test.ts +0 -202
- package/dist/resources/extensions/shared/bundled-extension-paths.ts +0 -11
- package/src/resources/extensions/gsd/mcp-server.ts +0 -108
- package/src/resources/extensions/gsd/tests/marketplace-discovery.test.ts +0 -202
- package/src/resources/extensions/shared/bundled-extension-paths.ts +0 -11
package/README.md
CHANGED
|
@@ -141,21 +141,23 @@ Auto mode is a state machine driven by files on disk. It reads `.gsd/STATE.md`,
|
|
|
141
141
|
|
|
142
142
|
3. **Git worktree isolation** — Each milestone runs in its own git worktree with a `milestone/<MID>` branch. All slice work commits sequentially — no branch switching, no merge conflicts. When the milestone completes, it's squash-merged to main as one clean commit.
|
|
143
143
|
|
|
144
|
-
4. **Crash recovery** — A lock file tracks the current unit. If the session dies, the next `/gsd auto` reads the surviving session file, synthesizes a recovery briefing from every tool call that made it to disk, and resumes with full context. Parallel orchestrator state is persisted to disk with PID liveness detection, so multi-worker sessions survive crashes too.
|
|
144
|
+
4. **Crash recovery** — A lock file tracks the current unit. If the session dies, the next `/gsd auto` reads the surviving session file, synthesizes a recovery briefing from every tool call that made it to disk, and resumes with full context. Parallel orchestrator state is persisted to disk with PID liveness detection, so multi-worker sessions survive crashes too. In headless mode, crashes trigger automatic restart with exponential backoff (default 3 attempts).
|
|
145
145
|
|
|
146
|
-
5. **
|
|
146
|
+
5. **Provider error recovery** — Transient provider errors (rate limits, 500/503 server errors, overloaded) auto-resume after a delay. Permanent errors (auth, billing) pause for manual review. The model fallback chain retries transient network errors before switching models.
|
|
147
147
|
|
|
148
|
-
6. **
|
|
148
|
+
6. **Stuck detection** — If the same unit dispatches twice (the LLM didn't produce the expected artifact), it retries once with a deep diagnostic. If it fails again, auto mode stops with the exact file it expected.
|
|
149
149
|
|
|
150
|
-
7. **
|
|
150
|
+
7. **Timeout supervision** — Soft timeout warns the LLM to wrap up. Idle watchdog detects stalls. Hard timeout pauses auto mode. Recovery steering nudges the LLM to finish durable output before giving up.
|
|
151
151
|
|
|
152
|
-
8. **
|
|
152
|
+
8. **Cost tracking** — Every unit's token usage and cost is captured, broken down by phase, slice, and model. The dashboard shows running totals and projections. Budget ceilings can pause auto mode before overspending.
|
|
153
153
|
|
|
154
|
-
9. **
|
|
154
|
+
9. **Adaptive replanning** — After each slice completes, the roadmap is reassessed. If the work revealed new information that changes the plan, slices are reordered, added, or removed before continuing.
|
|
155
155
|
|
|
156
|
-
10. **
|
|
156
|
+
10. **Verification enforcement** — Configure shell commands (`npm run lint`, `npm run test`, etc.) that run automatically after task execution. Failures trigger auto-fix retries before advancing. Configurable via `verification_commands`, `verification_auto_fix`, and `verification_max_retries` preferences.
|
|
157
157
|
|
|
158
|
-
11. **
|
|
158
|
+
11. **Milestone validation** — After all slices complete, a `validate-milestone` gate compares roadmap success criteria against actual results before sealing the milestone.
|
|
159
|
+
|
|
160
|
+
12. **Escape hatch** — Press Escape to pause. The conversation is preserved. Interact with the agent, inspect what happened, or just `/gsd auto` to resume from disk state.
|
|
159
161
|
|
|
160
162
|
### `/gsd` and `/gsd next` — Step Mode
|
|
161
163
|
|
|
@@ -247,14 +249,14 @@ gsd headless new-milestone --context spec.md --auto
|
|
|
247
249
|
# One unit at a time (cron-friendly)
|
|
248
250
|
gsd headless next
|
|
249
251
|
|
|
250
|
-
#
|
|
251
|
-
gsd headless
|
|
252
|
+
# Instant JSON snapshot (no LLM, ~50ms)
|
|
253
|
+
gsd headless query
|
|
252
254
|
|
|
253
255
|
# Force a specific pipeline phase
|
|
254
256
|
gsd headless dispatch plan
|
|
255
257
|
```
|
|
256
258
|
|
|
257
|
-
Headless auto-responds to interactive prompts, detects completion, and exits with structured codes: `0` complete, `1` error/timeout, `2` blocked. Auto-restarts on crash with exponential backoff. Pair with [remote questions](./docs/remote-questions.md) to route decisions to Slack or Discord when human input is needed.
|
|
259
|
+
Headless auto-responds to interactive prompts, detects completion, and exits with structured codes: `0` complete, `1` error/timeout, `2` blocked. Auto-restarts on crash with exponential backoff. Use `gsd headless query` for instant, machine-readable state inspection — returns phase, next dispatch preview, and parallel worker costs as a single JSON object without spawning an LLM session. Pair with [remote questions](./docs/remote-questions.md) to route decisions to Slack or Discord when human input is needed.
|
|
258
260
|
|
|
259
261
|
**Multi-session orchestration** — headless mode supports file-based IPC in `.gsd/parallel/` for coordinating multiple GSD workers across milestones. Build orchestrators that spawn, monitor, and budget-cap a fleet of GSD workers.
|
|
260
262
|
|
|
@@ -295,6 +297,7 @@ On first run, GSD launches a branded setup wizard that walks you through LLM pro
|
|
|
295
297
|
| `gsd config` | Re-run the setup wizard (LLM provider + tool keys) |
|
|
296
298
|
| `gsd update` | Update GSD to the latest version |
|
|
297
299
|
| `gsd headless [cmd]` | Run `/gsd` commands without TUI (CI, cron, scripts) |
|
|
300
|
+
| `gsd headless query` | Instant JSON snapshot — state, next dispatch, costs (no LLM) |
|
|
298
301
|
| `gsd --continue` (`-c`) | Resume the most recent session for the current directory |
|
|
299
302
|
| `gsd sessions` | Interactive session picker — browse and resume any saved session |
|
|
300
303
|
|
|
@@ -414,7 +417,8 @@ auto_report: true
|
|
|
414
417
|
| `skill_rules` | Situational rules for skill routing |
|
|
415
418
|
| `skill_staleness_days` | Skills unused for N days get deprioritized (default: 60, 0 = disabled) |
|
|
416
419
|
| `unique_milestone_ids` | Uses unique milestone names to avoid clashes when working in teams of people |
|
|
417
|
-
| `git.isolation` | `worktree` (default) or `none` — disable worktree isolation for projects that don't need it |
|
|
420
|
+
| `git.isolation` | `worktree` (default), `branch`, or `none` — disable worktree isolation for projects that don't need it |
|
|
421
|
+
| `git.manage_gitignore` | Set `false` to prevent GSD from modifying `.gitignore` |
|
|
418
422
|
| `verification_commands`| Array of shell commands to run after task execution (e.g., `["npm run lint", "npm run test"]`) |
|
|
419
423
|
| `verification_auto_fix`| Auto-retry on verification failures (default: true) |
|
|
420
424
|
| `verification_max_retries` | Max retries for verification failures (default: 2) |
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless Query — `gsd headless query`
|
|
3
|
+
*
|
|
4
|
+
* Single read-only command that returns the full project snapshot as JSON
|
|
5
|
+
* to stdout, without spawning an LLM session. Instant (~50ms).
|
|
6
|
+
*
|
|
7
|
+
* Output: { state, next, cost }
|
|
8
|
+
* state — deriveState() output (phase, milestones, progress, blockers)
|
|
9
|
+
* next — dry-run dispatch preview (what auto-mode would do next)
|
|
10
|
+
* cost — aggregated parallel worker costs
|
|
11
|
+
*/
|
|
12
|
+
import type { GSDState } from './resources/extensions/gsd/types.js';
|
|
13
|
+
export interface QuerySnapshot {
|
|
14
|
+
state: GSDState;
|
|
15
|
+
next: {
|
|
16
|
+
action: 'dispatch' | 'stop' | 'skip';
|
|
17
|
+
unitType?: string;
|
|
18
|
+
unitId?: string;
|
|
19
|
+
reason?: string;
|
|
20
|
+
};
|
|
21
|
+
cost: {
|
|
22
|
+
workers: Array<{
|
|
23
|
+
milestoneId: string;
|
|
24
|
+
pid: number;
|
|
25
|
+
state: string;
|
|
26
|
+
cost: number;
|
|
27
|
+
lastHeartbeat: number;
|
|
28
|
+
}>;
|
|
29
|
+
total: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export interface QueryResult {
|
|
33
|
+
exitCode: number;
|
|
34
|
+
data?: QuerySnapshot;
|
|
35
|
+
}
|
|
36
|
+
export declare function handleQuery(basePath: string): Promise<QueryResult>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Headless Query — `gsd headless query`
|
|
3
|
+
*
|
|
4
|
+
* Single read-only command that returns the full project snapshot as JSON
|
|
5
|
+
* to stdout, without spawning an LLM session. Instant (~50ms).
|
|
6
|
+
*
|
|
7
|
+
* Output: { state, next, cost }
|
|
8
|
+
* state — deriveState() output (phase, milestones, progress, blockers)
|
|
9
|
+
* next — dry-run dispatch preview (what auto-mode would do next)
|
|
10
|
+
* cost — aggregated parallel worker costs
|
|
11
|
+
*/
|
|
12
|
+
import { deriveState } from './resources/extensions/gsd/state.js';
|
|
13
|
+
import { resolveDispatch } from './resources/extensions/gsd/auto-dispatch.js';
|
|
14
|
+
import { readAllSessionStatuses } from './resources/extensions/gsd/session-status-io.js';
|
|
15
|
+
import { loadEffectiveGSDPreferences } from './resources/extensions/gsd/preferences.js';
|
|
16
|
+
// ─── Implementation ─────────────────────────────────────────────────────────
|
|
17
|
+
export async function handleQuery(basePath) {
|
|
18
|
+
const state = await deriveState(basePath);
|
|
19
|
+
// Derive next dispatch action
|
|
20
|
+
let next;
|
|
21
|
+
if (!state.activeMilestone) {
|
|
22
|
+
next = {
|
|
23
|
+
action: 'stop',
|
|
24
|
+
reason: state.phase === 'complete' ? 'All milestones complete.' : state.nextAction,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
const loaded = loadEffectiveGSDPreferences();
|
|
29
|
+
const dispatch = await resolveDispatch({
|
|
30
|
+
basePath,
|
|
31
|
+
mid: state.activeMilestone.id,
|
|
32
|
+
midTitle: state.activeMilestone.title,
|
|
33
|
+
state,
|
|
34
|
+
prefs: loaded?.preferences,
|
|
35
|
+
});
|
|
36
|
+
next = {
|
|
37
|
+
action: dispatch.action,
|
|
38
|
+
unitType: dispatch.action === 'dispatch' ? dispatch.unitType : undefined,
|
|
39
|
+
unitId: dispatch.action === 'dispatch' ? dispatch.unitId : undefined,
|
|
40
|
+
reason: dispatch.action === 'stop' ? dispatch.reason : undefined,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Aggregate parallel worker costs
|
|
44
|
+
const statuses = readAllSessionStatuses(basePath);
|
|
45
|
+
const workers = statuses.map((s) => ({
|
|
46
|
+
milestoneId: s.milestoneId,
|
|
47
|
+
pid: s.pid,
|
|
48
|
+
state: s.state,
|
|
49
|
+
cost: s.cost,
|
|
50
|
+
lastHeartbeat: s.lastHeartbeat,
|
|
51
|
+
}));
|
|
52
|
+
const snapshot = {
|
|
53
|
+
state,
|
|
54
|
+
next,
|
|
55
|
+
cost: { workers, total: workers.reduce((sum, w) => sum + w.cost, 0) },
|
|
56
|
+
};
|
|
57
|
+
process.stdout.write(JSON.stringify(snapshot) + '\n');
|
|
58
|
+
return { exitCode: 0, data: snapshot };
|
|
59
|
+
}
|
package/dist/headless.js
CHANGED
|
@@ -12,10 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
14
14
|
import { join, resolve } from 'node:path';
|
|
15
|
-
|
|
16
|
-
// This relative path resolves correctly from both src/ (via tsx) and dist/ (compiled).
|
|
17
|
-
import { RpcClient } from '../packages/pi-coding-agent/dist/modes/rpc/rpc-client.js';
|
|
18
|
-
import { attachJsonlLineReader, serializeJsonLine } from '../packages/pi-coding-agent/dist/modes/rpc/jsonl.js';
|
|
15
|
+
import { RpcClient, attachJsonlLineReader, serializeJsonLine } from '@gsd/pi-coding-agent';
|
|
19
16
|
// ---------------------------------------------------------------------------
|
|
20
17
|
// CLI Argument Parser
|
|
21
18
|
// ---------------------------------------------------------------------------
|
|
@@ -336,6 +333,12 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
336
333
|
process.stderr.write("[headless] Run 'gsd' interactively first to initialize a project.\n");
|
|
337
334
|
process.exit(1);
|
|
338
335
|
}
|
|
336
|
+
// Query: read-only state snapshot, no RPC child needed
|
|
337
|
+
if (options.command === 'query') {
|
|
338
|
+
const { handleQuery } = await import('./headless-query.js');
|
|
339
|
+
const result = await handleQuery(process.cwd());
|
|
340
|
+
return { exitCode: result.exitCode, interrupted: false };
|
|
341
|
+
}
|
|
339
342
|
// Resolve CLI path for the child process
|
|
340
343
|
const cliPath = process.env.GSD_BIN_PATH || process.argv[1];
|
|
341
344
|
if (!cliPath) {
|
package/dist/help-text.js
CHANGED
|
@@ -46,6 +46,7 @@ const SUBCOMMAND_HELP = {
|
|
|
46
46
|
' next Run one unit',
|
|
47
47
|
' status Show progress dashboard',
|
|
48
48
|
' new-milestone Create a milestone from a specification document',
|
|
49
|
+
' query JSON snapshot: state + next dispatch + costs (no LLM)',
|
|
49
50
|
'',
|
|
50
51
|
'new-milestone flags:',
|
|
51
52
|
' --context <path> Path to spec/PRD file (use \'-\' for stdin)',
|
|
@@ -62,6 +63,7 @@ const SUBCOMMAND_HELP = {
|
|
|
62
63
|
' cat spec.md | gsd headless new-milestone --context - From stdin',
|
|
63
64
|
' gsd headless new-milestone --context spec.md --auto Create + auto-execute',
|
|
64
65
|
' gsd headless --supervised auto Supervised orchestrator mode',
|
|
66
|
+
' gsd headless query Instant JSON state snapshot',
|
|
65
67
|
'',
|
|
66
68
|
'Exit codes: 0 = complete, 1 = error/timeout, 2 = blocked',
|
|
67
69
|
].join('\n'),
|
|
@@ -103,7 +103,7 @@ import {
|
|
|
103
103
|
import { computeBudgets, resolveExecutorContextWindow } from "./context-budget.js";
|
|
104
104
|
import { join } from "node:path";
|
|
105
105
|
import { sep as pathSep } from "node:path";
|
|
106
|
-
import { readdirSync, readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync, statSync } from "node:fs";
|
|
106
|
+
import { readdirSync, readFileSync, existsSync, mkdirSync, writeFileSync, renameSync, unlinkSync, statSync } from "node:fs";
|
|
107
107
|
import { nativeIsRepo, nativeInit, nativeAddPaths, nativeCommit } from "./native-git-bridge.js";
|
|
108
108
|
import {
|
|
109
109
|
autoCommitCurrentBranch,
|
|
@@ -2138,7 +2138,11 @@ async function dispatchNextUnit(
|
|
|
2138
2138
|
// Clear completed-units.json for the finished milestone
|
|
2139
2139
|
try {
|
|
2140
2140
|
const file = completedKeysPath(s.basePath);
|
|
2141
|
-
if (existsSync(file))
|
|
2141
|
+
if (existsSync(file)) {
|
|
2142
|
+
const tmpFile = file + ".tmp";
|
|
2143
|
+
writeFileSync(tmpFile, JSON.stringify([]), "utf-8");
|
|
2144
|
+
renameSync(tmpFile, file);
|
|
2145
|
+
}
|
|
2142
2146
|
s.completedKeySet.clear();
|
|
2143
2147
|
} catch { /* non-fatal */ }
|
|
2144
2148
|
|
|
@@ -2286,7 +2290,11 @@ async function dispatchNextUnit(
|
|
|
2286
2290
|
// Clear completed-units.json for the finished milestone so it doesn't grow unbounded.
|
|
2287
2291
|
try {
|
|
2288
2292
|
const file = completedKeysPath(s.basePath);
|
|
2289
|
-
if (existsSync(file))
|
|
2293
|
+
if (existsSync(file)) {
|
|
2294
|
+
const tmpFile = file + ".tmp";
|
|
2295
|
+
writeFileSync(tmpFile, JSON.stringify([]), "utf-8");
|
|
2296
|
+
renameSync(tmpFile, file);
|
|
2297
|
+
}
|
|
2290
2298
|
s.completedKeySet.clear();
|
|
2291
2299
|
} catch { /* non-fatal */ }
|
|
2292
2300
|
// ── Milestone merge: squash-merge milestone branch to main before stopping ──
|
|
@@ -77,7 +77,7 @@ function projectRoot(): string {
|
|
|
77
77
|
|
|
78
78
|
export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
79
79
|
pi.registerCommand("gsd", {
|
|
80
|
-
description: "GSD — Get Shit Done: /gsd help|next|auto|stop|pause|status|visualize|queue|quick|capture|triage|dispatch|history|undo|skip|export|cleanup|mode|prefs|config|hooks|run-hook|skill-health|doctor|forensics|migrate|remote|steer|knowledge|new-milestone|parallel",
|
|
80
|
+
description: "GSD — Get Shit Done: /gsd help|next|auto|stop|pause|status|visualize|queue|quick|capture|triage|dispatch|history|undo|skip|export|cleanup|mode|prefs|config|hooks|run-hook|skill-health|doctor|forensics|migrate|remote|steer|knowledge|new-milestone|parallel|update",
|
|
81
81
|
getArgumentCompletions: (prefix: string) => {
|
|
82
82
|
const subcommands = [
|
|
83
83
|
{ cmd: "help", desc: "Categorized command reference with descriptions" },
|
|
@@ -113,6 +113,7 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
113
113
|
{ cmd: "knowledge", desc: "Add persistent project knowledge (rule, pattern, or lesson)" },
|
|
114
114
|
{ cmd: "new-milestone", desc: "Create a milestone from a specification document (headless)" },
|
|
115
115
|
{ cmd: "parallel", desc: "Parallel milestone orchestration (start, status, stop, merge)" },
|
|
116
|
+
{ cmd: "update", desc: "Update GSD to the latest version" },
|
|
116
117
|
];
|
|
117
118
|
const parts = prefix.trim().split(/\s+/);
|
|
118
119
|
|
|
@@ -181,7 +182,7 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
181
182
|
|
|
182
183
|
if (parts[0] === "export" && parts.length <= 2) {
|
|
183
184
|
const flagPrefix = parts[1] ?? "";
|
|
184
|
-
return ["--json", "--markdown"]
|
|
185
|
+
return ["--json", "--markdown", "--html", "--html --all"]
|
|
185
186
|
.filter((f) => f.startsWith(flagPrefix))
|
|
186
187
|
.map((f) => ({ value: `export ${f}`, label: f }));
|
|
187
188
|
}
|
|
@@ -575,6 +576,11 @@ Examples:
|
|
|
575
576
|
return;
|
|
576
577
|
}
|
|
577
578
|
|
|
579
|
+
if (trimmed === "update") {
|
|
580
|
+
await handleUpdate(ctx);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
|
|
578
584
|
if (trimmed === "") {
|
|
579
585
|
// Bare /gsd defaults to step mode
|
|
580
586
|
await startAuto(ctx, pi, projectRoot(), false, { step: true });
|
|
@@ -625,11 +631,12 @@ function showHelp(ctx: ExtensionCommandContext): void {
|
|
|
625
631
|
"",
|
|
626
632
|
"MAINTENANCE",
|
|
627
633
|
" /gsd doctor Diagnose and repair .gsd/ state [audit|fix|heal] [scope]",
|
|
628
|
-
" /gsd export Export milestone/slice results [--json|--markdown|--html]",
|
|
634
|
+
" /gsd export Export milestone/slice results [--json|--markdown|--html] [--all]",
|
|
629
635
|
" /gsd cleanup Remove merged branches or snapshots [branches|snapshots]",
|
|
630
636
|
" /gsd migrate Upgrade .gsd/ structures to new format",
|
|
631
637
|
" /gsd remote Control remote auto-mode [slack|discord|status|disconnect]",
|
|
632
638
|
" /gsd inspect Show SQLite DB diagnostics (schema, row counts, recent entries)",
|
|
639
|
+
" /gsd update Update GSD to the latest version via npm",
|
|
633
640
|
];
|
|
634
641
|
ctx.ui.notify(lines.join("\n"), "info");
|
|
635
642
|
}
|
|
@@ -2091,3 +2098,48 @@ Examples:
|
|
|
2091
2098
|
ctx.ui.notify("Failed to dispatch hook. Auto-mode may have been cancelled.", "error");
|
|
2092
2099
|
}
|
|
2093
2100
|
}
|
|
2101
|
+
|
|
2102
|
+
// ─── Self-update handler ────────────────────────────────────────────────────
|
|
2103
|
+
|
|
2104
|
+
async function handleUpdate(ctx: ExtensionCommandContext): Promise<void> {
|
|
2105
|
+
const { execSync } = await import("node:child_process");
|
|
2106
|
+
const { compareSemver } = await import("../../../update-check.js");
|
|
2107
|
+
|
|
2108
|
+
const NPM_PACKAGE = "gsd-pi";
|
|
2109
|
+
const current = process.env.GSD_VERSION || "0.0.0";
|
|
2110
|
+
|
|
2111
|
+
ctx.ui.notify(`Current version: v${current}\nChecking npm registry...`, "info");
|
|
2112
|
+
|
|
2113
|
+
let latest: string;
|
|
2114
|
+
try {
|
|
2115
|
+
latest = execSync(`npm view ${NPM_PACKAGE} version`, {
|
|
2116
|
+
encoding: "utf-8",
|
|
2117
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
2118
|
+
}).trim();
|
|
2119
|
+
} catch {
|
|
2120
|
+
ctx.ui.notify("Failed to reach npm registry. Check your network connection.", "error");
|
|
2121
|
+
return;
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
if (compareSemver(latest, current) <= 0) {
|
|
2125
|
+
ctx.ui.notify(`Already up to date (v${current}).`, "info");
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
ctx.ui.notify(`Updating: v${current} → v${latest}...`, "info");
|
|
2130
|
+
|
|
2131
|
+
try {
|
|
2132
|
+
execSync(`npm install -g ${NPM_PACKAGE}@latest`, {
|
|
2133
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
2134
|
+
});
|
|
2135
|
+
ctx.ui.notify(
|
|
2136
|
+
`Updated to v${latest}. Restart your GSD session to use the new version.`,
|
|
2137
|
+
"info",
|
|
2138
|
+
);
|
|
2139
|
+
} catch {
|
|
2140
|
+
ctx.ui.notify(
|
|
2141
|
+
`Update failed. Try manually: npm install -g ${NPM_PACKAGE}@latest`,
|
|
2142
|
+
"error",
|
|
2143
|
+
);
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* so the file on disk reflects every tool call up to the crash point).
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { writeFileSync, readFileSync, unlinkSync, existsSync } from "node:fs";
|
|
13
|
+
import { renameSync, writeFileSync, readFileSync, unlinkSync, existsSync } from "node:fs";
|
|
14
14
|
import { join } from "node:path";
|
|
15
15
|
import { gsdRoot } from "./paths.js";
|
|
16
16
|
|
|
@@ -49,7 +49,10 @@ export function writeLock(
|
|
|
49
49
|
completedUnits,
|
|
50
50
|
sessionFile,
|
|
51
51
|
};
|
|
52
|
-
|
|
52
|
+
const lp = lockPath(basePath);
|
|
53
|
+
const tmpLp = lp + ".tmp";
|
|
54
|
+
writeFileSync(tmpLp, JSON.stringify(data, null, 2), "utf-8");
|
|
55
|
+
renameSync(tmpLp, lp);
|
|
53
56
|
} catch (e) { /* non-fatal: lock write failure */ void e; }
|
|
54
57
|
}
|
|
55
58
|
|
|
@@ -104,6 +104,8 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|
|
104
104
|
- Object with provider: `{ model: "claude-opus-4-6", provider: "bedrock" }` — explicit provider targeting in object format
|
|
105
105
|
- Omit a key to use whatever model is currently active. Fallbacks are tried when model switching fails (provider unavailable, rate limited, etc.).
|
|
106
106
|
|
|
107
|
+
- `skill_staleness_days`: number — skills unused for this many days get deprioritized during discovery. Set to `0` to disable staleness tracking. Default: `60`.
|
|
108
|
+
|
|
107
109
|
- `skill_discovery`: controls how GSD discovers and applies skills during auto-mode. Valid values:
|
|
108
110
|
- `auto` — skills are found and applied automatically without prompting.
|
|
109
111
|
- `suggest` — (default) skills are identified during research but not installed automatically.
|
|
@@ -126,6 +128,7 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|
|
126
128
|
- `merge_strategy`: `"squash"` or `"merge"` — controls how worktree branches are merged back. `"squash"` combines all commits into one; `"merge"` preserves individual commits. Default: `"squash"`.
|
|
127
129
|
- `isolation`: `"worktree"`, `"branch"`, or `"none"` — controls auto-mode git isolation strategy. `"worktree"` creates a milestone worktree for isolated work; `"branch"` works directly in the project root but creates a milestone branch (useful for submodule-heavy repos); `"none"` works directly on the current branch with no worktree or milestone branch (ideal for step-mode with hot reloads). Default: `"worktree"`.
|
|
128
130
|
- `commit_docs`: boolean — when `false`, prevents GSD from committing `.gsd/` planning artifacts to git. The `.gsd/` folder is added to `.gitignore` and kept local-only. Useful for teams where only some members use GSD, or when company policy requires a clean repository. Default: `true`.
|
|
131
|
+
- `manage_gitignore`: boolean — when `false`, GSD will not touch `.gitignore` at all. Useful when your project has a strictly managed `.gitignore` and you don't want GSD adding entries. Default: `true`.
|
|
129
132
|
- `worktree_post_create`: string — script to run after a worktree is created (both auto-mode and manual `/worktree`). Receives `SOURCE_DIR` and `WORKTREE_DIR` as environment variables. Can be absolute or relative to project root. Runs with 30-second timeout. Failure is non-fatal (logged as warning). Default: none.
|
|
130
133
|
|
|
131
134
|
- `unique_milestone_ids`: boolean — when `true`, generates milestone IDs in `M{seq}-{rand6}` format (e.g. `M001-eh88as`) instead of plain sequential `M001`. Prevents ID collisions in team workflows where multiple contributors create milestones concurrently. Both formats coexist — existing `M001`-style milestones remain valid. Default: `false`.
|
|
@@ -161,6 +164,31 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|
|
161
164
|
- `on_milestone`: boolean — notify when a milestone finishes. Default: `true`.
|
|
162
165
|
- `on_attention`: boolean — notify when manual attention is needed. Default: `true`.
|
|
163
166
|
|
|
167
|
+
- `dynamic_routing`: configures the dynamic model router that adjusts model selection based on task complexity. Keys:
|
|
168
|
+
- `enabled`: boolean — enable dynamic routing. Default: `false`.
|
|
169
|
+
- `tier_models`: object — model overrides per complexity tier. Keys: `light`, `standard`, `heavy`. Values are model ID strings.
|
|
170
|
+
- `escalate_on_failure`: boolean — escalate to a higher-tier model when the current one fails. Default: `true`.
|
|
171
|
+
- `budget_pressure`: boolean — downgrade model tier when budget is under pressure. Default: `true`.
|
|
172
|
+
- `cross_provider`: boolean — allow routing across different providers. Default: `true`.
|
|
173
|
+
- `hooks`: boolean — enable routing hooks. Default: `true`.
|
|
174
|
+
|
|
175
|
+
- `auto_visualize`: boolean — show a visualizer hint after each milestone completion in auto-mode. Default: `false`.
|
|
176
|
+
|
|
177
|
+
- `auto_report`: boolean — generate an HTML report snapshot after each milestone completion. Default: `true`.
|
|
178
|
+
|
|
179
|
+
- `parallel`: configures parallel orchestration for running multiple slices concurrently. Keys:
|
|
180
|
+
- `enabled`: boolean — enable parallel execution. Default: `false`.
|
|
181
|
+
- `max_workers`: number — maximum concurrent workers (1-4). Default: `2`.
|
|
182
|
+
- `budget_ceiling`: number — optional per-parallel-run budget ceiling.
|
|
183
|
+
- `merge_strategy`: `"per-slice"` or `"per-milestone"` — when to merge worktree results back. Default: `"per-milestone"`.
|
|
184
|
+
- `auto_merge`: `"auto"`, `"confirm"`, or `"manual"` — merge behavior after completion. `"auto"` merges immediately; `"confirm"` asks first; `"manual"` leaves branches for you. Default: `"confirm"`.
|
|
185
|
+
|
|
186
|
+
- `verification_commands`: string[] — shell commands to run as verification after task execution (e.g., `["npm test", "npm run lint"]`). Commands run in order; if any fails, the task is marked as needing fixes.
|
|
187
|
+
|
|
188
|
+
- `verification_auto_fix`: boolean — when `true`, automatically attempt to fix verification failures instead of just reporting them. Default: `false`.
|
|
189
|
+
|
|
190
|
+
- `verification_max_retries`: number — maximum number of fix-and-retry cycles for verification failures. Default: `0` (no retries).
|
|
191
|
+
|
|
164
192
|
- `uat_dispatch`: boolean — when `true`, enables UAT (User Acceptance Testing) dispatch mode. Default: `false`.
|
|
165
193
|
|
|
166
194
|
- `post_unit_hooks`: array — hooks that fire after a unit completes. Each entry has:
|
|
@@ -531,3 +559,58 @@ remote_questions:
|
|
|
531
559
|
```
|
|
532
560
|
|
|
533
561
|
Routes interactive questions to a Slack channel for headless auto-mode sessions. Questions time out after 15 minutes if unanswered.
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Dynamic Routing Example
|
|
566
|
+
|
|
567
|
+
```yaml
|
|
568
|
+
---
|
|
569
|
+
version: 1
|
|
570
|
+
dynamic_routing:
|
|
571
|
+
enabled: true
|
|
572
|
+
tier_models:
|
|
573
|
+
light: openrouter/minimax/minimax-m2.5
|
|
574
|
+
standard: claude-sonnet-4-6
|
|
575
|
+
heavy: claude-opus-4-6
|
|
576
|
+
escalate_on_failure: true
|
|
577
|
+
budget_pressure: true
|
|
578
|
+
---
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
Automatically selects model tier based on task complexity. Simple tasks use the `light` model, complex tasks escalate to `heavy`. Under budget pressure, tasks are routed to cheaper tiers.
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## Parallel Execution Example
|
|
586
|
+
|
|
587
|
+
```yaml
|
|
588
|
+
---
|
|
589
|
+
version: 1
|
|
590
|
+
parallel:
|
|
591
|
+
enabled: true
|
|
592
|
+
max_workers: 3
|
|
593
|
+
merge_strategy: per-milestone
|
|
594
|
+
auto_merge: confirm
|
|
595
|
+
---
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
Runs up to 3 slices concurrently in separate worktrees. Results are merged per-milestone with user confirmation.
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
## Verification Example
|
|
603
|
+
|
|
604
|
+
```yaml
|
|
605
|
+
---
|
|
606
|
+
version: 1
|
|
607
|
+
verification_commands:
|
|
608
|
+
- npm test
|
|
609
|
+
- npm run lint
|
|
610
|
+
- npm run typecheck
|
|
611
|
+
verification_auto_fix: true
|
|
612
|
+
verification_max_retries: 2
|
|
613
|
+
---
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
Runs test, lint, and typecheck after each task. On failure, auto-fix is attempted up to 2 times before reporting the issue.
|
|
@@ -98,43 +98,106 @@ export function writeExportFile(
|
|
|
98
98
|
export async function handleExport(args: string, ctx: ExtensionCommandContext, basePath: string): Promise<void> {
|
|
99
99
|
// HTML report — delegates to the full visualizer-data pipeline
|
|
100
100
|
if (args.includes("--html")) {
|
|
101
|
+
const generateAll = args.includes("--all");
|
|
101
102
|
try {
|
|
102
103
|
const { loadVisualizerData } = await import("./visualizer-data.js");
|
|
103
104
|
const { generateHtmlReport } = await import("./export-html.js");
|
|
104
|
-
const { writeReportSnapshot,
|
|
105
|
+
const { writeReportSnapshot, loadReportsIndex } = await import("./reports.js");
|
|
105
106
|
const { basename: bn } = await import("node:path");
|
|
106
107
|
const data = await loadVisualizerData(basePath);
|
|
107
108
|
const projName = basename(basePath);
|
|
108
109
|
const gsdVersion = process.env.GSD_VERSION ?? "0.0.0";
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
basePath,
|
|
113
|
-
html: generateHtmlReport(data, {
|
|
114
|
-
projectName: projName,
|
|
115
|
-
projectPath: basePath,
|
|
116
|
-
gsdVersion,
|
|
117
|
-
indexRelPath: "index.html",
|
|
118
|
-
}),
|
|
119
|
-
milestoneId: data.milestones.find(m => m.status === "active")?.id ?? "manual",
|
|
120
|
-
milestoneTitle: data.milestones.find(m => m.status === "active")?.title ?? "",
|
|
121
|
-
kind: "manual",
|
|
110
|
+
const doneMilestones = data.milestones.filter(m => m.status === "complete").length;
|
|
111
|
+
|
|
112
|
+
const htmlOpts = {
|
|
122
113
|
projectName: projName,
|
|
123
114
|
projectPath: basePath,
|
|
124
115
|
gsdVersion,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
116
|
+
indexRelPath: "index.html",
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
if (generateAll) {
|
|
120
|
+
// Generate a report snapshot for every milestone
|
|
121
|
+
const existing = loadReportsIndex(basePath);
|
|
122
|
+
const existingIds = new Set(existing?.entries.map(e => e.milestoneId) ?? []);
|
|
123
|
+
|
|
124
|
+
const targets = data.milestones.filter(m => !existingIds.has(m.id));
|
|
125
|
+
if (targets.length === 0) {
|
|
126
|
+
ctx.ui.notify(
|
|
127
|
+
"All milestones already have report snapshots. Run without --all to create a new snapshot for the active milestone.",
|
|
128
|
+
"info",
|
|
129
|
+
);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const html = generateHtmlReport(data, htmlOpts);
|
|
134
|
+
const paths: string[] = [];
|
|
135
|
+
|
|
136
|
+
for (const ms of targets) {
|
|
137
|
+
const msSlicesDone = ms.slices.filter(sl => sl.done).length;
|
|
138
|
+
const msSlicesTotal = ms.slices.length;
|
|
139
|
+
|
|
140
|
+
// Accumulate project-wide progress up to and including this milestone
|
|
141
|
+
const msIdx = data.milestones.indexOf(ms);
|
|
142
|
+
let cumulativeDone = 0;
|
|
143
|
+
let cumulativeTotal = 0;
|
|
144
|
+
for (let i = 0; i <= msIdx; i++) {
|
|
145
|
+
cumulativeDone += data.milestones[i].slices.filter(sl => sl.done).length;
|
|
146
|
+
cumulativeTotal += data.milestones[i].slices.length;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const outPath = writeReportSnapshot({
|
|
150
|
+
basePath,
|
|
151
|
+
html,
|
|
152
|
+
milestoneId: ms.id,
|
|
153
|
+
milestoneTitle: ms.title,
|
|
154
|
+
kind: ms.status === "complete" ? "milestone" : "manual",
|
|
155
|
+
projectName: projName,
|
|
156
|
+
projectPath: basePath,
|
|
157
|
+
gsdVersion,
|
|
158
|
+
totalCost: data.totals?.cost ?? 0,
|
|
159
|
+
totalTokens: data.totals?.tokens.total ?? 0,
|
|
160
|
+
totalDuration: data.totals?.duration ?? 0,
|
|
161
|
+
doneSlices: cumulativeDone,
|
|
162
|
+
totalSlices: cumulativeTotal,
|
|
163
|
+
doneMilestones: data.milestones.slice(0, msIdx + 1).filter(m => m.status === "complete").length,
|
|
164
|
+
totalMilestones: data.milestones.length,
|
|
165
|
+
phase: ms.status === "complete" ? "complete" : data.phase,
|
|
166
|
+
});
|
|
167
|
+
paths.push(bn(outPath));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
ctx.ui.notify(
|
|
171
|
+
`Generated ${paths.length} report snapshot${paths.length !== 1 ? "s" : ""}:\n${paths.map(p => ` ${p}`).join("\n")}\nBrowse all reports: .gsd/reports/index.html`,
|
|
172
|
+
"success",
|
|
173
|
+
);
|
|
174
|
+
} else {
|
|
175
|
+
// Single report for the active milestone (existing behavior)
|
|
176
|
+
const doneSlices = data.milestones.reduce((s, m) => s + m.slices.filter(sl => sl.done).length, 0);
|
|
177
|
+
const totalSlices = data.milestones.reduce((s, m) => s + m.slices.length, 0);
|
|
178
|
+
const outPath = writeReportSnapshot({
|
|
179
|
+
basePath,
|
|
180
|
+
html: generateHtmlReport(data, htmlOpts),
|
|
181
|
+
milestoneId: data.milestones.find(m => m.status === "active")?.id ?? "manual",
|
|
182
|
+
milestoneTitle: data.milestones.find(m => m.status === "active")?.title ?? "",
|
|
183
|
+
kind: "manual",
|
|
184
|
+
projectName: projName,
|
|
185
|
+
projectPath: basePath,
|
|
186
|
+
gsdVersion,
|
|
187
|
+
totalCost: data.totals?.cost ?? 0,
|
|
188
|
+
totalTokens: data.totals?.tokens.total ?? 0,
|
|
189
|
+
totalDuration: data.totals?.duration ?? 0,
|
|
190
|
+
doneSlices,
|
|
191
|
+
totalSlices,
|
|
192
|
+
doneMilestones,
|
|
193
|
+
totalMilestones: data.milestones.length,
|
|
194
|
+
phase: data.phase,
|
|
195
|
+
});
|
|
196
|
+
ctx.ui.notify(
|
|
197
|
+
`HTML report saved: .gsd/reports/${bn(outPath)}\nBrowse all reports: .gsd/reports/index.html`,
|
|
198
|
+
"success",
|
|
199
|
+
);
|
|
200
|
+
}
|
|
138
201
|
} catch (err) {
|
|
139
202
|
ctx.ui.notify(
|
|
140
203
|
`HTML export failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
@@ -25,7 +25,7 @@ A researcher explored the codebase and a planner decomposed the work — you are
|
|
|
25
25
|
{{priorTaskLines}}
|
|
26
26
|
|
|
27
27
|
Then:
|
|
28
|
-
0. Narrate step transitions, key implementation decisions, and verification outcomes as you work. Keep it terse — one line between tool-call clusters, not between every call.
|
|
28
|
+
0. Narrate step transitions, key implementation decisions, and verification outcomes as you work. Keep it terse — one line between tool-call clusters, not between every call — but write complete sentences in user-facing prose, not shorthand notes or scratchpad fragments.
|
|
29
29
|
1. **Load relevant skills before writing code.** Check the `GSD Skill Preferences` block in system context and the `<available_skills>` catalog in your system prompt. For each skill that matches this task's technology stack (e.g., React, Next.js, accessibility, component design), `read` its SKILL.md file now. Skills contain implementation rules and patterns that should guide your code. If no skills match this task, skip this step.
|
|
30
30
|
2. Execute the steps in the inlined task plan
|
|
31
31
|
3. Build the real thing. If the task plan says "create login endpoint", build an endpoint that actually authenticates against a real store, not one that returns a hardcoded success response. If the task plan says "create dashboard page", build a page that renders real data from the API, not a component with hardcoded props. Stubs and mocks are for tests, not for the shipped feature.
|
|
@@ -16,7 +16,7 @@ A **researcher agent** already explored the codebase and documented findings in
|
|
|
16
16
|
|
|
17
17
|
After you finish, each slice goes through its own research → plan → execute cycle. Slice researchers dive deeper into the specific area. Slice planners decompose into tasks. Executors build each task. Your roadmap sets the strategic frame for all of them.
|
|
18
18
|
|
|
19
|
-
Narrate your decomposition reasoning — why you're grouping work this way, what risks are driving the order, what verification strategy you're choosing and why.
|
|
19
|
+
Narrate your decomposition reasoning — why you're grouping work this way, what risks are driving the order, what verification strategy you're choosing and why. Use complete sentences rather than planner shorthand or fragmentary notes.
|
|
20
20
|
|
|
21
21
|
Then:
|
|
22
22
|
1. Use the **Roadmap** output template from the inlined context above
|