gsd-pi 2.19.0 → 2.20.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 +5 -1
- package/dist/cli.js +3 -3
- package/dist/onboarding.d.ts +3 -1
- package/dist/onboarding.js +77 -3
- package/dist/remote-questions-config.d.ts +1 -1
- package/dist/resources/extensions/google-search/index.ts +164 -47
- package/dist/resources/extensions/gsd/auto-prompts.ts +103 -24
- package/dist/resources/extensions/gsd/auto-worktree.ts +93 -9
- package/dist/resources/extensions/gsd/auto.ts +424 -30
- package/dist/resources/extensions/gsd/commands.ts +518 -36
- package/dist/resources/extensions/gsd/context-budget.ts +243 -0
- package/dist/resources/extensions/gsd/context-store.ts +195 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +41 -3
- package/dist/resources/extensions/gsd/db-writer.ts +341 -0
- package/dist/resources/extensions/gsd/debug-logger.ts +178 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +0 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +54 -0
- package/dist/resources/extensions/gsd/doctor-proactive.ts +286 -0
- package/dist/resources/extensions/gsd/doctor.ts +283 -2
- package/dist/resources/extensions/gsd/export.ts +81 -2
- package/dist/resources/extensions/gsd/files.ts +39 -9
- package/dist/resources/extensions/gsd/git-service.ts +6 -0
- package/dist/resources/extensions/gsd/gsd-db.ts +752 -0
- package/dist/resources/extensions/gsd/guided-flow.ts +26 -1
- package/dist/resources/extensions/gsd/history.ts +0 -1
- package/dist/resources/extensions/gsd/index.ts +277 -1
- package/dist/resources/extensions/gsd/md-importer.ts +526 -0
- package/dist/resources/extensions/gsd/metrics.ts +39 -3
- package/dist/resources/extensions/gsd/notifications.ts +0 -1
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +70 -1
- package/dist/resources/extensions/gsd/preferences.ts +125 -150
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -5
- package/dist/resources/extensions/gsd/prompts/heal-skill.md +45 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +48 -0
- package/dist/resources/extensions/gsd/prompts/system.md +2 -1
- package/dist/resources/extensions/gsd/quick.ts +156 -0
- package/dist/resources/extensions/gsd/skill-discovery.ts +5 -3
- package/dist/resources/extensions/gsd/skill-health.ts +417 -0
- package/dist/resources/extensions/gsd/skill-telemetry.ts +127 -0
- package/dist/resources/extensions/gsd/state.ts +30 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +1 -0
- package/dist/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/context-store.test.ts +462 -0
- package/dist/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
- package/dist/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
- package/dist/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
- package/dist/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
- package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
- package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
- package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
- package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
- package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
- package/dist/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
- package/dist/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
- package/dist/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
- package/dist/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
- package/dist/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
- package/dist/resources/extensions/gsd/tests/metrics.test.ts +197 -0
- package/dist/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/parsers.test.ts +40 -0
- package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
- package/dist/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
- package/dist/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
- package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +262 -1
- package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
- package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
- package/dist/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
- package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
- package/dist/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
- package/dist/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
- package/dist/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +92 -0
- package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +228 -5
- package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/dist/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
- package/dist/resources/extensions/gsd/types.ts +29 -0
- package/dist/resources/extensions/gsd/undo.ts +0 -1
- package/dist/resources/extensions/gsd/unit-runtime.ts +5 -1
- package/dist/resources/extensions/gsd/visualizer-data.ts +352 -1
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +166 -22
- package/dist/resources/extensions/gsd/visualizer-views.ts +464 -2
- package/dist/resources/extensions/gsd/worktree-command.ts +18 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +11 -4
- package/dist/resources/extensions/remote-questions/config.ts +4 -2
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +2 -4
- package/dist/resources/extensions/remote-questions/format.ts +154 -8
- package/dist/resources/extensions/remote-questions/manager.ts +9 -7
- package/dist/resources/extensions/remote-questions/remote-command.ts +100 -4
- package/dist/resources/extensions/remote-questions/slack-adapter.ts +58 -2
- package/dist/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
- package/dist/resources/extensions/remote-questions/types.ts +2 -1
- package/dist/resources/extensions/ttsr/ttsr-manager.ts +26 -0
- package/dist/resources/extensions/voice/index.ts +4 -3
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +12 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +6 -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 +25 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.js +106 -3
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp.md +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.js +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.js +45 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +43 -11
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +7 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +13 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +26 -0
- package/packages/pi-coding-agent/src/core/lsp/index.ts +157 -2
- package/packages/pi-coding-agent/src/core/lsp/lsp.md +6 -0
- package/packages/pi-coding-agent/src/core/lsp/types.ts +53 -0
- package/packages/pi-coding-agent/src/core/lsp/utils.ts +56 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +41 -11
- package/packages/pi-coding-agent/src/core/system-prompt.ts +7 -1
- package/packages/pi-coding-agent/src/core/tools/edit.ts +3 -0
- package/packages/pi-coding-agent/src/core/tools/write.ts +3 -0
- package/src/resources/extensions/google-search/index.ts +164 -47
- package/src/resources/extensions/gsd/auto-prompts.ts +103 -24
- package/src/resources/extensions/gsd/auto-worktree.ts +93 -9
- package/src/resources/extensions/gsd/auto.ts +424 -30
- package/src/resources/extensions/gsd/commands.ts +518 -36
- package/src/resources/extensions/gsd/context-budget.ts +243 -0
- package/src/resources/extensions/gsd/context-store.ts +195 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +41 -3
- package/src/resources/extensions/gsd/db-writer.ts +341 -0
- package/src/resources/extensions/gsd/debug-logger.ts +178 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +0 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +54 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +286 -0
- package/src/resources/extensions/gsd/doctor.ts +283 -2
- package/src/resources/extensions/gsd/export.ts +81 -2
- package/src/resources/extensions/gsd/files.ts +39 -9
- package/src/resources/extensions/gsd/git-service.ts +6 -0
- package/src/resources/extensions/gsd/gsd-db.ts +752 -0
- package/src/resources/extensions/gsd/guided-flow.ts +26 -1
- package/src/resources/extensions/gsd/history.ts +0 -1
- package/src/resources/extensions/gsd/index.ts +277 -1
- package/src/resources/extensions/gsd/md-importer.ts +526 -0
- package/src/resources/extensions/gsd/metrics.ts +39 -3
- package/src/resources/extensions/gsd/notifications.ts +0 -1
- package/src/resources/extensions/gsd/post-unit-hooks.ts +70 -1
- package/src/resources/extensions/gsd/preferences.ts +125 -150
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -5
- package/src/resources/extensions/gsd/prompts/heal-skill.md +45 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +48 -0
- package/src/resources/extensions/gsd/prompts/system.md +2 -1
- package/src/resources/extensions/gsd/quick.ts +156 -0
- package/src/resources/extensions/gsd/skill-discovery.ts +5 -3
- package/src/resources/extensions/gsd/skill-health.ts +417 -0
- package/src/resources/extensions/gsd/skill-telemetry.ts +127 -0
- package/src/resources/extensions/gsd/state.ts +30 -0
- package/src/resources/extensions/gsd/templates/preferences.md +1 -0
- package/src/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/context-store.test.ts +462 -0
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +197 -0
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/parsers.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +262 -1
- package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +228 -5
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
- package/src/resources/extensions/gsd/types.ts +29 -0
- package/src/resources/extensions/gsd/undo.ts +0 -1
- package/src/resources/extensions/gsd/unit-runtime.ts +5 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +352 -1
- package/src/resources/extensions/gsd/visualizer-overlay.ts +166 -22
- package/src/resources/extensions/gsd/visualizer-views.ts +464 -2
- package/src/resources/extensions/gsd/worktree-command.ts +18 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +11 -4
- package/src/resources/extensions/remote-questions/config.ts +4 -2
- package/src/resources/extensions/remote-questions/discord-adapter.ts +2 -4
- package/src/resources/extensions/remote-questions/format.ts +154 -8
- package/src/resources/extensions/remote-questions/manager.ts +9 -7
- package/src/resources/extensions/remote-questions/remote-command.ts +100 -4
- package/src/resources/extensions/remote-questions/slack-adapter.ts +58 -2
- package/src/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
- package/src/resources/extensions/remote-questions/types.ts +2 -1
- package/src/resources/extensions/ttsr/ttsr-manager.ts +26 -0
- package/src/resources/extensions/voice/index.ts +4 -3
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
// GSD DB Writer — Markdown generators + DB-first write helpers
|
|
2
|
+
//
|
|
3
|
+
// The missing DB→markdown direction. S03 established markdown→DB (md-importer.ts).
|
|
4
|
+
// This module generates DECISIONS.md and REQUIREMENTS.md from DB state,
|
|
5
|
+
// computes next decision IDs, and provides write helpers that upsert to DB
|
|
6
|
+
// then regenerate the corresponding markdown file.
|
|
7
|
+
//
|
|
8
|
+
// Critical invariant: generated markdown must round-trip through
|
|
9
|
+
// parseDecisionsTable() and parseRequirementsSections() with field fidelity.
|
|
10
|
+
|
|
11
|
+
import { join, resolve } from 'node:path';
|
|
12
|
+
import type { Decision, Requirement } from './types.js';
|
|
13
|
+
import { resolveGsdRootFile } from './paths.js';
|
|
14
|
+
import { saveFile } from './files.js';
|
|
15
|
+
|
|
16
|
+
// ─── Markdown Generators ──────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generate full DECISIONS.md content from an array of Decision objects.
|
|
20
|
+
* Produces the canonical format: H1 header, HTML comment block, table header,
|
|
21
|
+
* separator, and one data row per decision.
|
|
22
|
+
*
|
|
23
|
+
* Column order: #, When, Scope, Decision, Choice, Rationale, Revisable?
|
|
24
|
+
*/
|
|
25
|
+
export function generateDecisionsMd(decisions: Decision[]): string {
|
|
26
|
+
const lines: string[] = [];
|
|
27
|
+
|
|
28
|
+
lines.push('# Decisions Register');
|
|
29
|
+
lines.push('');
|
|
30
|
+
lines.push('<!-- Append-only. Never edit or remove existing rows.');
|
|
31
|
+
lines.push(' To reverse a decision, add a new row that supersedes it.');
|
|
32
|
+
lines.push(' Read this file at the start of any planning or research phase. -->');
|
|
33
|
+
lines.push('');
|
|
34
|
+
lines.push('| # | When | Scope | Decision | Choice | Rationale | Revisable? |');
|
|
35
|
+
lines.push('|---|------|-------|----------|--------|-----------|------------|');
|
|
36
|
+
|
|
37
|
+
for (const d of decisions) {
|
|
38
|
+
// Escape pipe characters within cell values to preserve table structure
|
|
39
|
+
const cells = [
|
|
40
|
+
d.id,
|
|
41
|
+
d.when_context,
|
|
42
|
+
d.scope,
|
|
43
|
+
d.decision,
|
|
44
|
+
d.choice,
|
|
45
|
+
d.rationale,
|
|
46
|
+
d.revisable,
|
|
47
|
+
].map(cell => (cell ?? '').replace(/\|/g, '\\|'));
|
|
48
|
+
|
|
49
|
+
lines.push(`| ${cells.join(' | ')} |`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return lines.join('\n') + '\n';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ─── Requirements Markdown Generator ──────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
/** Status values that map to specific sections, in display order. */
|
|
58
|
+
const STATUS_SECTION_MAP: Array<{ status: string; heading: string }> = [
|
|
59
|
+
{ status: 'active', heading: 'Active' },
|
|
60
|
+
{ status: 'validated', heading: 'Validated' },
|
|
61
|
+
{ status: 'deferred', heading: 'Deferred' },
|
|
62
|
+
{ status: 'out-of-scope', heading: 'Out of Scope' },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generate full REQUIREMENTS.md content from an array of Requirement objects.
|
|
67
|
+
* Groups requirements by status into sections (## Active, ## Validated, etc.),
|
|
68
|
+
* each containing ### RXXX — Description headings with bullet fields.
|
|
69
|
+
* Only emits sections that have content. Appends Traceability table and
|
|
70
|
+
* Coverage Summary at the bottom.
|
|
71
|
+
*/
|
|
72
|
+
export function generateRequirementsMd(requirements: Requirement[]): string {
|
|
73
|
+
const lines: string[] = [];
|
|
74
|
+
|
|
75
|
+
lines.push('# Requirements');
|
|
76
|
+
lines.push('');
|
|
77
|
+
lines.push('This file is the explicit capability and coverage contract for the project.');
|
|
78
|
+
lines.push('');
|
|
79
|
+
|
|
80
|
+
// Group by status
|
|
81
|
+
const byStatus = new Map<string, Requirement[]>();
|
|
82
|
+
for (const r of requirements) {
|
|
83
|
+
const status = (r.status || 'active').toLowerCase();
|
|
84
|
+
if (!byStatus.has(status)) byStatus.set(status, []);
|
|
85
|
+
byStatus.get(status)!.push(r);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Emit sections in canonical order
|
|
89
|
+
for (const { status, heading } of STATUS_SECTION_MAP) {
|
|
90
|
+
const reqs = byStatus.get(status);
|
|
91
|
+
if (!reqs || reqs.length === 0) continue;
|
|
92
|
+
|
|
93
|
+
lines.push(`## ${heading}`);
|
|
94
|
+
lines.push('');
|
|
95
|
+
|
|
96
|
+
for (const r of reqs) {
|
|
97
|
+
lines.push(`### ${r.id} — ${r.description || 'Untitled'}`);
|
|
98
|
+
|
|
99
|
+
// Emit bullet fields — only those with content
|
|
100
|
+
if (r.class) lines.push(`- Class: ${r.class}`);
|
|
101
|
+
if (r.status) lines.push(`- Status: ${r.status}`);
|
|
102
|
+
if (r.description) lines.push(`- Description: ${r.description}`);
|
|
103
|
+
if (r.why) lines.push(`- Why it matters: ${r.why}`);
|
|
104
|
+
if (r.source) lines.push(`- Source: ${r.source}`);
|
|
105
|
+
if (r.primary_owner) lines.push(`- Primary owning slice: ${r.primary_owner}`);
|
|
106
|
+
if (r.supporting_slices) lines.push(`- Supporting slices: ${r.supporting_slices}`);
|
|
107
|
+
if (r.validation) lines.push(`- Validation: ${r.validation}`);
|
|
108
|
+
if (r.notes) lines.push(`- Notes: ${r.notes}`);
|
|
109
|
+
lines.push('');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Traceability table
|
|
114
|
+
lines.push('## Traceability');
|
|
115
|
+
lines.push('');
|
|
116
|
+
lines.push('| ID | Class | Status | Primary owner | Supporting | Proof |');
|
|
117
|
+
lines.push('|---|---|---|---|---|---|');
|
|
118
|
+
|
|
119
|
+
for (const r of requirements) {
|
|
120
|
+
const proof = r.validation || 'unmapped';
|
|
121
|
+
lines.push(
|
|
122
|
+
`| ${r.id} | ${r.class || ''} | ${r.status || ''} | ${r.primary_owner || 'none'} | ${r.supporting_slices || 'none'} | ${proof} |`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
lines.push('');
|
|
127
|
+
|
|
128
|
+
// Coverage Summary
|
|
129
|
+
const activeCount = byStatus.get('active')?.length ?? 0;
|
|
130
|
+
const validatedReqs = byStatus.get('validated') ?? [];
|
|
131
|
+
const validatedIds = validatedReqs.map(r => r.id).join(', ');
|
|
132
|
+
|
|
133
|
+
lines.push('## Coverage Summary');
|
|
134
|
+
lines.push('');
|
|
135
|
+
lines.push(`- Active requirements: ${activeCount}`);
|
|
136
|
+
lines.push(`- Mapped to slices: ${activeCount}`);
|
|
137
|
+
lines.push(`- Validated: ${validatedReqs.length}${validatedIds ? ` (${validatedIds})` : ''}`);
|
|
138
|
+
lines.push(`- Unmapped active requirements: 0`);
|
|
139
|
+
|
|
140
|
+
return lines.join('\n') + '\n';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── Next Decision ID ─────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Compute the next decision ID from the current DB state.
|
|
147
|
+
* Queries MAX(CAST(SUBSTR(id, 2) AS INTEGER)) from decisions table.
|
|
148
|
+
* Returns D001 if no decisions exist. Zero-pads to 3 digits.
|
|
149
|
+
*/
|
|
150
|
+
export async function nextDecisionId(): Promise<string> {
|
|
151
|
+
try {
|
|
152
|
+
const db = await import('./gsd-db.js');
|
|
153
|
+
const adapter = db._getAdapter();
|
|
154
|
+
if (!adapter) return 'D001';
|
|
155
|
+
|
|
156
|
+
const row = adapter
|
|
157
|
+
.prepare('SELECT MAX(CAST(SUBSTR(id, 2) AS INTEGER)) as max_num FROM decisions')
|
|
158
|
+
.get();
|
|
159
|
+
|
|
160
|
+
const maxNum = row ? (row['max_num'] as number | null) : null;
|
|
161
|
+
if (maxNum == null || isNaN(maxNum)) return 'D001';
|
|
162
|
+
|
|
163
|
+
const next = maxNum + 1;
|
|
164
|
+
return `D${String(next).padStart(3, '0')}`;
|
|
165
|
+
} catch (err) {
|
|
166
|
+
process.stderr.write(`gsd-db: nextDecisionId failed: ${(err as Error).message}\n`);
|
|
167
|
+
return 'D001';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ─── Save Decision to DB + Regenerate Markdown ────────────────────────────
|
|
172
|
+
|
|
173
|
+
export interface SaveDecisionFields {
|
|
174
|
+
scope: string;
|
|
175
|
+
decision: string;
|
|
176
|
+
choice: string;
|
|
177
|
+
rationale: string;
|
|
178
|
+
revisable?: string;
|
|
179
|
+
when_context?: string;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Save a new decision to DB and regenerate DECISIONS.md.
|
|
184
|
+
* Auto-assigns the next ID via nextDecisionId().
|
|
185
|
+
* Returns the assigned ID.
|
|
186
|
+
*/
|
|
187
|
+
export async function saveDecisionToDb(
|
|
188
|
+
fields: SaveDecisionFields,
|
|
189
|
+
basePath: string,
|
|
190
|
+
): Promise<{ id: string }> {
|
|
191
|
+
try {
|
|
192
|
+
const db = await import('./gsd-db.js');
|
|
193
|
+
|
|
194
|
+
const id = await nextDecisionId();
|
|
195
|
+
|
|
196
|
+
db.upsertDecision({
|
|
197
|
+
id,
|
|
198
|
+
when_context: fields.when_context ?? '',
|
|
199
|
+
scope: fields.scope,
|
|
200
|
+
decision: fields.decision,
|
|
201
|
+
choice: fields.choice,
|
|
202
|
+
rationale: fields.rationale,
|
|
203
|
+
revisable: fields.revisable ?? 'Yes',
|
|
204
|
+
superseded_by: null,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Fetch all decisions (including superseded for the full register)
|
|
208
|
+
const adapter = db._getAdapter();
|
|
209
|
+
let allDecisions: Decision[] = [];
|
|
210
|
+
if (adapter) {
|
|
211
|
+
const rows = adapter.prepare('SELECT * FROM decisions ORDER BY seq').all();
|
|
212
|
+
allDecisions = rows.map(row => ({
|
|
213
|
+
seq: row['seq'] as number,
|
|
214
|
+
id: row['id'] as string,
|
|
215
|
+
when_context: row['when_context'] as string,
|
|
216
|
+
scope: row['scope'] as string,
|
|
217
|
+
decision: row['decision'] as string,
|
|
218
|
+
choice: row['choice'] as string,
|
|
219
|
+
rationale: row['rationale'] as string,
|
|
220
|
+
revisable: row['revisable'] as string,
|
|
221
|
+
superseded_by: (row['superseded_by'] as string) ?? null,
|
|
222
|
+
}));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const md = generateDecisionsMd(allDecisions);
|
|
226
|
+
const filePath = resolveGsdRootFile(basePath, 'DECISIONS');
|
|
227
|
+
await saveFile(filePath, md);
|
|
228
|
+
|
|
229
|
+
return { id };
|
|
230
|
+
} catch (err) {
|
|
231
|
+
process.stderr.write(`gsd-db: saveDecisionToDb failed: ${(err as Error).message}\n`);
|
|
232
|
+
throw err;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ─── Update Requirement in DB + Regenerate Markdown ───────────────────────
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Update a requirement in DB and regenerate REQUIREMENTS.md.
|
|
240
|
+
* Fetches existing requirement, merges updates, upserts, then regenerates.
|
|
241
|
+
*/
|
|
242
|
+
export async function updateRequirementInDb(
|
|
243
|
+
id: string,
|
|
244
|
+
updates: Partial<Requirement>,
|
|
245
|
+
basePath: string,
|
|
246
|
+
): Promise<void> {
|
|
247
|
+
try {
|
|
248
|
+
const db = await import('./gsd-db.js');
|
|
249
|
+
|
|
250
|
+
const existing = db.getRequirementById(id);
|
|
251
|
+
if (!existing) {
|
|
252
|
+
throw new Error(`Requirement ${id} not found`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Merge updates into existing
|
|
256
|
+
const merged: Requirement = {
|
|
257
|
+
...existing,
|
|
258
|
+
...updates,
|
|
259
|
+
id: existing.id, // ID cannot be changed
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
db.upsertRequirement(merged);
|
|
263
|
+
|
|
264
|
+
// Fetch ALL requirements (including superseded) for full file regeneration
|
|
265
|
+
const adapter = db._getAdapter();
|
|
266
|
+
let allRequirements: Requirement[] = [];
|
|
267
|
+
if (adapter) {
|
|
268
|
+
const rows = adapter.prepare('SELECT * FROM requirements ORDER BY id').all();
|
|
269
|
+
allRequirements = rows.map(row => ({
|
|
270
|
+
id: row['id'] as string,
|
|
271
|
+
class: row['class'] as string,
|
|
272
|
+
status: row['status'] as string,
|
|
273
|
+
description: row['description'] as string,
|
|
274
|
+
why: row['why'] as string,
|
|
275
|
+
source: row['source'] as string,
|
|
276
|
+
primary_owner: row['primary_owner'] as string,
|
|
277
|
+
supporting_slices: row['supporting_slices'] as string,
|
|
278
|
+
validation: row['validation'] as string,
|
|
279
|
+
notes: row['notes'] as string,
|
|
280
|
+
full_content: row['full_content'] as string,
|
|
281
|
+
superseded_by: (row['superseded_by'] as string) ?? null,
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Filter to non-superseded for the markdown file
|
|
286
|
+
// (superseded requirements don't appear in section headings)
|
|
287
|
+
const nonSuperseded = allRequirements.filter(r => r.superseded_by == null);
|
|
288
|
+
|
|
289
|
+
const md = generateRequirementsMd(nonSuperseded);
|
|
290
|
+
const filePath = resolveGsdRootFile(basePath, 'REQUIREMENTS');
|
|
291
|
+
await saveFile(filePath, md);
|
|
292
|
+
} catch (err) {
|
|
293
|
+
process.stderr.write(`gsd-db: updateRequirementInDb failed: ${(err as Error).message}\n`);
|
|
294
|
+
throw err;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ─── Save Artifact to DB + Disk ───────────────────────────────────────────
|
|
299
|
+
|
|
300
|
+
export interface SaveArtifactOpts {
|
|
301
|
+
path: string;
|
|
302
|
+
artifact_type: string;
|
|
303
|
+
content: string;
|
|
304
|
+
milestone_id?: string;
|
|
305
|
+
slice_id?: string;
|
|
306
|
+
task_id?: string;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Save an artifact to DB and write the corresponding markdown file to disk.
|
|
311
|
+
* The path is relative to .gsd/ (e.g. "milestones/M001/slices/S06/tasks/T01-SUMMARY.md").
|
|
312
|
+
* The full file path is computed as basePath + '.gsd/' + path.
|
|
313
|
+
*/
|
|
314
|
+
export async function saveArtifactToDb(
|
|
315
|
+
opts: SaveArtifactOpts,
|
|
316
|
+
basePath: string,
|
|
317
|
+
): Promise<void> {
|
|
318
|
+
try {
|
|
319
|
+
const db = await import('./gsd-db.js');
|
|
320
|
+
|
|
321
|
+
db.insertArtifact({
|
|
322
|
+
path: opts.path,
|
|
323
|
+
artifact_type: opts.artifact_type,
|
|
324
|
+
milestone_id: opts.milestone_id ?? null,
|
|
325
|
+
slice_id: opts.slice_id ?? null,
|
|
326
|
+
task_id: opts.task_id ?? null,
|
|
327
|
+
full_content: opts.content,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Write the file to disk (guard against path traversal)
|
|
331
|
+
const gsdDir = resolve(basePath, '.gsd');
|
|
332
|
+
const fullPath = resolve(basePath, '.gsd', opts.path);
|
|
333
|
+
if (!fullPath.startsWith(gsdDir)) {
|
|
334
|
+
throw new Error(`saveArtifactToDb: path escapes .gsd/ directory: ${opts.path}`);
|
|
335
|
+
}
|
|
336
|
+
await saveFile(fullPath, opts.content);
|
|
337
|
+
} catch (err) {
|
|
338
|
+
process.stderr.write(`gsd-db: saveArtifactToDb failed: ${(err as Error).message}\n`);
|
|
339
|
+
throw err;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// GSD Extension — Debug Logger
|
|
2
|
+
// Structured JSONL debug logging for diagnosing stuck/slow GSD sessions.
|
|
3
|
+
// Zero overhead when disabled — all public functions are no-ops.
|
|
4
|
+
|
|
5
|
+
import { appendFileSync, mkdirSync, readdirSync, unlinkSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { gsdRoot } from './paths.js';
|
|
8
|
+
|
|
9
|
+
// ─── State ────────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
let _enabled = false;
|
|
12
|
+
let _logPath: string | null = null;
|
|
13
|
+
let _startTime = 0;
|
|
14
|
+
|
|
15
|
+
/** Rolling counters for the debug summary written on stop. */
|
|
16
|
+
const _counters = {
|
|
17
|
+
deriveStateCalls: 0,
|
|
18
|
+
deriveStateTotalMs: 0,
|
|
19
|
+
ttsrChecks: 0,
|
|
20
|
+
ttsrTotalMs: 0,
|
|
21
|
+
ttsrPeakBuffer: 0,
|
|
22
|
+
parseRoadmapCalls: 0,
|
|
23
|
+
parseRoadmapTotalMs: 0,
|
|
24
|
+
parsePlanCalls: 0,
|
|
25
|
+
parsePlanTotalMs: 0,
|
|
26
|
+
dispatches: 0,
|
|
27
|
+
renders: 0,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Max debug log files to keep. Older ones are pruned on enable. */
|
|
31
|
+
const MAX_DEBUG_LOGS = 5;
|
|
32
|
+
|
|
33
|
+
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Enable debug logging. Creates the log file and prunes old logs.
|
|
37
|
+
* Can be activated via `--debug` flag or `GSD_DEBUG=1` env var.
|
|
38
|
+
*/
|
|
39
|
+
export function enableDebug(basePath: string): void {
|
|
40
|
+
const debugDir = join(gsdRoot(basePath), 'debug');
|
|
41
|
+
mkdirSync(debugDir, { recursive: true });
|
|
42
|
+
|
|
43
|
+
// Prune old debug logs
|
|
44
|
+
try {
|
|
45
|
+
const files = readdirSync(debugDir)
|
|
46
|
+
.filter(f => f.startsWith('debug-') && f.endsWith('.log'))
|
|
47
|
+
.sort();
|
|
48
|
+
while (files.length >= MAX_DEBUG_LOGS) {
|
|
49
|
+
const oldest = files.shift()!;
|
|
50
|
+
try { unlinkSync(join(debugDir, oldest)); } catch { /* ignore */ }
|
|
51
|
+
}
|
|
52
|
+
} catch { /* non-fatal */ }
|
|
53
|
+
|
|
54
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
55
|
+
_logPath = join(debugDir, `debug-${timestamp}.log`);
|
|
56
|
+
_startTime = Date.now();
|
|
57
|
+
_enabled = true;
|
|
58
|
+
|
|
59
|
+
// Reset counters
|
|
60
|
+
for (const key of Object.keys(_counters) as (keyof typeof _counters)[]) {
|
|
61
|
+
_counters[key] = 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Disable debug logging and return the log file path (if any). */
|
|
66
|
+
export function disableDebug(): string | null {
|
|
67
|
+
const path = _logPath;
|
|
68
|
+
_enabled = false;
|
|
69
|
+
_logPath = null;
|
|
70
|
+
_startTime = 0;
|
|
71
|
+
return path;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Check if debug mode is active. */
|
|
75
|
+
export function isDebugEnabled(): boolean {
|
|
76
|
+
return _enabled;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Return the current log file path (or null). */
|
|
80
|
+
export function getDebugLogPath(): string | null {
|
|
81
|
+
return _logPath;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Log a structured debug event. No-op when debug is disabled.
|
|
86
|
+
*
|
|
87
|
+
* Each event is one JSON line: `{ ts, event, ...data }`
|
|
88
|
+
*/
|
|
89
|
+
export function debugLog(event: string, data?: Record<string, unknown>): void {
|
|
90
|
+
if (!_enabled || !_logPath) return;
|
|
91
|
+
|
|
92
|
+
const entry = {
|
|
93
|
+
ts: new Date().toISOString(),
|
|
94
|
+
event,
|
|
95
|
+
...data,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
appendFileSync(_logPath, JSON.stringify(entry) + '\n');
|
|
100
|
+
} catch {
|
|
101
|
+
// Silently ignore write failures — debug logging must never break GSD
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Start a timer for a named operation. Returns a stop function that logs
|
|
107
|
+
* the elapsed time and optional result data.
|
|
108
|
+
*
|
|
109
|
+
* Usage:
|
|
110
|
+
* ```ts
|
|
111
|
+
* const stop = debugTime('derive-state');
|
|
112
|
+
* const result = await deriveState(base);
|
|
113
|
+
* stop({ phase: result.phase });
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export function debugTime(event: string): (data?: Record<string, unknown>) => void {
|
|
117
|
+
if (!_enabled) return _noop;
|
|
118
|
+
|
|
119
|
+
const start = performance.now();
|
|
120
|
+
return (data?: Record<string, unknown>) => {
|
|
121
|
+
const elapsed_ms = Math.round((performance.now() - start) * 100) / 100;
|
|
122
|
+
debugLog(event, { elapsed_ms, ...data });
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ─── Counter Helpers ──────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
/** Increment a debug counter (used by instrumentation points). */
|
|
129
|
+
export function debugCount(counter: keyof typeof _counters, value = 1): void {
|
|
130
|
+
if (!_enabled) return;
|
|
131
|
+
_counters[counter] += value;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Record a peak value (only updates if new value is higher). */
|
|
135
|
+
export function debugPeak(counter: keyof typeof _counters, value: number): void {
|
|
136
|
+
if (!_enabled) return;
|
|
137
|
+
if (value > _counters[counter]) {
|
|
138
|
+
_counters[counter] = value;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Write the debug summary and disable logging. Call this when auto-mode stops.
|
|
144
|
+
* Returns the log file path for user notification.
|
|
145
|
+
*/
|
|
146
|
+
export function writeDebugSummary(): string | null {
|
|
147
|
+
if (!_enabled || !_logPath) return null;
|
|
148
|
+
|
|
149
|
+
const totalElapsed_ms = Date.now() - _startTime;
|
|
150
|
+
const avgDeriveState_ms = _counters.deriveStateCalls > 0
|
|
151
|
+
? Math.round((_counters.deriveStateTotalMs / _counters.deriveStateCalls) * 100) / 100
|
|
152
|
+
: 0;
|
|
153
|
+
const avgTtsrCheck_ms = _counters.ttsrChecks > 0
|
|
154
|
+
? Math.round((_counters.ttsrTotalMs / _counters.ttsrChecks) * 100) / 100
|
|
155
|
+
: 0;
|
|
156
|
+
|
|
157
|
+
debugLog('debug-summary', {
|
|
158
|
+
totalElapsed_ms,
|
|
159
|
+
dispatches: _counters.dispatches,
|
|
160
|
+
deriveStateCalls: _counters.deriveStateCalls,
|
|
161
|
+
avgDeriveState_ms,
|
|
162
|
+
parseRoadmapCalls: _counters.parseRoadmapCalls,
|
|
163
|
+
avgParseRoadmap_ms: _counters.parseRoadmapCalls > 0
|
|
164
|
+
? Math.round((_counters.parseRoadmapTotalMs / _counters.parseRoadmapCalls) * 100) / 100
|
|
165
|
+
: 0,
|
|
166
|
+
parsePlanCalls: _counters.parsePlanCalls,
|
|
167
|
+
ttsrChecks: _counters.ttsrChecks,
|
|
168
|
+
avgTtsrCheck_ms,
|
|
169
|
+
ttsrPeakBuffer: _counters.ttsrPeakBuffer,
|
|
170
|
+
renders: _counters.renders,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
return disableDebug();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ─── Internal ─────────────────────────────────────────────────────────────────
|
|
177
|
+
|
|
178
|
+
function _noop(_data?: Record<string, unknown>): void { /* no-op */ }
|
|
@@ -72,6 +72,20 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|
|
72
72
|
|
|
73
73
|
- `version`: schema version. Start at `1`.
|
|
74
74
|
|
|
75
|
+
- `mode`: workflow mode — `"solo"` or `"team"`. Sets sensible defaults for git and project settings based on your workflow. Mode defaults are the lowest priority layer — any explicit preference overrides them. Omit to configure everything manually.
|
|
76
|
+
|
|
77
|
+
| Setting | `solo` | `team` |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| `git.auto_push` | `true` | `false` |
|
|
80
|
+
| `git.push_branches` | `false` | `true` |
|
|
81
|
+
| `git.pre_merge_check` | `false` | `true` |
|
|
82
|
+
| `git.merge_strategy` | `"squash"` | `"squash"` |
|
|
83
|
+
| `git.isolation` | `"worktree"` | `"worktree"` |
|
|
84
|
+
| `git.commit_docs` | `true` | `true` |
|
|
85
|
+
| `unique_milestone_ids` | `false` | `true` |
|
|
86
|
+
|
|
87
|
+
Quick setup: `/gsd mode` (global) or `/gsd mode project` (project-level).
|
|
88
|
+
|
|
75
89
|
- `always_use_skills`: skills GSD should use whenever they are relevant.
|
|
76
90
|
|
|
77
91
|
- `prefer_skills`: soft defaults GSD should prefer when relevant.
|
|
@@ -111,6 +125,7 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|
|
111
125
|
- `merge_strategy`: `"squash"` or `"merge"` — controls how worktree branches are merged back. `"squash"` combines all commits into one; `"merge"` preserves individual commits. Default: `"squash"`.
|
|
112
126
|
- `isolation`: `"worktree"` or `"branch"` — controls auto-mode git isolation strategy. `"worktree"` creates a milestone worktree for isolated work; `"branch"` works directly in the project root (useful for submodule-heavy repos). Default: `"worktree"`.
|
|
113
127
|
- `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`.
|
|
128
|
+
- `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.
|
|
114
129
|
|
|
115
130
|
- `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`.
|
|
116
131
|
|
|
@@ -189,6 +204,45 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|
|
189
204
|
|
|
190
205
|
---
|
|
191
206
|
|
|
207
|
+
## Workflow Mode Examples
|
|
208
|
+
|
|
209
|
+
**Solo developer — auto-push, simple IDs:**
|
|
210
|
+
|
|
211
|
+
```yaml
|
|
212
|
+
---
|
|
213
|
+
version: 1
|
|
214
|
+
mode: solo
|
|
215
|
+
---
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Equivalent to setting `git.auto_push: true`, `git.push_branches: false`, `git.pre_merge_check: false`, `git.merge_strategy: squash`, `git.isolation: worktree`, `git.commit_docs: true`, `unique_milestone_ids: false`.
|
|
219
|
+
|
|
220
|
+
**Team — unique IDs, push branches, pre-merge checks:**
|
|
221
|
+
|
|
222
|
+
```yaml
|
|
223
|
+
---
|
|
224
|
+
version: 1
|
|
225
|
+
mode: team
|
|
226
|
+
---
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Equivalent to setting `git.auto_push: false`, `git.push_branches: true`, `git.pre_merge_check: true`, `git.merge_strategy: squash`, `git.isolation: worktree`, `git.commit_docs: true`, `unique_milestone_ids: true`.
|
|
230
|
+
|
|
231
|
+
**Mode with overrides — team mode but with auto-push:**
|
|
232
|
+
|
|
233
|
+
```yaml
|
|
234
|
+
---
|
|
235
|
+
version: 1
|
|
236
|
+
mode: team
|
|
237
|
+
git:
|
|
238
|
+
auto_push: true
|
|
239
|
+
---
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Gets all team defaults except `auto_push`, which is explicitly overridden to `true`. Any explicit setting always wins over the mode default.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
192
246
|
## Minimal Example
|
|
193
247
|
|
|
194
248
|
The cleanest preferences file only specifies what you actually want:
|