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
|
@@ -8,6 +8,7 @@ import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent
|
|
|
8
8
|
import { AuthStorage } from "@gsd/pi-coding-agent";
|
|
9
9
|
import { existsSync, readFileSync, mkdirSync } from "node:fs";
|
|
10
10
|
import { join, dirname } from "node:path";
|
|
11
|
+
import { enableDebug, isDebugEnabled } from "./debug-logger.js";
|
|
11
12
|
import { fileURLToPath } from "node:url";
|
|
12
13
|
import { deriveState } from "./state.js";
|
|
13
14
|
import { GSDDashboardOverlay } from "./dashboard-overlay.js";
|
|
@@ -36,12 +37,13 @@ import {
|
|
|
36
37
|
import { loadPrompt } from "./prompt-loader.js";
|
|
37
38
|
|
|
38
39
|
import { handleRemote } from "../remote-questions/remote-command.js";
|
|
40
|
+
import { handleQuick } from "./quick.js";
|
|
39
41
|
import { handleHistory } from "./history.js";
|
|
40
42
|
import { handleUndo } from "./undo.js";
|
|
41
43
|
import { handleExport } from "./export.js";
|
|
42
44
|
import { nativeBranchList, nativeDetectMainBranch, nativeBranchListMerged, nativeBranchDelete, nativeForEachRef, nativeUpdateRef } from "./native-git-bridge.js";
|
|
43
45
|
|
|
44
|
-
function dispatchDoctorHeal(pi: ExtensionAPI, scope: string | undefined, reportText: string, structuredIssues: string): void {
|
|
46
|
+
export function dispatchDoctorHeal(pi: ExtensionAPI, scope: string | undefined, reportText: string, structuredIssues: string): void {
|
|
45
47
|
const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(process.env.HOME ?? "~", ".pi", "GSD-WORKFLOW.md");
|
|
46
48
|
const workflow = readFileSync(workflowPath, "utf-8");
|
|
47
49
|
const prompt = loadPrompt("doctor-heal", {
|
|
@@ -66,13 +68,13 @@ function projectRoot(): string {
|
|
|
66
68
|
|
|
67
69
|
export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
68
70
|
pi.registerCommand("gsd", {
|
|
69
|
-
description: "GSD — Get Shit Done: /gsd next|auto|stop|pause|status|visualize|queue|capture|triage|history|undo|skip|export|cleanup|prefs|config|hooks|doctor|migrate|remote|steer|knowledge",
|
|
71
|
+
description: "GSD — Get Shit Done: /gsd help|next|auto|stop|pause|status|visualize|queue|quick|capture|triage|history|undo|skip|export|cleanup|mode|prefs|config|hooks|run-hook|skill-health|doctor|migrate|remote|steer|knowledge",
|
|
70
72
|
getArgumentCompletions: (prefix: string) => {
|
|
71
73
|
const subcommands = [
|
|
72
|
-
"next", "auto", "stop", "pause", "status", "visualize", "queue", "discuss",
|
|
74
|
+
"help", "next", "auto", "stop", "pause", "status", "visualize", "queue", "quick", "discuss",
|
|
73
75
|
"capture", "triage",
|
|
74
|
-
"history", "undo", "skip", "export", "cleanup", "prefs",
|
|
75
|
-
"config", "hooks", "doctor", "migrate", "remote", "steer", "knowledge",
|
|
76
|
+
"history", "undo", "skip", "export", "cleanup", "mode", "prefs",
|
|
77
|
+
"config", "hooks", "run-hook", "skill-health", "doctor", "migrate", "remote", "steer", "inspect", "knowledge",
|
|
76
78
|
];
|
|
77
79
|
const parts = prefix.trim().split(/\s+/);
|
|
78
80
|
|
|
@@ -84,11 +86,18 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
84
86
|
|
|
85
87
|
if (parts[0] === "auto" && parts.length <= 2) {
|
|
86
88
|
const flagPrefix = parts[1] ?? "";
|
|
87
|
-
return ["--verbose"]
|
|
89
|
+
return ["--verbose", "--debug"]
|
|
88
90
|
.filter((f) => f.startsWith(flagPrefix))
|
|
89
91
|
.map((f) => ({ value: `auto ${f}`, label: f }));
|
|
90
92
|
}
|
|
91
93
|
|
|
94
|
+
if (parts[0] === "mode" && parts.length <= 2) {
|
|
95
|
+
const subPrefix = parts[1] ?? "";
|
|
96
|
+
return ["global", "project"]
|
|
97
|
+
.filter((cmd) => cmd.startsWith(subPrefix))
|
|
98
|
+
.map((cmd) => ({ value: `mode ${cmd}`, label: cmd }));
|
|
99
|
+
}
|
|
100
|
+
|
|
92
101
|
if (parts[0] === "prefs" && parts.length <= 2) {
|
|
93
102
|
const subPrefix = parts[1] ?? "";
|
|
94
103
|
return ["global", "project", "status", "wizard", "setup"]
|
|
@@ -161,6 +170,11 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
161
170
|
async handler(args: string, ctx: ExtensionCommandContext) {
|
|
162
171
|
const trimmed = (typeof args === "string" ? args : "").trim();
|
|
163
172
|
|
|
173
|
+
if (trimmed === "help" || trimmed === "h" || trimmed === "?") {
|
|
174
|
+
showHelp(ctx);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
164
178
|
if (trimmed === "status") {
|
|
165
179
|
await handleStatus(ctx);
|
|
166
180
|
return;
|
|
@@ -171,6 +185,15 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
171
185
|
return;
|
|
172
186
|
}
|
|
173
187
|
|
|
188
|
+
if (trimmed === "mode" || trimmed.startsWith("mode ")) {
|
|
189
|
+
const modeArgs = trimmed.replace(/^mode\s*/, "").trim();
|
|
190
|
+
const scope = modeArgs === "project" ? "project" : "global";
|
|
191
|
+
const path = scope === "project" ? getProjectGSDPreferencesPath() : getGlobalGSDPreferencesPath();
|
|
192
|
+
await ensurePreferencesFile(path, ctx, scope);
|
|
193
|
+
await handlePrefsMode(ctx, scope);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
174
197
|
if (trimmed === "prefs" || trimmed.startsWith("prefs ")) {
|
|
175
198
|
await handlePrefs(trimmed.replace(/^prefs\s*/, "").trim(), ctx);
|
|
176
199
|
return;
|
|
@@ -187,12 +210,16 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
187
210
|
return;
|
|
188
211
|
}
|
|
189
212
|
const verboseMode = trimmed.includes("--verbose");
|
|
213
|
+
const debugMode = trimmed.includes("--debug");
|
|
214
|
+
if (debugMode) enableDebug(projectRoot());
|
|
190
215
|
await startAuto(ctx, pi, projectRoot(), verboseMode, { step: true });
|
|
191
216
|
return;
|
|
192
217
|
}
|
|
193
218
|
|
|
194
219
|
if (trimmed === "auto" || trimmed.startsWith("auto ")) {
|
|
195
220
|
const verboseMode = trimmed.includes("--verbose");
|
|
221
|
+
const debugMode = trimmed.includes("--debug");
|
|
222
|
+
if (debugMode) enableDebug(projectRoot());
|
|
196
223
|
await startAuto(ctx, pi, projectRoot(), verboseMode);
|
|
197
224
|
return;
|
|
198
225
|
}
|
|
@@ -277,6 +304,11 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
277
304
|
return;
|
|
278
305
|
}
|
|
279
306
|
|
|
307
|
+
if (trimmed === "quick" || trimmed.startsWith("quick ")) {
|
|
308
|
+
await handleQuick(trimmed.replace(/^quick\s*/, "").trim(), ctx, pi);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
280
312
|
if (trimmed === "config") {
|
|
281
313
|
await handleConfig(ctx);
|
|
282
314
|
return;
|
|
@@ -288,6 +320,32 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
288
320
|
return;
|
|
289
321
|
}
|
|
290
322
|
|
|
323
|
+
// ─── Skill Health ────────────────────────────────────────────
|
|
324
|
+
if (trimmed === "skill-health" || trimmed.startsWith("skill-health ")) {
|
|
325
|
+
await handleSkillHealth(trimmed.replace(/^skill-health\s*/, "").trim(), ctx);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (trimmed.startsWith("run-hook ")) {
|
|
330
|
+
await handleRunHook(trimmed.replace(/^run-hook\s*/, "").trim(), ctx, pi);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (trimmed === "run-hook") {
|
|
334
|
+
ctx.ui.notify(`Usage: /gsd run-hook <hook-name> <unit-type> <unit-id>
|
|
335
|
+
|
|
336
|
+
Unit types:
|
|
337
|
+
execute-task - Task execution (unit-id: M001/S01/T01)
|
|
338
|
+
plan-slice - Slice planning (unit-id: M001/S01)
|
|
339
|
+
research-milestone - Milestone research (unit-id: M001)
|
|
340
|
+
complete-slice - Slice completion (unit-id: M001/S01)
|
|
341
|
+
complete-milestone - Milestone completion (unit-id: M001)
|
|
342
|
+
|
|
343
|
+
Examples:
|
|
344
|
+
/gsd run-hook code-review execute-task M001/S01/T01
|
|
345
|
+
/gsd run-hook lint-check plan-slice M001/S01`, "warning");
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
291
349
|
if (trimmed.startsWith("steer ")) {
|
|
292
350
|
await handleSteer(trimmed.replace(/^steer\s+/, "").trim(), ctx, pi);
|
|
293
351
|
return;
|
|
@@ -317,6 +375,11 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
317
375
|
return;
|
|
318
376
|
}
|
|
319
377
|
|
|
378
|
+
if (trimmed === "inspect") {
|
|
379
|
+
await handleInspect(ctx);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
320
383
|
if (trimmed === "") {
|
|
321
384
|
// Bare /gsd defaults to step mode
|
|
322
385
|
await startAuto(ctx, pi, projectRoot(), false, { step: true });
|
|
@@ -324,13 +387,57 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
324
387
|
}
|
|
325
388
|
|
|
326
389
|
ctx.ui.notify(
|
|
327
|
-
`Unknown: /gsd ${trimmed}.
|
|
390
|
+
`Unknown: /gsd ${trimmed}. Run /gsd help for available commands.`,
|
|
328
391
|
"warning",
|
|
329
392
|
);
|
|
330
393
|
},
|
|
331
394
|
});
|
|
332
395
|
}
|
|
333
396
|
|
|
397
|
+
function showHelp(ctx: ExtensionCommandContext): void {
|
|
398
|
+
const lines = [
|
|
399
|
+
"GSD — Get Shit Done\n",
|
|
400
|
+
"WORKFLOW",
|
|
401
|
+
" /gsd Run next unit in step mode (same as /gsd next)",
|
|
402
|
+
" /gsd next Execute next task, then pause [--dry-run] [--verbose]",
|
|
403
|
+
" /gsd auto Run all queued units continuously [--verbose]",
|
|
404
|
+
" /gsd stop Stop auto-mode gracefully",
|
|
405
|
+
" /gsd pause Pause auto-mode (preserves state, /gsd auto to resume)",
|
|
406
|
+
" /gsd discuss Start guided milestone/slice discussion",
|
|
407
|
+
"",
|
|
408
|
+
"VISIBILITY",
|
|
409
|
+
" /gsd status Show progress dashboard (Ctrl+Alt+G)",
|
|
410
|
+
" /gsd visualize Interactive 7-tab TUI (progress, deps, metrics, timeline, agent, changes, export)",
|
|
411
|
+
" /gsd queue Show queued/dispatched units and execution order",
|
|
412
|
+
" /gsd history View execution history [--cost] [--phase] [--model] [N]",
|
|
413
|
+
"",
|
|
414
|
+
"COURSE CORRECTION",
|
|
415
|
+
" /gsd steer <desc> Apply user override to active work",
|
|
416
|
+
" /gsd capture <text> Quick-capture a thought to CAPTURES.md",
|
|
417
|
+
" /gsd triage Classify and route pending captures",
|
|
418
|
+
" /gsd skip <unit> Prevent a unit from auto-mode dispatch",
|
|
419
|
+
" /gsd undo Revert last completed unit [--force]",
|
|
420
|
+
"",
|
|
421
|
+
"PROJECT KNOWLEDGE",
|
|
422
|
+
" /gsd knowledge <type> <text> Add rule, pattern, or lesson to KNOWLEDGE.md",
|
|
423
|
+
"",
|
|
424
|
+
"CONFIGURATION",
|
|
425
|
+
" /gsd mode Set workflow mode (solo/team) [global|project]",
|
|
426
|
+
" /gsd prefs Manage preferences [global|project|status|wizard|setup]",
|
|
427
|
+
" /gsd config Set API keys for external tools",
|
|
428
|
+
" /gsd hooks Show post-unit hook configuration",
|
|
429
|
+
"",
|
|
430
|
+
"MAINTENANCE",
|
|
431
|
+
" /gsd doctor Diagnose and repair .gsd/ state [audit|fix|heal] [scope]",
|
|
432
|
+
" /gsd export Export milestone/slice results [--json|--markdown]",
|
|
433
|
+
" /gsd cleanup Remove merged branches or snapshots [branches|snapshots]",
|
|
434
|
+
" /gsd migrate Upgrade .gsd/ structures to new format",
|
|
435
|
+
" /gsd remote Control remote auto-mode [slack|discord|status|disconnect]",
|
|
436
|
+
" /gsd inspect Show SQLite DB diagnostics (schema, row counts, recent entries)",
|
|
437
|
+
];
|
|
438
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
439
|
+
}
|
|
440
|
+
|
|
334
441
|
async function handleStatus(ctx: ExtensionCommandContext): Promise<void> {
|
|
335
442
|
const basePath = projectRoot();
|
|
336
443
|
const state = await deriveState(basePath);
|
|
@@ -433,6 +540,36 @@ async function handlePrefs(args: string, ctx: ExtensionCommandContext): Promise<
|
|
|
433
540
|
ctx.ui.notify("Usage: /gsd prefs [global|project|status|wizard|setup]", "info");
|
|
434
541
|
}
|
|
435
542
|
|
|
543
|
+
async function handlePrefsMode(ctx: ExtensionCommandContext, scope: "global" | "project"): Promise<void> {
|
|
544
|
+
const path = scope === "project" ? getProjectGSDPreferencesPath() : getGlobalGSDPreferencesPath();
|
|
545
|
+
const existing = scope === "project" ? loadProjectGSDPreferences() : loadGlobalGSDPreferences();
|
|
546
|
+
const prefs: Record<string, unknown> = existing?.preferences ? { ...existing.preferences } : {};
|
|
547
|
+
|
|
548
|
+
await configureMode(ctx, prefs);
|
|
549
|
+
|
|
550
|
+
// Serialize and save
|
|
551
|
+
prefs.version = prefs.version || 1;
|
|
552
|
+
const frontmatter = serializePreferencesToFrontmatter(prefs);
|
|
553
|
+
|
|
554
|
+
let body = "\n# GSD Skill Preferences\n\nSee `~/.gsd/agent/extensions/gsd/docs/preferences-reference.md` for full field documentation and examples.\n";
|
|
555
|
+
if (existsSync(path)) {
|
|
556
|
+
const existingContent = readFileSync(path, "utf-8");
|
|
557
|
+
const closingIdx = existingContent.indexOf("\n---", existingContent.indexOf("---"));
|
|
558
|
+
if (closingIdx !== -1) {
|
|
559
|
+
const afterFrontmatter = existingContent.slice(closingIdx + 4);
|
|
560
|
+
if (afterFrontmatter.trim()) {
|
|
561
|
+
body = afterFrontmatter;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const content = `---\n${frontmatter}---${body}`;
|
|
567
|
+
await saveFile(path, content);
|
|
568
|
+
await ctx.waitForIdle();
|
|
569
|
+
await ctx.reload();
|
|
570
|
+
ctx.ui.notify(`Saved ${scope} preferences to ${path}`, "info");
|
|
571
|
+
}
|
|
572
|
+
|
|
436
573
|
async function handleDoctor(args: string, ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void> {
|
|
437
574
|
const trimmed = args.trim();
|
|
438
575
|
const parts = trimmed ? trimmed.split(/\s+/) : [];
|
|
@@ -471,19 +608,220 @@ async function handleDoctor(args: string, ctx: ExtensionCommandContext, pi: Exte
|
|
|
471
608
|
}
|
|
472
609
|
}
|
|
473
610
|
|
|
611
|
+
// ─── Inspect ──────────────────────────────────────────────────────────────────
|
|
612
|
+
|
|
613
|
+
export interface InspectData {
|
|
614
|
+
schemaVersion: number | null;
|
|
615
|
+
counts: { decisions: number; requirements: number; artifacts: number };
|
|
616
|
+
recentDecisions: Array<{ id: string; decision: string; choice: string }>;
|
|
617
|
+
recentRequirements: Array<{ id: string; status: string; description: string }>;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export function formatInspectOutput(data: InspectData): string {
|
|
621
|
+
const lines: string[] = [];
|
|
622
|
+
lines.push("=== GSD Database Inspect ===");
|
|
623
|
+
lines.push(`Schema version: ${data.schemaVersion ?? "unknown"}`);
|
|
624
|
+
lines.push("");
|
|
625
|
+
lines.push(`Decisions: ${data.counts.decisions}`);
|
|
626
|
+
lines.push(`Requirements: ${data.counts.requirements}`);
|
|
627
|
+
lines.push(`Artifacts: ${data.counts.artifacts}`);
|
|
628
|
+
|
|
629
|
+
if (data.recentDecisions.length > 0) {
|
|
630
|
+
lines.push("");
|
|
631
|
+
lines.push("Recent decisions:");
|
|
632
|
+
for (const d of data.recentDecisions) {
|
|
633
|
+
lines.push(` ${d.id}: ${d.decision} → ${d.choice}`);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (data.recentRequirements.length > 0) {
|
|
638
|
+
lines.push("");
|
|
639
|
+
lines.push("Recent requirements:");
|
|
640
|
+
for (const r of data.recentRequirements) {
|
|
641
|
+
lines.push(` ${r.id} [${r.status}]: ${r.description}`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return lines.join("\n");
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
async function handleInspect(ctx: ExtensionCommandContext): Promise<void> {
|
|
649
|
+
try {
|
|
650
|
+
const { isDbAvailable, _getAdapter } = await import("./gsd-db.js");
|
|
651
|
+
|
|
652
|
+
if (!isDbAvailable()) {
|
|
653
|
+
ctx.ui.notify("No GSD database available. Run /gsd auto to create one.", "info");
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const adapter = _getAdapter();
|
|
658
|
+
if (!adapter) {
|
|
659
|
+
ctx.ui.notify("No GSD database available. Run /gsd auto to create one.", "info");
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const versionRow = adapter.prepare("SELECT MAX(version) as v FROM schema_version").get();
|
|
664
|
+
const schemaVersion = versionRow ? (versionRow["v"] as number | null) : null;
|
|
665
|
+
|
|
666
|
+
const dCount = adapter.prepare("SELECT count(*) as cnt FROM decisions").get();
|
|
667
|
+
const rCount = adapter.prepare("SELECT count(*) as cnt FROM requirements").get();
|
|
668
|
+
const aCount = adapter.prepare("SELECT count(*) as cnt FROM artifacts").get();
|
|
669
|
+
|
|
670
|
+
const recentDecisions = adapter
|
|
671
|
+
.prepare("SELECT id, decision, choice FROM decisions ORDER BY seq DESC LIMIT 5")
|
|
672
|
+
.all() as Array<{ id: string; decision: string; choice: string }>;
|
|
673
|
+
|
|
674
|
+
const recentRequirements = adapter
|
|
675
|
+
.prepare("SELECT id, status, description FROM requirements ORDER BY id DESC LIMIT 5")
|
|
676
|
+
.all() as Array<{ id: string; status: string; description: string }>;
|
|
677
|
+
|
|
678
|
+
const data: InspectData = {
|
|
679
|
+
schemaVersion,
|
|
680
|
+
counts: {
|
|
681
|
+
decisions: (dCount?.["cnt"] as number) ?? 0,
|
|
682
|
+
requirements: (rCount?.["cnt"] as number) ?? 0,
|
|
683
|
+
artifacts: (aCount?.["cnt"] as number) ?? 0,
|
|
684
|
+
},
|
|
685
|
+
recentDecisions,
|
|
686
|
+
recentRequirements,
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
ctx.ui.notify(formatInspectOutput(data), "info");
|
|
690
|
+
} catch (err) {
|
|
691
|
+
process.stderr.write(`gsd-db: /gsd inspect failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
692
|
+
ctx.ui.notify("Failed to inspect GSD database. Check stderr for details.", "error");
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// ─── Skill Health ─────────────────────────────────────────────────────────────
|
|
697
|
+
|
|
698
|
+
async function handleSkillHealth(args: string, ctx: ExtensionCommandContext): Promise<void> {
|
|
699
|
+
const {
|
|
700
|
+
generateSkillHealthReport,
|
|
701
|
+
formatSkillHealthReport,
|
|
702
|
+
formatSkillDetail,
|
|
703
|
+
} = await import("./skill-health.js");
|
|
704
|
+
|
|
705
|
+
const basePath = projectRoot();
|
|
706
|
+
|
|
707
|
+
// /gsd skill-health <skill-name> — detail view
|
|
708
|
+
if (args && !args.startsWith("--")) {
|
|
709
|
+
const detail = formatSkillDetail(basePath, args);
|
|
710
|
+
ctx.ui.notify(detail, "info");
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Parse flags
|
|
715
|
+
const staleMatch = args.match(/--stale\s+(\d+)/);
|
|
716
|
+
const staleDays = staleMatch ? parseInt(staleMatch[1], 10) : undefined;
|
|
717
|
+
const decliningOnly = args.includes("--declining");
|
|
718
|
+
|
|
719
|
+
const report = generateSkillHealthReport(basePath, staleDays);
|
|
720
|
+
|
|
721
|
+
if (decliningOnly) {
|
|
722
|
+
if (report.decliningSkills.length === 0) {
|
|
723
|
+
ctx.ui.notify("No skills flagged for declining performance.", "info");
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const filtered = {
|
|
727
|
+
...report,
|
|
728
|
+
skills: report.skills.filter(s => s.flagged),
|
|
729
|
+
};
|
|
730
|
+
ctx.ui.notify(formatSkillHealthReport(filtered), "info");
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
ctx.ui.notify(formatSkillHealthReport(report), "info");
|
|
735
|
+
}
|
|
736
|
+
|
|
474
737
|
// ─── Preferences Wizard ───────────────────────────────────────────────────────
|
|
475
738
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
739
|
+
/** Build short summary strings for each preference category. */
|
|
740
|
+
function buildCategorySummaries(prefs: Record<string, unknown>): Record<string, string> {
|
|
741
|
+
// Mode
|
|
742
|
+
const mode = prefs.mode as string | undefined;
|
|
743
|
+
const modeSummary = mode ?? "(not set)";
|
|
744
|
+
|
|
745
|
+
// Models
|
|
746
|
+
const models = prefs.models as Record<string, string> | undefined;
|
|
747
|
+
let modelsSummary = "(not configured)";
|
|
748
|
+
if (models && Object.keys(models).length > 0) {
|
|
749
|
+
const parts = Object.entries(models).map(([phase, model]) => `${phase}: ${model}`);
|
|
750
|
+
modelsSummary = parts.join(", ");
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Timeouts
|
|
754
|
+
const autoSup = prefs.auto_supervisor as Record<string, unknown> | undefined;
|
|
755
|
+
let timeoutsSummary = "(defaults)";
|
|
756
|
+
if (autoSup && Object.keys(autoSup).length > 0) {
|
|
757
|
+
const soft = autoSup.soft_timeout_minutes ?? "20";
|
|
758
|
+
const idle = autoSup.idle_timeout_minutes ?? "10";
|
|
759
|
+
const hard = autoSup.hard_timeout_minutes ?? "30";
|
|
760
|
+
timeoutsSummary = `soft: ${soft}m, idle: ${idle}m, hard: ${hard}m`;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Git
|
|
764
|
+
const git = prefs.git as Record<string, unknown> | undefined;
|
|
765
|
+
let gitSummary = "(defaults)";
|
|
766
|
+
if (git && Object.keys(git).length > 0) {
|
|
767
|
+
const branch = git.main_branch ?? "main";
|
|
768
|
+
const push = git.auto_push ? "on" : "off";
|
|
769
|
+
gitSummary = `main: ${branch}, push: ${push}`;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Skills
|
|
773
|
+
const discovery = prefs.skill_discovery as string | undefined;
|
|
774
|
+
const uat = prefs.uat_dispatch;
|
|
775
|
+
let skillsSummary = "(not configured)";
|
|
776
|
+
if (discovery || uat !== undefined) {
|
|
777
|
+
const parts: string[] = [];
|
|
778
|
+
if (discovery) parts.push(`discovery: ${discovery}`);
|
|
779
|
+
if (uat !== undefined) parts.push(`uat: ${uat}`);
|
|
780
|
+
skillsSummary = parts.join(", ");
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// Budget
|
|
784
|
+
const ceiling = prefs.budget_ceiling;
|
|
785
|
+
const enforcement = prefs.budget_enforcement as string | undefined;
|
|
786
|
+
let budgetSummary = "(no limit)";
|
|
787
|
+
if (ceiling !== undefined) {
|
|
788
|
+
budgetSummary = `$${ceiling}`;
|
|
789
|
+
if (enforcement) budgetSummary += ` / ${enforcement}`;
|
|
790
|
+
} else if (enforcement) {
|
|
791
|
+
budgetSummary = enforcement;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// Notifications
|
|
795
|
+
const notif = prefs.notifications as Record<string, boolean> | undefined;
|
|
796
|
+
let notifSummary = "(defaults)";
|
|
797
|
+
if (notif && Object.keys(notif).length > 0) {
|
|
798
|
+
const allKeys = ["enabled", "on_complete", "on_error", "on_budget", "on_milestone", "on_attention"];
|
|
799
|
+
const enabledCount = allKeys.filter(k => notif[k] !== false).length;
|
|
800
|
+
notifSummary = `${enabledCount}/${allKeys.length} enabled`;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// Advanced
|
|
804
|
+
const uniqueIds = prefs.unique_milestone_ids;
|
|
805
|
+
let advancedSummary = "(defaults)";
|
|
806
|
+
if (uniqueIds !== undefined) {
|
|
807
|
+
advancedSummary = `unique IDs: ${uniqueIds ? "on" : "off"}`;
|
|
808
|
+
}
|
|
483
809
|
|
|
484
|
-
|
|
810
|
+
return {
|
|
811
|
+
mode: modeSummary,
|
|
812
|
+
models: modelsSummary,
|
|
813
|
+
timeouts: timeoutsSummary,
|
|
814
|
+
git: gitSummary,
|
|
815
|
+
skills: skillsSummary,
|
|
816
|
+
budget: budgetSummary,
|
|
817
|
+
notifications: notifSummary,
|
|
818
|
+
advanced: advancedSummary,
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// ─── Category configuration functions ────────────────────────────────────────
|
|
485
823
|
|
|
486
|
-
|
|
824
|
+
async function configureModels(ctx: ExtensionCommandContext, prefs: Record<string, unknown>): Promise<void> {
|
|
487
825
|
const modelPhases = ["research", "planning", "execution", "completion"] as const;
|
|
488
826
|
const models: Record<string, string> = (prefs.models as Record<string, string>) ?? {};
|
|
489
827
|
|
|
@@ -506,7 +844,6 @@ async function handlePrefsWizard(
|
|
|
506
844
|
}
|
|
507
845
|
}
|
|
508
846
|
} else {
|
|
509
|
-
// No authenticated models available — fall back to text input
|
|
510
847
|
for (const phase of modelPhases) {
|
|
511
848
|
const current = models[phase] ?? "";
|
|
512
849
|
const input = await ctx.ui.input(
|
|
@@ -526,8 +863,9 @@ async function handlePrefsWizard(
|
|
|
526
863
|
if (Object.keys(models).length > 0) {
|
|
527
864
|
prefs.models = models;
|
|
528
865
|
}
|
|
866
|
+
}
|
|
529
867
|
|
|
530
|
-
|
|
868
|
+
async function configureTimeouts(ctx: ExtensionCommandContext, prefs: Record<string, unknown>): Promise<void> {
|
|
531
869
|
const autoSup: Record<string, unknown> = (prefs.auto_supervisor as Record<string, unknown>) ?? {};
|
|
532
870
|
const timeoutFields = [
|
|
533
871
|
{ key: "soft_timeout_minutes", label: "Soft timeout (minutes)", defaultVal: "20" },
|
|
@@ -556,8 +894,9 @@ async function handlePrefsWizard(
|
|
|
556
894
|
if (Object.keys(autoSup).length > 0) {
|
|
557
895
|
prefs.auto_supervisor = autoSup;
|
|
558
896
|
}
|
|
897
|
+
}
|
|
559
898
|
|
|
560
|
-
|
|
899
|
+
async function configureGit(ctx: ExtensionCommandContext, prefs: Record<string, unknown>): Promise<void> {
|
|
561
900
|
const git: Record<string, unknown> = (prefs.git as Record<string, unknown>) ?? {};
|
|
562
901
|
|
|
563
902
|
// main_branch
|
|
@@ -658,7 +997,7 @@ async function handlePrefsWizard(
|
|
|
658
997
|
git.isolation = isolationChoice;
|
|
659
998
|
}
|
|
660
999
|
|
|
661
|
-
//
|
|
1000
|
+
// commit_docs
|
|
662
1001
|
const currentCommitDocs = git.commit_docs;
|
|
663
1002
|
const commitDocsChoice = await ctx.ui.select(
|
|
664
1003
|
`Track .gsd/ planning docs in git${currentCommitDocs !== undefined ? ` (current: ${currentCommitDocs})` : ""}:`,
|
|
@@ -671,8 +1010,10 @@ async function handlePrefsWizard(
|
|
|
671
1010
|
if (Object.keys(git).length > 0) {
|
|
672
1011
|
prefs.git = git;
|
|
673
1012
|
}
|
|
1013
|
+
}
|
|
674
1014
|
|
|
675
|
-
|
|
1015
|
+
async function configureSkills(ctx: ExtensionCommandContext, prefs: Record<string, unknown>): Promise<void> {
|
|
1016
|
+
// Skill discovery mode
|
|
676
1017
|
const currentDiscovery = (prefs.skill_discovery as string) ?? "";
|
|
677
1018
|
const discoveryChoice = await ctx.ui.select(
|
|
678
1019
|
`Skill discovery mode${currentDiscovery ? ` (current: ${currentDiscovery})` : ""}:`,
|
|
@@ -682,17 +1023,18 @@ async function handlePrefsWizard(
|
|
|
682
1023
|
prefs.skill_discovery = discoveryChoice;
|
|
683
1024
|
}
|
|
684
1025
|
|
|
685
|
-
//
|
|
686
|
-
const
|
|
687
|
-
const
|
|
688
|
-
`
|
|
1026
|
+
// UAT dispatch
|
|
1027
|
+
const currentUat = prefs.uat_dispatch;
|
|
1028
|
+
const uatChoice = await ctx.ui.select(
|
|
1029
|
+
`UAT dispatch mode${currentUat !== undefined ? ` (current: ${currentUat})` : " (default: false)"}:`,
|
|
689
1030
|
["true", "false", "(keep current)"],
|
|
690
1031
|
);
|
|
691
|
-
if (
|
|
692
|
-
prefs.
|
|
1032
|
+
if (uatChoice && uatChoice !== "(keep current)") {
|
|
1033
|
+
prefs.uat_dispatch = uatChoice === "true";
|
|
693
1034
|
}
|
|
1035
|
+
}
|
|
694
1036
|
|
|
695
|
-
|
|
1037
|
+
async function configureBudget(ctx: ExtensionCommandContext, prefs: Record<string, unknown>): Promise<void> {
|
|
696
1038
|
const currentCeiling = prefs.budget_ceiling;
|
|
697
1039
|
const ceilingStr = currentCeiling !== undefined ? String(currentCeiling) : "";
|
|
698
1040
|
const ceilingInput = await ctx.ui.input(
|
|
@@ -738,8 +1080,9 @@ async function handlePrefsWizard(
|
|
|
738
1080
|
ctx.ui.notify(`Invalid context pause threshold "${val}" — must be 0-100. Keeping previous value.`, "warning");
|
|
739
1081
|
}
|
|
740
1082
|
}
|
|
1083
|
+
}
|
|
741
1084
|
|
|
742
|
-
|
|
1085
|
+
async function configureNotifications(ctx: ExtensionCommandContext, prefs: Record<string, unknown>): Promise<void> {
|
|
743
1086
|
const notif: Record<string, boolean> = (prefs.notifications as Record<string, boolean>) ?? {};
|
|
744
1087
|
const notifFields = [
|
|
745
1088
|
{ key: "enabled", label: "Notifications enabled (master toggle)", defaultVal: true },
|
|
@@ -764,15 +1107,88 @@ async function handlePrefsWizard(
|
|
|
764
1107
|
if (Object.keys(notif).length > 0) {
|
|
765
1108
|
prefs.notifications = notif;
|
|
766
1109
|
}
|
|
1110
|
+
}
|
|
767
1111
|
|
|
768
|
-
|
|
769
|
-
const
|
|
770
|
-
const
|
|
771
|
-
`
|
|
1112
|
+
async function configureMode(ctx: ExtensionCommandContext, prefs: Record<string, unknown>): Promise<void> {
|
|
1113
|
+
const currentMode = prefs.mode as string | undefined;
|
|
1114
|
+
const modeChoice = await ctx.ui.select(
|
|
1115
|
+
`Workflow mode${currentMode ? ` (current: ${currentMode})` : ""}:`,
|
|
1116
|
+
[
|
|
1117
|
+
"solo — auto-push, squash, simple IDs (personal projects)",
|
|
1118
|
+
"team — unique IDs, push branches, pre-merge checks (shared repos)",
|
|
1119
|
+
"(none) — configure everything manually",
|
|
1120
|
+
"(keep current)",
|
|
1121
|
+
],
|
|
1122
|
+
);
|
|
1123
|
+
const modeStr = typeof modeChoice === "string" ? modeChoice : "";
|
|
1124
|
+
if (modeStr && modeStr !== "(keep current)") {
|
|
1125
|
+
if (modeStr.startsWith("solo")) {
|
|
1126
|
+
prefs.mode = "solo";
|
|
1127
|
+
ctx.ui.notify(
|
|
1128
|
+
"Mode: solo — defaults: auto_push=true, push_branches=false, pre_merge_check=false, merge_strategy=squash, isolation=worktree, commit_docs=true, unique_milestone_ids=false",
|
|
1129
|
+
"info",
|
|
1130
|
+
);
|
|
1131
|
+
} else if (modeStr.startsWith("team")) {
|
|
1132
|
+
prefs.mode = "team";
|
|
1133
|
+
ctx.ui.notify(
|
|
1134
|
+
"Mode: team — defaults: auto_push=false, push_branches=true, pre_merge_check=true, merge_strategy=squash, isolation=worktree, commit_docs=true, unique_milestone_ids=true",
|
|
1135
|
+
"info",
|
|
1136
|
+
);
|
|
1137
|
+
} else {
|
|
1138
|
+
delete prefs.mode;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
async function configureAdvanced(ctx: ExtensionCommandContext, prefs: Record<string, unknown>): Promise<void> {
|
|
1144
|
+
const currentUnique = prefs.unique_milestone_ids;
|
|
1145
|
+
const uniqueChoice = await ctx.ui.select(
|
|
1146
|
+
`Unique milestone IDs${currentUnique !== undefined ? ` (current: ${currentUnique})` : ""}:`,
|
|
772
1147
|
["true", "false", "(keep current)"],
|
|
773
1148
|
);
|
|
774
|
-
if (
|
|
775
|
-
prefs.
|
|
1149
|
+
if (uniqueChoice && uniqueChoice !== "(keep current)") {
|
|
1150
|
+
prefs.unique_milestone_ids = uniqueChoice === "true";
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
// ─── Main wizard with category menu ─────────────────────────────────────────
|
|
1155
|
+
|
|
1156
|
+
async function handlePrefsWizard(
|
|
1157
|
+
ctx: ExtensionCommandContext,
|
|
1158
|
+
scope: "global" | "project",
|
|
1159
|
+
): Promise<void> {
|
|
1160
|
+
const path = scope === "project" ? getProjectGSDPreferencesPath() : getGlobalGSDPreferencesPath();
|
|
1161
|
+
const existing = scope === "project" ? loadProjectGSDPreferences() : loadGlobalGSDPreferences();
|
|
1162
|
+
const prefs: Record<string, unknown> = existing?.preferences ? { ...existing.preferences } : {};
|
|
1163
|
+
|
|
1164
|
+
ctx.ui.notify(`GSD preferences (${scope}) — pick a category to configure.`, "info");
|
|
1165
|
+
|
|
1166
|
+
while (true) {
|
|
1167
|
+
const summaries = buildCategorySummaries(prefs);
|
|
1168
|
+
const options = [
|
|
1169
|
+
`Workflow Mode ${summaries.mode}`,
|
|
1170
|
+
`Models ${summaries.models}`,
|
|
1171
|
+
`Timeouts ${summaries.timeouts}`,
|
|
1172
|
+
`Git ${summaries.git}`,
|
|
1173
|
+
`Skills ${summaries.skills}`,
|
|
1174
|
+
`Budget ${summaries.budget}`,
|
|
1175
|
+
`Notifications ${summaries.notifications}`,
|
|
1176
|
+
`Advanced ${summaries.advanced}`,
|
|
1177
|
+
`── Save & Exit ──`,
|
|
1178
|
+
];
|
|
1179
|
+
|
|
1180
|
+
const raw = await ctx.ui.select("GSD Preferences", options);
|
|
1181
|
+
const choice = typeof raw === "string" ? raw : "";
|
|
1182
|
+
if (!choice || choice.includes("Save & Exit")) break;
|
|
1183
|
+
|
|
1184
|
+
if (choice.startsWith("Workflow Mode")) await configureMode(ctx, prefs);
|
|
1185
|
+
else if (choice.startsWith("Models")) await configureModels(ctx, prefs);
|
|
1186
|
+
else if (choice.startsWith("Timeouts")) await configureTimeouts(ctx, prefs);
|
|
1187
|
+
else if (choice.startsWith("Git")) await configureGit(ctx, prefs);
|
|
1188
|
+
else if (choice.startsWith("Skills")) await configureSkills(ctx, prefs);
|
|
1189
|
+
else if (choice.startsWith("Budget")) await configureBudget(ctx, prefs);
|
|
1190
|
+
else if (choice.startsWith("Notifications")) await configureNotifications(ctx, prefs);
|
|
1191
|
+
else if (choice.startsWith("Advanced")) await configureAdvanced(ctx, prefs);
|
|
776
1192
|
}
|
|
777
1193
|
|
|
778
1194
|
// ─── Serialize to frontmatter ───────────────────────────────────────────
|
|
@@ -863,7 +1279,7 @@ function serializePreferencesToFrontmatter(prefs: Record<string, unknown>): stri
|
|
|
863
1279
|
|
|
864
1280
|
// Ordered keys for consistent output
|
|
865
1281
|
const orderedKeys = [
|
|
866
|
-
"version", "always_use_skills", "prefer_skills", "avoid_skills",
|
|
1282
|
+
"version", "mode", "always_use_skills", "prefer_skills", "avoid_skills",
|
|
867
1283
|
"skill_rules", "custom_instructions", "models", "skill_discovery",
|
|
868
1284
|
"auto_supervisor", "uat_dispatch", "unique_milestone_ids",
|
|
869
1285
|
"budget_ceiling", "budget_enforcement", "context_pause_threshold",
|
|
@@ -1373,3 +1789,69 @@ async function handleSteer(change: string, ctx: ExtensionCommandContext, pi: Ext
|
|
|
1373
1789
|
ctx.ui.notify(`Override registered: "${change}". Update plan documents to reflect this change.`, "info");
|
|
1374
1790
|
}
|
|
1375
1791
|
}
|
|
1792
|
+
|
|
1793
|
+
async function handleRunHook(args: string, ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void> {
|
|
1794
|
+
const parts = args.trim().split(/\s+/);
|
|
1795
|
+
if (parts.length < 3) {
|
|
1796
|
+
ctx.ui.notify(`Usage: /gsd run-hook <hook-name> <unit-type> <unit-id>
|
|
1797
|
+
|
|
1798
|
+
Unit types:
|
|
1799
|
+
execute-task - Task execution (unit-id: M001/S01/T01)
|
|
1800
|
+
plan-slice - Slice planning (unit-id: M001/S01)
|
|
1801
|
+
research-milestone - Milestone research (unit-id: M001)
|
|
1802
|
+
complete-slice - Slice completion (unit-id: M001/S01)
|
|
1803
|
+
complete-milestone - Milestone completion (unit-id: M001)
|
|
1804
|
+
|
|
1805
|
+
Examples:
|
|
1806
|
+
/gsd run-hook code-review execute-task M001/S01/T01
|
|
1807
|
+
/gsd run-hook lint-check plan-slice M001/S01`, "warning");
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
const [hookName, unitType, unitId] = parts;
|
|
1812
|
+
const basePath = projectRoot();
|
|
1813
|
+
|
|
1814
|
+
// Import the hook trigger function
|
|
1815
|
+
const { triggerHookManually, formatHookStatus, getHookStatus } = await import("./post-unit-hooks.js");
|
|
1816
|
+
const { dispatchHookUnit } = await import("./auto.js");
|
|
1817
|
+
|
|
1818
|
+
// Check if the hook exists
|
|
1819
|
+
const hooks = getHookStatus();
|
|
1820
|
+
const hookExists = hooks.some(h => h.name === hookName);
|
|
1821
|
+
if (!hookExists) {
|
|
1822
|
+
ctx.ui.notify(`Hook "${hookName}" not found. Configured hooks:\n${formatHookStatus()}`, "error");
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
// Validate unit ID format
|
|
1827
|
+
const unitIdPattern = /^M\d{3}\/S\d{2,3}\/T\d{2,3}$/;
|
|
1828
|
+
if (!unitIdPattern.test(unitId)) {
|
|
1829
|
+
ctx.ui.notify(`Invalid unit ID format: "${unitId}". Expected format: M004/S04/T03`, "warning");
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
// Trigger the hook manually
|
|
1834
|
+
const hookUnit = triggerHookManually(hookName, unitType, unitId, basePath);
|
|
1835
|
+
if (!hookUnit) {
|
|
1836
|
+
ctx.ui.notify(`Failed to trigger hook "${hookName}". The hook may be disabled or not configured for unit type "${unitType}".`, "error");
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
ctx.ui.notify(`Manually triggering hook: ${hookName} for ${unitType} ${unitId}`, "info");
|
|
1841
|
+
|
|
1842
|
+
// Dispatch the hook unit directly, bypassing normal pre-dispatch hooks
|
|
1843
|
+
const success = await dispatchHookUnit(
|
|
1844
|
+
ctx,
|
|
1845
|
+
pi,
|
|
1846
|
+
hookName,
|
|
1847
|
+
unitType,
|
|
1848
|
+
unitId,
|
|
1849
|
+
hookUnit.prompt,
|
|
1850
|
+
hookUnit.model,
|
|
1851
|
+
basePath,
|
|
1852
|
+
);
|
|
1853
|
+
|
|
1854
|
+
if (!success) {
|
|
1855
|
+
ctx.ui.notify("Failed to dispatch hook. Auto-mode may have been cancelled.", "error");
|
|
1856
|
+
}
|
|
1857
|
+
}
|