gsd-pi 2.28.0-dev.e19bf89 → 2.29.0-dev.2ccf3fb
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 +24 -17
- package/dist/cli.js +15 -9
- package/dist/resource-loader.js +80 -8
- package/dist/resources/extensions/bg-shell/process-manager.ts +13 -0
- package/dist/resources/extensions/gsd/auto-dashboard.ts +186 -65
- package/dist/resources/extensions/gsd/auto-post-unit.ts +14 -6
- package/dist/resources/extensions/gsd/auto-recovery.ts +33 -23
- package/dist/resources/extensions/gsd/auto-start.ts +25 -10
- package/dist/resources/extensions/gsd/auto-verification.ts +41 -7
- package/dist/resources/extensions/gsd/auto-worktree-sync.ts +21 -6
- package/dist/resources/extensions/gsd/auto.ts +67 -22
- package/dist/resources/extensions/gsd/commands-handlers.ts +3 -11
- package/dist/resources/extensions/gsd/commands-logs.ts +536 -0
- package/dist/resources/extensions/gsd/commands-prefs-wizard.ts +90 -47
- package/dist/resources/extensions/gsd/commands-workflow-templates.ts +544 -0
- package/dist/resources/extensions/gsd/commands.ts +75 -29
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +2 -1
- package/dist/resources/extensions/gsd/doctor-types.ts +13 -0
- package/dist/resources/extensions/gsd/doctor.ts +2 -6
- package/dist/resources/extensions/gsd/export.ts +28 -2
- package/dist/resources/extensions/gsd/gsd-db.ts +19 -0
- package/dist/resources/extensions/gsd/index.ts +2 -1
- package/dist/resources/extensions/gsd/json-persistence.ts +67 -0
- package/dist/resources/extensions/gsd/metrics.ts +17 -31
- package/dist/resources/extensions/gsd/paths.ts +0 -8
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +28 -0
- package/dist/resources/extensions/gsd/queue-order.ts +10 -11
- package/dist/resources/extensions/gsd/routing-history.ts +13 -17
- package/dist/resources/extensions/gsd/session-lock.ts +284 -0
- package/dist/resources/extensions/gsd/session-status-io.ts +23 -41
- package/dist/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/auto-skip-loop.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/commands-logs.test.ts +241 -0
- package/dist/resources/extensions/gsd/tests/extension-selector-separator.test.ts +60 -38
- package/dist/resources/extensions/gsd/tests/gsd-inspect.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/session-lock.test.ts +315 -0
- package/dist/resources/extensions/gsd/tests/validate-milestone.test.ts +55 -0
- package/dist/resources/extensions/gsd/tests/verification-evidence.test.ts +26 -24
- package/dist/resources/extensions/gsd/tests/verification-gate.test.ts +136 -7
- package/dist/resources/extensions/gsd/tests/workflow-templates.test.ts +173 -0
- package/dist/resources/extensions/gsd/types.ts +1 -0
- package/dist/resources/extensions/gsd/unit-runtime.ts +16 -13
- package/dist/resources/extensions/gsd/verification-evidence.ts +2 -0
- package/dist/resources/extensions/gsd/verification-gate.ts +13 -2
- package/dist/resources/extensions/gsd/workflow-templates/bugfix.md +87 -0
- package/dist/resources/extensions/gsd/workflow-templates/dep-upgrade.md +74 -0
- package/dist/resources/extensions/gsd/workflow-templates/full-project.md +41 -0
- package/dist/resources/extensions/gsd/workflow-templates/hotfix.md +45 -0
- package/dist/resources/extensions/gsd/workflow-templates/refactor.md +83 -0
- package/dist/resources/extensions/gsd/workflow-templates/registry.json +85 -0
- package/dist/resources/extensions/gsd/workflow-templates/security-audit.md +73 -0
- package/dist/resources/extensions/gsd/workflow-templates/small-feature.md +81 -0
- package/dist/resources/extensions/gsd/workflow-templates/spike.md +69 -0
- package/dist/resources/extensions/gsd/workflow-templates.ts +241 -0
- package/dist/resources/extensions/mcp-client/index.ts +459 -0
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +9 -20
- package/dist/resources/extensions/remote-questions/http-client.ts +76 -0
- package/dist/resources/extensions/remote-questions/notify.ts +1 -2
- package/dist/resources/extensions/remote-questions/slack-adapter.ts +11 -18
- package/dist/resources/extensions/remote-questions/telegram-adapter.ts +8 -20
- package/dist/resources/extensions/remote-questions/types.ts +3 -0
- package/dist/resources/extensions/shared/mod.ts +3 -0
- package/dist/resources/skills/create-gsd-extension/SKILL.md +87 -0
- package/dist/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
- package/dist/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
- package/dist/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
- package/dist/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
- package/dist/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
- package/dist/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
- package/dist/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
- package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
- package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
- package/dist/resources/skills/create-gsd-extension/references/state-management.md +70 -0
- package/dist/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
- package/dist/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
- package/dist/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
- package/dist/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
- package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
- package/dist/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
- package/dist/resources/skills/create-skill/SKILL.md +184 -0
- package/dist/resources/skills/create-skill/references/api-security.md +226 -0
- package/dist/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
- package/dist/resources/skills/create-skill/references/common-patterns.md +595 -0
- package/dist/resources/skills/create-skill/references/core-principles.md +437 -0
- package/dist/resources/skills/create-skill/references/executable-code.md +175 -0
- package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
- package/dist/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
- package/dist/resources/skills/create-skill/references/recommended-structure.md +168 -0
- package/dist/resources/skills/create-skill/references/skill-structure.md +372 -0
- package/dist/resources/skills/create-skill/references/use-xml-tags.md +466 -0
- package/dist/resources/skills/create-skill/references/using-scripts.md +113 -0
- package/dist/resources/skills/create-skill/references/using-templates.md +112 -0
- package/dist/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
- package/dist/resources/skills/create-skill/templates/router-skill.md +73 -0
- package/dist/resources/skills/create-skill/templates/simple-skill.md +33 -0
- package/dist/resources/skills/create-skill/workflows/add-reference.md +96 -0
- package/dist/resources/skills/create-skill/workflows/add-script.md +93 -0
- package/dist/resources/skills/create-skill/workflows/add-template.md +74 -0
- package/dist/resources/skills/create-skill/workflows/add-workflow.md +120 -0
- package/dist/resources/skills/create-skill/workflows/audit-skill.md +148 -0
- package/dist/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
- package/dist/resources/skills/create-skill/workflows/get-guidance.md +121 -0
- package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
- package/dist/resources/skills/create-skill/workflows/verify-skill.md +204 -0
- package/package.json +6 -3
- package/packages/native/dist/native.d.ts +2 -0
- package/packages/native/dist/native.js +19 -5
- package/packages/native/src/native.ts +23 -9
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +13 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -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 +8 -0
- 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 +10 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.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 +4 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/scripts/copy-assets.cjs +39 -8
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +13 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +11 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +11 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +4 -1
- package/packages/pi-tui/dist/autocomplete.d.ts +3 -0
- package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
- package/packages/pi-tui/dist/autocomplete.js +14 -0
- package/packages/pi-tui/dist/autocomplete.js.map +1 -1
- package/packages/pi-tui/src/autocomplete.ts +19 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/bg-shell/process-manager.ts +13 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +186 -65
- package/src/resources/extensions/gsd/auto-post-unit.ts +14 -6
- package/src/resources/extensions/gsd/auto-recovery.ts +33 -23
- package/src/resources/extensions/gsd/auto-start.ts +25 -10
- package/src/resources/extensions/gsd/auto-verification.ts +41 -7
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +21 -6
- package/src/resources/extensions/gsd/auto.ts +67 -22
- package/src/resources/extensions/gsd/commands-handlers.ts +3 -11
- package/src/resources/extensions/gsd/commands-logs.ts +536 -0
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +90 -47
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +544 -0
- package/src/resources/extensions/gsd/commands.ts +75 -29
- package/src/resources/extensions/gsd/dashboard-overlay.ts +2 -1
- package/src/resources/extensions/gsd/doctor-types.ts +13 -0
- package/src/resources/extensions/gsd/doctor.ts +2 -6
- package/src/resources/extensions/gsd/export.ts +28 -2
- package/src/resources/extensions/gsd/gsd-db.ts +19 -0
- package/src/resources/extensions/gsd/index.ts +2 -1
- package/src/resources/extensions/gsd/json-persistence.ts +67 -0
- package/src/resources/extensions/gsd/metrics.ts +17 -31
- package/src/resources/extensions/gsd/paths.ts +0 -8
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/workflow-start.md +28 -0
- package/src/resources/extensions/gsd/queue-order.ts +10 -11
- package/src/resources/extensions/gsd/routing-history.ts +13 -17
- package/src/resources/extensions/gsd/session-lock.ts +284 -0
- package/src/resources/extensions/gsd/session-status-io.ts +23 -41
- package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-skip-loop.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/commands-logs.test.ts +241 -0
- package/src/resources/extensions/gsd/tests/extension-selector-separator.test.ts +60 -38
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/session-lock.test.ts +315 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +26 -24
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +136 -7
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +173 -0
- package/src/resources/extensions/gsd/types.ts +1 -0
- package/src/resources/extensions/gsd/unit-runtime.ts +16 -13
- package/src/resources/extensions/gsd/verification-evidence.ts +2 -0
- package/src/resources/extensions/gsd/verification-gate.ts +13 -2
- package/src/resources/extensions/gsd/workflow-templates/bugfix.md +87 -0
- package/src/resources/extensions/gsd/workflow-templates/dep-upgrade.md +74 -0
- package/src/resources/extensions/gsd/workflow-templates/full-project.md +41 -0
- package/src/resources/extensions/gsd/workflow-templates/hotfix.md +45 -0
- package/src/resources/extensions/gsd/workflow-templates/refactor.md +83 -0
- package/src/resources/extensions/gsd/workflow-templates/registry.json +85 -0
- package/src/resources/extensions/gsd/workflow-templates/security-audit.md +73 -0
- package/src/resources/extensions/gsd/workflow-templates/small-feature.md +81 -0
- package/src/resources/extensions/gsd/workflow-templates/spike.md +69 -0
- package/src/resources/extensions/gsd/workflow-templates.ts +241 -0
- package/src/resources/extensions/mcp-client/index.ts +459 -0
- package/src/resources/extensions/remote-questions/discord-adapter.ts +9 -20
- package/src/resources/extensions/remote-questions/http-client.ts +76 -0
- package/src/resources/extensions/remote-questions/notify.ts +1 -2
- package/src/resources/extensions/remote-questions/slack-adapter.ts +11 -18
- package/src/resources/extensions/remote-questions/telegram-adapter.ts +8 -20
- package/src/resources/extensions/remote-questions/types.ts +3 -0
- package/src/resources/extensions/shared/mod.ts +3 -0
- package/src/resources/skills/create-gsd-extension/SKILL.md +87 -0
- package/src/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
- package/src/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
- package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
- package/src/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
- package/src/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
- package/src/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
- package/src/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
- package/src/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
- package/src/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
- package/src/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
- package/src/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
- package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
- package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
- package/src/resources/skills/create-gsd-extension/references/state-management.md +70 -0
- package/src/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
- package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
- package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
- package/src/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
- package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
- package/src/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
- package/src/resources/skills/create-skill/SKILL.md +184 -0
- package/src/resources/skills/create-skill/references/api-security.md +226 -0
- package/src/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
- package/src/resources/skills/create-skill/references/common-patterns.md +595 -0
- package/src/resources/skills/create-skill/references/core-principles.md +437 -0
- package/src/resources/skills/create-skill/references/executable-code.md +175 -0
- package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
- package/src/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
- package/src/resources/skills/create-skill/references/recommended-structure.md +168 -0
- package/src/resources/skills/create-skill/references/skill-structure.md +372 -0
- package/src/resources/skills/create-skill/references/use-xml-tags.md +466 -0
- package/src/resources/skills/create-skill/references/using-scripts.md +113 -0
- package/src/resources/skills/create-skill/references/using-templates.md +112 -0
- package/src/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
- package/src/resources/skills/create-skill/templates/router-skill.md +73 -0
- package/src/resources/skills/create-skill/templates/simple-skill.md +33 -0
- package/src/resources/skills/create-skill/workflows/add-reference.md +96 -0
- package/src/resources/skills/create-skill/workflows/add-script.md +93 -0
- package/src/resources/skills/create-skill/workflows/add-template.md +74 -0
- package/src/resources/skills/create-skill/workflows/add-workflow.md +120 -0
- package/src/resources/skills/create-skill/workflows/audit-skill.md +148 -0
- package/src/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
- package/src/resources/skills/create-skill/workflows/get-guidance.md +121 -0
- package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
- package/src/resources/skills/create-skill/workflows/verify-skill.md +204 -0
- package/dist/resources/extensions/gsd/preferences-hooks.ts +0 -10
- package/dist/resources/extensions/mcporter/index.ts +0 -525
- package/dist/resources/extensions/shared/progress-widget.ts +0 -282
- package/dist/resources/extensions/shared/thinking-widget.ts +0 -107
- package/src/resources/extensions/gsd/preferences-hooks.ts +0 -10
- package/src/resources/extensions/mcporter/index.ts +0 -525
- package/src/resources/extensions/shared/progress-widget.ts +0 -282
- package/src/resources/extensions/shared/thinking-widget.ts +0 -107
|
@@ -13,7 +13,8 @@ import { deriveState } from "./state.js";
|
|
|
13
13
|
import { GSDDashboardOverlay } from "./dashboard-overlay.js";
|
|
14
14
|
import { GSDVisualizerOverlay } from "./visualizer-overlay.js";
|
|
15
15
|
import { showQueue, showDiscuss, showHeadlessMilestoneCreation } from "./guided-flow.js";
|
|
16
|
-
import { startAuto, stopAuto, pauseAuto, isAutoActive, isAutoPaused, isStepMode, stopAutoRemote
|
|
16
|
+
import { startAuto, stopAuto, pauseAuto, isAutoActive, isAutoPaused, isStepMode, stopAutoRemote } from "./auto.js";
|
|
17
|
+
import { dispatchDirectPhase } from "./auto-direct-dispatch.js";
|
|
17
18
|
import { resolveProjectRoot } from "./worktree.js";
|
|
18
19
|
import { assertSafeDirectory } from "./validate-directory.js";
|
|
19
20
|
import {
|
|
@@ -21,8 +22,6 @@ import {
|
|
|
21
22
|
getProjectGSDPreferencesPath,
|
|
22
23
|
loadEffectiveGSDPreferences,
|
|
23
24
|
} from "./preferences.js";
|
|
24
|
-
import { loadPrompt } from "./prompt-loader.js";
|
|
25
|
-
|
|
26
25
|
import { handleRemote } from "../remote-questions/mod.js";
|
|
27
26
|
import { handleQuick } from "./quick.js";
|
|
28
27
|
import { handleHistory } from "./history.js";
|
|
@@ -43,31 +42,9 @@ import { handleConfig } from "./commands-config.js";
|
|
|
43
42
|
import { handleInspect } from "./commands-inspect.js";
|
|
44
43
|
import { handleCleanupBranches, handleCleanupSnapshots, handleSkip, handleDryRun } from "./commands-maintenance.js";
|
|
45
44
|
import { handleDoctor, handleSteer, handleCapture, handleTriage, handleKnowledge, handleRunHook, handleUpdate, handleSkillHealth } from "./commands-handlers.js";
|
|
45
|
+
import { handleLogs } from "./commands-logs.js";
|
|
46
|
+
import { handleStart, handleTemplates, getTemplateCompletions } from "./commands-workflow-templates.js";
|
|
46
47
|
|
|
47
|
-
// ─── Re-exports (preserve public API surface) ───────────────────────────────
|
|
48
|
-
export { handlePrefs, handlePrefsMode, handlePrefsWizard, ensurePreferencesFile, handleImportClaude, buildCategorySummaries, serializePreferencesToFrontmatter, yamlSafeString, configureMode } from "./commands-prefs-wizard.js";
|
|
49
|
-
export { TOOL_KEYS, loadToolApiKeys, getConfigAuthStorage, handleConfig } from "./commands-config.js";
|
|
50
|
-
export { type InspectData, formatInspectOutput, handleInspect } from "./commands-inspect.js";
|
|
51
|
-
export { handleCleanupBranches, handleCleanupSnapshots, handleSkip, handleDryRun } from "./commands-maintenance.js";
|
|
52
|
-
export { handleDoctor, handleSteer, handleCapture, handleTriage, handleKnowledge, handleRunHook, handleUpdate, handleSkillHealth } from "./commands-handlers.js";
|
|
53
|
-
|
|
54
|
-
export function dispatchDoctorHeal(pi: ExtensionAPI, scope: string | undefined, reportText: string, structuredIssues: string): void {
|
|
55
|
-
const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(process.env.HOME ?? "~", ".pi", "GSD-WORKFLOW.md");
|
|
56
|
-
const workflow = readFileSync(workflowPath, "utf-8");
|
|
57
|
-
const prompt = loadPrompt("doctor-heal", {
|
|
58
|
-
doctorSummary: reportText,
|
|
59
|
-
structuredIssues,
|
|
60
|
-
scopeLabel: scope ?? "active milestone / blocking scope",
|
|
61
|
-
doctorCommandSuffix: scope ? ` ${scope}` : "",
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const content = `Read the following GSD workflow protocol and execute exactly.\n\n${workflow}\n\n## Your Task\n\n${prompt}`;
|
|
65
|
-
|
|
66
|
-
pi.sendMessage(
|
|
67
|
-
{ customType: "gsd-doctor-heal", content, display: false },
|
|
68
|
-
{ triggerTurn: true },
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
48
|
|
|
72
49
|
/** Resolve the effective project root, accounting for worktree paths. */
|
|
73
50
|
export function projectRoot(): string {
|
|
@@ -78,7 +55,7 @@ export function projectRoot(): string {
|
|
|
78
55
|
|
|
79
56
|
export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
80
57
|
pi.registerCommand("gsd", {
|
|
81
|
-
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|keys|hooks|run-hook|skill-health|doctor|forensics|migrate|remote|steer|knowledge|new-milestone|parallel|update",
|
|
58
|
+
description: "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|visualize|queue|quick|capture|triage|dispatch|history|undo|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|forensics|migrate|remote|steer|knowledge|new-milestone|parallel|update",
|
|
82
59
|
getArgumentCompletions: (prefix: string) => {
|
|
83
60
|
const subcommands = [
|
|
84
61
|
{ cmd: "help", desc: "Categorized command reference with descriptions" },
|
|
@@ -107,6 +84,7 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
107
84
|
{ cmd: "run-hook", desc: "Manually trigger a specific hook" },
|
|
108
85
|
{ cmd: "skill-health", desc: "Skill lifecycle dashboard" },
|
|
109
86
|
{ cmd: "doctor", desc: "Runtime health checks with auto-fix" },
|
|
87
|
+
{ cmd: "logs", desc: "Browse activity logs, debug logs, and metrics" },
|
|
110
88
|
{ cmd: "forensics", desc: "Examine execution logs" },
|
|
111
89
|
{ cmd: "init", desc: "Project init wizard — detect, configure, bootstrap .gsd/" },
|
|
112
90
|
{ cmd: "setup", desc: "Global setup status and configuration" },
|
|
@@ -120,6 +98,8 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
120
98
|
{ cmd: "park", desc: "Park a milestone — skip without deleting" },
|
|
121
99
|
{ cmd: "unpark", desc: "Reactivate a parked milestone" },
|
|
122
100
|
{ cmd: "update", desc: "Update GSD to the latest version" },
|
|
101
|
+
{ cmd: "start", desc: "Start a workflow template (bugfix, spike, feature, etc.)" },
|
|
102
|
+
{ cmd: "templates", desc: "List available workflow templates" },
|
|
123
103
|
];
|
|
124
104
|
const parts = prefix.trim().split(/\s+/);
|
|
125
105
|
|
|
@@ -184,6 +164,18 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
184
164
|
.map((s) => ({ value: `setup ${s.cmd}`, label: s.cmd, description: s.desc }));
|
|
185
165
|
}
|
|
186
166
|
|
|
167
|
+
if (parts[0] === "logs" && parts.length <= 2) {
|
|
168
|
+
const subPrefix = parts[1] ?? "";
|
|
169
|
+
const subs = [
|
|
170
|
+
{ cmd: "debug", desc: "List or view debug log files" },
|
|
171
|
+
{ cmd: "tail", desc: "Show last N activity log summaries" },
|
|
172
|
+
{ cmd: "clear", desc: "Remove old activity and debug logs" },
|
|
173
|
+
];
|
|
174
|
+
return subs
|
|
175
|
+
.filter((s) => s.cmd.startsWith(subPrefix))
|
|
176
|
+
.map((s) => ({ value: `logs ${s.cmd}`, label: s.cmd, description: s.desc }));
|
|
177
|
+
}
|
|
178
|
+
|
|
187
179
|
if (parts[0] === "keys" && parts.length <= 2) {
|
|
188
180
|
const subPrefix = parts[1] ?? "";
|
|
189
181
|
const subs = [
|
|
@@ -293,6 +285,42 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
293
285
|
.map((s) => ({ value: `knowledge ${s.cmd}`, label: s.cmd, description: s.desc }));
|
|
294
286
|
}
|
|
295
287
|
|
|
288
|
+
if (parts[0] === "start" && parts.length <= 2) {
|
|
289
|
+
const subPrefix = parts[1] ?? "";
|
|
290
|
+
const subs = [
|
|
291
|
+
{ cmd: "bugfix", desc: "Triage, fix, test, and ship a bug fix" },
|
|
292
|
+
{ cmd: "small-feature", desc: "Lightweight feature with optional discussion" },
|
|
293
|
+
{ cmd: "spike", desc: "Research, prototype, and document findings" },
|
|
294
|
+
{ cmd: "hotfix", desc: "Minimal: fix it, test it, ship it" },
|
|
295
|
+
{ cmd: "refactor", desc: "Inventory, plan waves, migrate, verify" },
|
|
296
|
+
{ cmd: "security-audit", desc: "Scan, triage, remediate, re-scan" },
|
|
297
|
+
{ cmd: "dep-upgrade", desc: "Assess, upgrade, fix breaks, verify" },
|
|
298
|
+
{ cmd: "full-project", desc: "Complete GSD workflow with full ceremony" },
|
|
299
|
+
{ cmd: "resume", desc: "Resume an in-progress workflow" },
|
|
300
|
+
{ cmd: "--list", desc: "List all available templates" },
|
|
301
|
+
{ cmd: "--dry-run", desc: "Preview workflow without executing" },
|
|
302
|
+
];
|
|
303
|
+
return subs
|
|
304
|
+
.filter((s) => s.cmd.startsWith(subPrefix))
|
|
305
|
+
.map((s) => ({ value: `start ${s.cmd}`, label: s.cmd, description: s.desc }));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (parts[0] === "templates" && parts.length <= 2) {
|
|
309
|
+
const subPrefix = parts[1] ?? "";
|
|
310
|
+
const subs = [
|
|
311
|
+
{ cmd: "info", desc: "Show detailed template info" },
|
|
312
|
+
];
|
|
313
|
+
return subs
|
|
314
|
+
.filter((s) => s.cmd.startsWith(subPrefix))
|
|
315
|
+
.map((s) => ({ value: `templates ${s.cmd}`, label: s.cmd, description: s.desc }));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (parts[0] === "templates" && parts[1] === "info" && parts.length <= 3) {
|
|
319
|
+
const namePrefix = parts[2] ?? "";
|
|
320
|
+
return getTemplateCompletions(namePrefix)
|
|
321
|
+
.map((c) => ({ value: `templates ${c.value}`, label: c.label, description: c.description }));
|
|
322
|
+
}
|
|
323
|
+
|
|
296
324
|
if (parts[0] === "doctor") {
|
|
297
325
|
const modePrefix = parts[1] ?? "";
|
|
298
326
|
const modes = [
|
|
@@ -392,6 +420,11 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
392
420
|
return;
|
|
393
421
|
}
|
|
394
422
|
|
|
423
|
+
if (trimmed === "logs" || trimmed.startsWith("logs ")) {
|
|
424
|
+
await handleLogs(trimmed.replace(/^logs\s*/, "").trim(), ctx);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
395
428
|
if (trimmed === "forensics" || trimmed.startsWith("forensics ")) {
|
|
396
429
|
const { handleForensics } = await import("./forensics.js");
|
|
397
430
|
await handleForensics(trimmed.replace(/^forensics\s*/, "").trim(), ctx, pi);
|
|
@@ -779,6 +812,17 @@ Examples:
|
|
|
779
812
|
return;
|
|
780
813
|
}
|
|
781
814
|
|
|
815
|
+
// ─── Workflow Templates ────────────────────────────────────────
|
|
816
|
+
if (trimmed === "start" || trimmed.startsWith("start ")) {
|
|
817
|
+
await handleStart(trimmed.replace(/^start\s*/, "").trim(), ctx, pi);
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (trimmed === "templates" || trimmed.startsWith("templates ")) {
|
|
822
|
+
await handleTemplates(trimmed.replace(/^templates\s*/, "").trim(), ctx);
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
|
|
782
826
|
if (trimmed === "") {
|
|
783
827
|
// Bare /gsd defaults to step mode
|
|
784
828
|
await startAuto(ctx, pi, projectRoot(), false, { step: true });
|
|
@@ -797,6 +841,8 @@ function showHelp(ctx: ExtensionCommandContext): void {
|
|
|
797
841
|
const lines = [
|
|
798
842
|
"GSD — Get Shit Done\n",
|
|
799
843
|
"WORKFLOW",
|
|
844
|
+
" /gsd start <tpl> Start a workflow template (bugfix, spike, feature, hotfix, etc.)",
|
|
845
|
+
" /gsd templates List available workflow templates [info <name>]",
|
|
800
846
|
" /gsd Run next unit in step mode (same as /gsd next)",
|
|
801
847
|
" /gsd next Execute next task, then pause [--dry-run] [--verbose]",
|
|
802
848
|
" /gsd auto Run all queued units continuously [--verbose]",
|
|
@@ -827,7 +873,7 @@ function showHelp(ctx: ExtensionCommandContext): void {
|
|
|
827
873
|
" /gsd init Project init wizard — detect, configure, bootstrap .gsd/",
|
|
828
874
|
" /gsd setup Global setup status [llm|search|remote|keys|prefs]",
|
|
829
875
|
" /gsd mode Set workflow mode (solo/team) [global|project]",
|
|
830
|
-
" /gsd prefs Manage preferences [global|project|status|wizard|setup]",
|
|
876
|
+
" /gsd prefs Manage preferences [global|project|status|wizard|setup|import-claude]",
|
|
831
877
|
" /gsd config Set API keys for external tools",
|
|
832
878
|
" /gsd keys API key manager [list|add|remove|test|rotate|doctor]",
|
|
833
879
|
" /gsd hooks Show post-unit hook configuration",
|
|
@@ -11,7 +11,8 @@ import { truncateToWidth, visibleWidth, matchesKey, Key } from "@gsd/pi-tui";
|
|
|
11
11
|
import { deriveState } from "./state.js";
|
|
12
12
|
import { loadFile, parseRoadmap, parsePlan } from "./files.js";
|
|
13
13
|
import { resolveMilestoneFile, resolveSliceFile } from "./paths.js";
|
|
14
|
-
import { getAutoDashboardData
|
|
14
|
+
import { getAutoDashboardData } from "./auto.js";
|
|
15
|
+
import type { AutoDashboardData } from "./auto-dashboard.js";
|
|
15
16
|
import {
|
|
16
17
|
getLedger, getProjectTotals, aggregateByPhase, aggregateBySlice,
|
|
17
18
|
aggregateByModel, aggregateCacheHitRate, formatCost, formatTokenCount, formatCostProjection,
|
|
@@ -32,6 +32,19 @@ export type DoctorIssueCode =
|
|
|
32
32
|
| "gitignore_missing_patterns"
|
|
33
33
|
| "unresolvable_dependency";
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Issue codes that represent expected completion-transition states.
|
|
37
|
+
* These are detected by the doctor but should NOT be auto-fixed at task level —
|
|
38
|
+
* they are resolved by the complete-slice/complete-milestone dispatch units.
|
|
39
|
+
* Consumers (e.g. auto-post-unit health tracking) should exclude these from
|
|
40
|
+
* error counts when running at task fixLevel to avoid false escalation.
|
|
41
|
+
*/
|
|
42
|
+
export const COMPLETION_TRANSITION_CODES = new Set<DoctorIssueCode>([
|
|
43
|
+
"all_tasks_done_missing_slice_summary",
|
|
44
|
+
"all_tasks_done_missing_slice_uat",
|
|
45
|
+
"all_tasks_done_roadmap_not_checked",
|
|
46
|
+
]);
|
|
47
|
+
|
|
35
48
|
export interface DoctorIssue {
|
|
36
49
|
severity: DoctorSeverity;
|
|
37
50
|
code: DoctorIssueCode;
|
|
@@ -8,6 +8,7 @@ import { invalidateAllCaches } from "./cache.js";
|
|
|
8
8
|
import { loadEffectiveGSDPreferences, type GSDPreferences } from "./preferences.js";
|
|
9
9
|
|
|
10
10
|
import type { DoctorIssue, DoctorIssueCode } from "./doctor-types.js";
|
|
11
|
+
import { COMPLETION_TRANSITION_CODES } from "./doctor-types.js";
|
|
11
12
|
import { checkGitHealth, checkRuntimeHealth } from "./doctor-checks.js";
|
|
12
13
|
|
|
13
14
|
// ── Re-exports ─────────────────────────────────────────────────────────────
|
|
@@ -356,16 +357,11 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean;
|
|
|
356
357
|
// dispatch lifecycle (complete-slice, complete-milestone units), not to
|
|
357
358
|
// mechanical post-hook bookkeeping. When fixLevel is "task", these are
|
|
358
359
|
// detected and reported but never auto-fixed.
|
|
359
|
-
const completionTransitionCodes = new Set<DoctorIssueCode>([
|
|
360
|
-
"all_tasks_done_missing_slice_summary",
|
|
361
|
-
"all_tasks_done_missing_slice_uat",
|
|
362
|
-
"all_tasks_done_roadmap_not_checked",
|
|
363
|
-
]);
|
|
364
360
|
|
|
365
361
|
/** Whether a given issue code should be auto-fixed at the current fixLevel. */
|
|
366
362
|
const shouldFix = (code: DoctorIssueCode): boolean => {
|
|
367
363
|
if (!fix) return false;
|
|
368
|
-
if (fixLevel === "task" &&
|
|
364
|
+
if (fixLevel === "task" && COMPLETION_TRANSITION_CODES.has(code)) return false;
|
|
369
365
|
return true;
|
|
370
366
|
};
|
|
371
367
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
5
5
|
import { writeFileSync, mkdirSync } from "node:fs";
|
|
6
6
|
import { join, basename } from "node:path";
|
|
7
|
+
import { exec } from "node:child_process";
|
|
7
8
|
import {
|
|
8
9
|
getLedger, getProjectTotals, aggregateByPhase, aggregateBySlice,
|
|
9
10
|
aggregateByModel, formatCost, formatTokenCount, loadLedgerFromDisk,
|
|
@@ -12,6 +13,28 @@ import type { UnitMetrics } from "./metrics.js";
|
|
|
12
13
|
import { gsdRoot } from "./paths.js";
|
|
13
14
|
import { formatDuration, fileLink } from "../shared/mod.js";
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Open a file in the user's default browser.
|
|
18
|
+
* Uses platform-specific commands: `open` (macOS), `xdg-open` (Linux), `start` (Windows).
|
|
19
|
+
* Non-blocking, non-fatal — failures are silently ignored.
|
|
20
|
+
*/
|
|
21
|
+
export function openInBrowser(filePath: string): void {
|
|
22
|
+
const cmd =
|
|
23
|
+
process.platform === "darwin" ? "open" :
|
|
24
|
+
process.platform === "win32" ? "start" :
|
|
25
|
+
"xdg-open";
|
|
26
|
+
|
|
27
|
+
// On Windows, `start` needs an empty title argument when the path has spaces
|
|
28
|
+
const args = process.platform === "win32"
|
|
29
|
+
? `"" "${filePath}"`
|
|
30
|
+
: `"${filePath}"`;
|
|
31
|
+
|
|
32
|
+
exec(`${cmd} ${args}`, (err) => {
|
|
33
|
+
// Non-fatal — if the browser can't be opened, the file path is still shown
|
|
34
|
+
if (err) void err;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
15
38
|
/**
|
|
16
39
|
* Write an export file directly, without requiring an ExtensionCommandContext.
|
|
17
40
|
* Used by the visualizer overlay export tab.
|
|
@@ -167,10 +190,12 @@ export async function handleExport(args: string, ctx: ExtensionCommandContext, b
|
|
|
167
190
|
paths.push(bn(outPath));
|
|
168
191
|
}
|
|
169
192
|
|
|
193
|
+
const indexPath = join(gsdRoot(basePath), "reports", "index.html");
|
|
170
194
|
ctx.ui.notify(
|
|
171
|
-
`Generated ${paths.length} report snapshot${paths.length !== 1 ? "s" : ""}:\n${paths.map(p => ` ${p}`).join("\n")}\
|
|
195
|
+
`Generated ${paths.length} report snapshot${paths.length !== 1 ? "s" : ""}:\n${paths.map(p => ` ${p}`).join("\n")}\nOpening reports index in browser...`,
|
|
172
196
|
"success",
|
|
173
197
|
);
|
|
198
|
+
openInBrowser(indexPath);
|
|
174
199
|
} else {
|
|
175
200
|
// Single report for the active milestone (existing behavior)
|
|
176
201
|
const doneSlices = data.milestones.reduce((s, m) => s + m.slices.filter(sl => sl.done).length, 0);
|
|
@@ -194,9 +219,10 @@ export async function handleExport(args: string, ctx: ExtensionCommandContext, b
|
|
|
194
219
|
phase: data.phase,
|
|
195
220
|
});
|
|
196
221
|
ctx.ui.notify(
|
|
197
|
-
`HTML report saved: .gsd/reports/${bn(outPath)}\
|
|
222
|
+
`HTML report saved: .gsd/reports/${bn(outPath)}\nOpening in browser...`,
|
|
198
223
|
"success",
|
|
199
224
|
);
|
|
225
|
+
openInBrowser(outPath);
|
|
200
226
|
}
|
|
201
227
|
} catch (err) {
|
|
202
228
|
ctx.ui.notify(
|
|
@@ -348,6 +348,8 @@ function migrateSchema(db: DbAdapter): void {
|
|
|
348
348
|
|
|
349
349
|
let currentDb: DbAdapter | null = null;
|
|
350
350
|
let currentPath: string | null = null;
|
|
351
|
+
/** PID that opened the current connection — used for diagnostic logging. */
|
|
352
|
+
let currentPid: number = 0;
|
|
351
353
|
|
|
352
354
|
// ─── Public API ────────────────────────────────────────────────────────────
|
|
353
355
|
|
|
@@ -395,6 +397,7 @@ export function openDatabase(path: string): boolean {
|
|
|
395
397
|
|
|
396
398
|
currentDb = adapter;
|
|
397
399
|
currentPath = path;
|
|
400
|
+
currentPid = process.pid;
|
|
398
401
|
return true;
|
|
399
402
|
}
|
|
400
403
|
|
|
@@ -410,6 +413,7 @@ export function closeDatabase(): void {
|
|
|
410
413
|
}
|
|
411
414
|
currentDb = null;
|
|
412
415
|
currentPath = null;
|
|
416
|
+
currentPid = 0;
|
|
413
417
|
}
|
|
414
418
|
}
|
|
415
419
|
|
|
@@ -724,6 +728,21 @@ export function reconcileWorktreeDb(
|
|
|
724
728
|
}
|
|
725
729
|
}
|
|
726
730
|
|
|
731
|
+
/**
|
|
732
|
+
* Returns the PID of the process that opened the current DB connection.
|
|
733
|
+
* Returns 0 if no connection is open.
|
|
734
|
+
*/
|
|
735
|
+
export function getDbOwnerPid(): number {
|
|
736
|
+
return currentPid;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Returns the path of the currently open database, or null if none.
|
|
741
|
+
*/
|
|
742
|
+
export function getDbPath(): string | null {
|
|
743
|
+
return currentPath;
|
|
744
|
+
}
|
|
745
|
+
|
|
727
746
|
// ─── Internal Access (for testing) ─────────────────────────────────────────
|
|
728
747
|
|
|
729
748
|
/**
|
|
@@ -27,7 +27,8 @@ import { createBashTool, createWriteTool, createReadTool, createEditTool, isTool
|
|
|
27
27
|
import { Type } from "@sinclair/typebox";
|
|
28
28
|
|
|
29
29
|
import { debugLog, debugTime } from "./debug-logger.js";
|
|
30
|
-
import { registerGSDCommand
|
|
30
|
+
import { registerGSDCommand } from "./commands.js";
|
|
31
|
+
import { loadToolApiKeys } from "./commands-config.js";
|
|
31
32
|
import { registerExitCommand } from "./exit-command.js";
|
|
32
33
|
import { registerWorktreeCommand, getWorktreeOriginalCwd, getActiveWorktreeName } from "./worktree-command.js";
|
|
33
34
|
import { getActiveAutoWorktreeContext } from "./auto-worktree.js";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Load a JSON file with validation, returning a default on failure.
|
|
6
|
+
* Handles missing files, corrupt JSON, and schema mismatches uniformly.
|
|
7
|
+
*/
|
|
8
|
+
export function loadJsonFile<T>(
|
|
9
|
+
filePath: string,
|
|
10
|
+
validate: (data: unknown) => data is T,
|
|
11
|
+
defaultFactory: () => T,
|
|
12
|
+
): T {
|
|
13
|
+
try {
|
|
14
|
+
if (!existsSync(filePath)) return defaultFactory();
|
|
15
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
16
|
+
const parsed = JSON.parse(raw);
|
|
17
|
+
return validate(parsed) ? parsed : defaultFactory();
|
|
18
|
+
} catch {
|
|
19
|
+
return defaultFactory();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Load a JSON file with validation, returning null on failure.
|
|
25
|
+
* For callers that distinguish "no data" from "default data".
|
|
26
|
+
*/
|
|
27
|
+
export function loadJsonFileOrNull<T>(
|
|
28
|
+
filePath: string,
|
|
29
|
+
validate: (data: unknown) => data is T,
|
|
30
|
+
): T | null {
|
|
31
|
+
try {
|
|
32
|
+
if (!existsSync(filePath)) return null;
|
|
33
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
34
|
+
const parsed = JSON.parse(raw);
|
|
35
|
+
return validate(parsed) ? parsed : null;
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Save a JSON file, creating parent directories as needed.
|
|
43
|
+
* Non-fatal — swallows errors to prevent persistence from breaking operations.
|
|
44
|
+
*/
|
|
45
|
+
export function saveJsonFile<T>(filePath: string, data: T): void {
|
|
46
|
+
try {
|
|
47
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
48
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
49
|
+
} catch {
|
|
50
|
+
// Non-fatal — don't let persistence failures break operation
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Write a JSON file atomically (write to .tmp, then rename).
|
|
56
|
+
* Creates parent directories as needed. Non-fatal on error.
|
|
57
|
+
*/
|
|
58
|
+
export function writeJsonFileAtomic<T>(filePath: string, data: T): void {
|
|
59
|
+
try {
|
|
60
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
61
|
+
const tmp = filePath + ".tmp";
|
|
62
|
+
writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
|
|
63
|
+
renameSync(tmp, filePath);
|
|
64
|
+
} catch {
|
|
65
|
+
// Non-fatal — don't let persistence failures break operation
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
* 4. On crash recovery or fresh start, the ledger is loaded from disk
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
17
16
|
import { join } from "node:path";
|
|
18
17
|
import type { ExtensionContext } from "@gsd/pi-coding-agent";
|
|
19
18
|
import { gsdRoot } from "./paths.js";
|
|
20
19
|
import { getAndClearSkills } from "./skill-telemetry.js";
|
|
20
|
+
import { loadJsonFile, loadJsonFileOrNull, saveJsonFile } from "./json-persistence.js";
|
|
21
21
|
|
|
22
22
|
// Re-export from shared — canonical implementation lives in format-utils.
|
|
23
23
|
export { formatTokenCount } from "../shared/mod.js";
|
|
@@ -502,45 +502,31 @@ function metricsPath(base: string): string {
|
|
|
502
502
|
return join(gsdRoot(base), "metrics.json");
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
+
function isMetricsLedger(data: unknown): data is MetricsLedger {
|
|
506
|
+
return (
|
|
507
|
+
typeof data === "object" &&
|
|
508
|
+
data !== null &&
|
|
509
|
+
(data as MetricsLedger).version === 1 &&
|
|
510
|
+
Array.isArray((data as MetricsLedger).units)
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function defaultLedger(): MetricsLedger {
|
|
515
|
+
return { version: 1, projectStartedAt: Date.now(), units: [] };
|
|
516
|
+
}
|
|
517
|
+
|
|
505
518
|
/**
|
|
506
519
|
* Load ledger from disk without initializing in-memory state.
|
|
507
520
|
* Used by history/export commands outside of auto-mode.
|
|
508
521
|
*/
|
|
509
522
|
export function loadLedgerFromDisk(base: string): MetricsLedger | null {
|
|
510
|
-
|
|
511
|
-
const raw = readFileSync(metricsPath(base), "utf-8");
|
|
512
|
-
const parsed = JSON.parse(raw);
|
|
513
|
-
if (parsed.version === 1 && Array.isArray(parsed.units)) {
|
|
514
|
-
return parsed as MetricsLedger;
|
|
515
|
-
}
|
|
516
|
-
} catch {
|
|
517
|
-
// File doesn't exist or is corrupt
|
|
518
|
-
}
|
|
519
|
-
return null;
|
|
523
|
+
return loadJsonFileOrNull(metricsPath(base), isMetricsLedger);
|
|
520
524
|
}
|
|
521
525
|
|
|
522
526
|
function loadLedger(base: string): MetricsLedger {
|
|
523
|
-
|
|
524
|
-
const raw = readFileSync(metricsPath(base), "utf-8");
|
|
525
|
-
const parsed = JSON.parse(raw);
|
|
526
|
-
if (parsed.version === 1 && Array.isArray(parsed.units)) {
|
|
527
|
-
return parsed as MetricsLedger;
|
|
528
|
-
}
|
|
529
|
-
} catch {
|
|
530
|
-
// File doesn't exist or is corrupt — start fresh
|
|
531
|
-
}
|
|
532
|
-
return {
|
|
533
|
-
version: 1,
|
|
534
|
-
projectStartedAt: Date.now(),
|
|
535
|
-
units: [],
|
|
536
|
-
};
|
|
527
|
+
return loadJsonFile(metricsPath(base), isMetricsLedger, defaultLedger);
|
|
537
528
|
}
|
|
538
529
|
|
|
539
530
|
function saveLedger(base: string, data: MetricsLedger): void {
|
|
540
|
-
|
|
541
|
-
mkdirSync(gsdRoot(base), { recursive: true });
|
|
542
|
-
writeFileSync(metricsPath(base), JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
543
|
-
} catch {
|
|
544
|
-
// Don't let metrics failures break auto-mode
|
|
545
|
-
}
|
|
531
|
+
saveJsonFile(metricsPath(base), data);
|
|
546
532
|
}
|
|
@@ -137,14 +137,6 @@ export function clearPathCache(): void {
|
|
|
137
137
|
|
|
138
138
|
// ─── Name Builders ─────────────────────────────────────────────────────────
|
|
139
139
|
|
|
140
|
-
/**
|
|
141
|
-
* Build a directory name from an ID.
|
|
142
|
-
* ("M001") → "M001"
|
|
143
|
-
*/
|
|
144
|
-
export function buildDirName(id: string): string {
|
|
145
|
-
return id;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
140
|
/**
|
|
149
141
|
* Build a milestone-level file name.
|
|
150
142
|
* ("M001", "CONTEXT") → "M001-CONTEXT.md"
|
|
@@ -91,7 +91,7 @@ Before moving to the wrap-up gate, verify you have covered:
|
|
|
91
91
|
- options: "Yes, you got it (Recommended)", "Not quite — let me clarify"
|
|
92
92
|
- **The question ID must contain `depth_verification`** (e.g. `depth_verification_confirm`) — this enables the write-gate downstream.
|
|
93
93
|
|
|
94
|
-
**If `{{structuredQuestionsAvailable}}` is `false`:** ask in plain text: "Did I capture that correctly?
|
|
94
|
+
**If `{{structuredQuestionsAvailable}}` is `false`:** ask in plain text: "Did I capture that correctly? If not, tell me what I missed." Wait for confirmation before proceeding.
|
|
95
95
|
|
|
96
96
|
If they clarify, absorb the correction and re-verify.
|
|
97
97
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Workflow Template: {{templateName}}
|
|
2
|
+
|
|
3
|
+
You are executing a **{{templateName}}** workflow (template: `{{templateId}}`).
|
|
4
|
+
|
|
5
|
+
## Context
|
|
6
|
+
|
|
7
|
+
- **Description:** {{description}}
|
|
8
|
+
- **Issue reference:** {{issueRef}}
|
|
9
|
+
- **Date:** {{date}}
|
|
10
|
+
- **Branch:** {{branch}}
|
|
11
|
+
- **Artifact directory:** {{artifactDir}}
|
|
12
|
+
- **Phases:** {{phases}}
|
|
13
|
+
- **Complexity:** {{complexity}}
|
|
14
|
+
|
|
15
|
+
## Workflow Definition
|
|
16
|
+
|
|
17
|
+
Follow the workflow defined below. Execute each phase in order, completing one before moving to the next. At each phase gate, confirm with the user before proceeding.
|
|
18
|
+
|
|
19
|
+
{{workflowContent}}
|
|
20
|
+
|
|
21
|
+
## Execution Rules
|
|
22
|
+
|
|
23
|
+
1. **Follow the phases in order.** Do not skip phases unless the workflow explicitly allows it.
|
|
24
|
+
2. **Artifact discipline.** If an artifact directory is specified, write all planning/summary documents there.
|
|
25
|
+
3. **Atomic commits.** Commit working code after each meaningful change. Use conventional commit format: `<type>(<scope>): <description>`.
|
|
26
|
+
4. **Verify before shipping.** Run the project's test suite and build before marking the workflow complete.
|
|
27
|
+
5. **Gate between phases.** After each phase, summarize what was done and ask the user to confirm before moving to the next phase.
|
|
28
|
+
6. **Stay focused.** This is a {{complexity}}-complexity workflow. Match your ceremony level to the task — don't over-engineer or under-deliver.
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
* survives branch switches and is shared across sessions.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
13
12
|
import { join } from "node:path";
|
|
14
13
|
import { gsdRoot } from "./paths.js";
|
|
15
14
|
import { milestoneIdSort } from "./milestone-ids.js";
|
|
15
|
+
import { loadJsonFileOrNull, saveJsonFile } from "./json-persistence.js";
|
|
16
16
|
|
|
17
17
|
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
18
18
|
|
|
@@ -45,6 +45,12 @@ function queueOrderPath(basePath: string): string {
|
|
|
45
45
|
return join(gsdRoot(basePath), "QUEUE-ORDER.json");
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// ─── Type Guards ─────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
function isQueueOrderFile(data: unknown): data is QueueOrderFile {
|
|
51
|
+
return data !== null && typeof data === "object" && "order" in data! && Array.isArray((data as QueueOrderFile).order);
|
|
52
|
+
}
|
|
53
|
+
|
|
48
54
|
// ─── Read / Write ────────────────────────────────────────────────────────────
|
|
49
55
|
|
|
50
56
|
/**
|
|
@@ -52,15 +58,8 @@ function queueOrderPath(basePath: string): string {
|
|
|
52
58
|
* the file is corrupt/unreadable.
|
|
53
59
|
*/
|
|
54
60
|
export function loadQueueOrder(basePath: string): string[] | null {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
const data: QueueOrderFile = JSON.parse(readFileSync(p, "utf-8"));
|
|
59
|
-
if (!Array.isArray(data.order)) return null;
|
|
60
|
-
return data.order;
|
|
61
|
-
} catch {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
61
|
+
const data = loadJsonFileOrNull(queueOrderPath(basePath), isQueueOrderFile);
|
|
62
|
+
return data?.order ?? null;
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
/**
|
|
@@ -71,7 +70,7 @@ export function saveQueueOrder(basePath: string, order: string[]): void {
|
|
|
71
70
|
order,
|
|
72
71
|
updatedAt: new Date().toISOString(),
|
|
73
72
|
};
|
|
74
|
-
|
|
73
|
+
saveJsonFile(queueOrderPath(basePath), data);
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
// ─── Sorting ─────────────────────────────────────────────────────────────────
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// Tracks success/failure per tier per unit-type pattern to improve
|
|
3
3
|
// classification accuracy over time.
|
|
4
4
|
|
|
5
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
6
5
|
import { join } from "node:path";
|
|
7
6
|
import { gsdRoot } from "./paths.js";
|
|
8
7
|
import type { ComplexityTier } from "./types.js";
|
|
8
|
+
import { loadJsonFile, saveJsonFile } from "./json-persistence.js";
|
|
9
9
|
|
|
10
10
|
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
11
11
|
|
|
@@ -267,24 +267,20 @@ function historyPath(base: string): string {
|
|
|
267
267
|
return join(gsdRoot(base), HISTORY_FILE);
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
function isRoutingHistoryData(data: unknown): data is RoutingHistoryData {
|
|
271
|
+
return (
|
|
272
|
+
typeof data === "object" &&
|
|
273
|
+
data !== null &&
|
|
274
|
+
(data as RoutingHistoryData).version === 1 &&
|
|
275
|
+
typeof (data as RoutingHistoryData).patterns === "object" &&
|
|
276
|
+
(data as RoutingHistoryData).patterns !== null
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
270
280
|
function loadHistory(base: string): RoutingHistoryData {
|
|
271
|
-
|
|
272
|
-
const raw = readFileSync(historyPath(base), "utf-8");
|
|
273
|
-
const parsed = JSON.parse(raw);
|
|
274
|
-
if (parsed.version === 1 && parsed.patterns) {
|
|
275
|
-
return parsed as RoutingHistoryData;
|
|
276
|
-
}
|
|
277
|
-
} catch {
|
|
278
|
-
// File doesn't exist or is corrupt — start fresh
|
|
279
|
-
}
|
|
280
|
-
return createEmptyHistory();
|
|
281
|
+
return loadJsonFile(historyPath(base), isRoutingHistoryData, createEmptyHistory);
|
|
281
282
|
}
|
|
282
283
|
|
|
283
284
|
function saveHistory(base: string, data: RoutingHistoryData): void {
|
|
284
|
-
|
|
285
|
-
mkdirSync(gsdRoot(base), { recursive: true });
|
|
286
|
-
writeFileSync(historyPath(base), JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
287
|
-
} catch {
|
|
288
|
-
// Non-fatal — don't let history failures break auto-mode
|
|
289
|
-
}
|
|
285
|
+
saveJsonFile(historyPath(base), data);
|
|
290
286
|
}
|