gsd-pi 2.29.0-dev.49d972f → 2.29.0-dev.4c155ee
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/headless.js +4 -0
- package/dist/resources/extensions/bg-shell/process-manager.ts +13 -0
- package/dist/resources/extensions/gsd/auto-dashboard.ts +217 -65
- package/dist/resources/extensions/gsd/auto-dispatch.ts +32 -3
- package/dist/resources/extensions/gsd/auto-post-unit.ts +44 -12
- package/dist/resources/extensions/gsd/auto-prompts.ts +40 -17
- package/dist/resources/extensions/gsd/auto-recovery.ts +2 -1
- package/dist/resources/extensions/gsd/auto-start.ts +18 -32
- package/dist/resources/extensions/gsd/auto-worktree.ts +21 -182
- package/dist/resources/extensions/gsd/auto.ts +2 -9
- package/dist/resources/extensions/gsd/captures.ts +4 -10
- package/dist/resources/extensions/gsd/commands-handlers.ts +2 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.ts +44 -14
- package/dist/resources/extensions/gsd/commands-workflow-templates.ts +544 -0
- package/dist/resources/extensions/gsd/commands.ts +55 -2
- package/dist/resources/extensions/gsd/detection.ts +2 -1
- package/dist/resources/extensions/gsd/doctor-checks.ts +49 -1
- package/dist/resources/extensions/gsd/doctor-types.ts +3 -1
- package/dist/resources/extensions/gsd/forensics.ts +2 -2
- package/dist/resources/extensions/gsd/git-service.ts +3 -2
- package/dist/resources/extensions/gsd/gitignore.ts +9 -63
- package/dist/resources/extensions/gsd/gsd-db.ts +1 -165
- package/dist/resources/extensions/gsd/guided-flow.ts +8 -5
- package/dist/resources/extensions/gsd/index.ts +3 -3
- package/dist/resources/extensions/gsd/md-importer.ts +3 -2
- package/dist/resources/extensions/gsd/mechanical-completion.ts +430 -0
- package/dist/resources/extensions/gsd/migrate/command.ts +3 -2
- package/dist/resources/extensions/gsd/migrate/writer.ts +2 -1
- package/dist/resources/extensions/gsd/migrate-external.ts +123 -0
- package/dist/resources/extensions/gsd/paths.ts +24 -2
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +6 -5
- package/dist/resources/extensions/gsd/preferences-models.ts +7 -1
- package/dist/resources/extensions/gsd/preferences-validation.ts +2 -1
- package/dist/resources/extensions/gsd/preferences.ts +10 -5
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -1
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +28 -0
- package/dist/resources/extensions/gsd/repo-identity.ts +148 -0
- package/dist/resources/extensions/gsd/resource-version.ts +99 -0
- package/dist/resources/extensions/gsd/session-forensics.ts +4 -3
- package/dist/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
- package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
- package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
- package/dist/resources/extensions/gsd/tests/extension-selector-separator.test.ts +60 -38
- package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
- package/dist/resources/extensions/gsd/tests/git-service.test.ts +10 -37
- package/dist/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
- package/dist/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
- package/dist/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
- package/dist/resources/extensions/gsd/tests/workflow-templates.test.ts +173 -0
- package/dist/resources/extensions/gsd/triage-resolution.ts +2 -1
- package/dist/resources/extensions/gsd/types.ts +2 -0
- 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/gsd/worktree-command.ts +1 -11
- package/dist/resources/extensions/gsd/worktree-manager.ts +3 -2
- package/dist/resources/extensions/gsd/worktree.ts +42 -5
- package/dist/resources/extensions/mcp-client/index.ts +459 -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/dist/resources/skills/react-best-practices/SKILL.md +1 -1
- package/package.json +1 -1
- 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/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +3 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +13 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +3 -0
- package/src/resources/extensions/bg-shell/process-manager.ts +13 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +217 -65
- package/src/resources/extensions/gsd/auto-dispatch.ts +32 -3
- package/src/resources/extensions/gsd/auto-post-unit.ts +44 -12
- package/src/resources/extensions/gsd/auto-prompts.ts +40 -17
- package/src/resources/extensions/gsd/auto-recovery.ts +2 -1
- package/src/resources/extensions/gsd/auto-start.ts +18 -32
- package/src/resources/extensions/gsd/auto-worktree.ts +21 -182
- package/src/resources/extensions/gsd/auto.ts +2 -9
- package/src/resources/extensions/gsd/captures.ts +4 -10
- package/src/resources/extensions/gsd/commands-handlers.ts +2 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +44 -14
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +544 -0
- package/src/resources/extensions/gsd/commands.ts +55 -2
- package/src/resources/extensions/gsd/detection.ts +2 -1
- package/src/resources/extensions/gsd/doctor-checks.ts +49 -1
- package/src/resources/extensions/gsd/doctor-types.ts +3 -1
- package/src/resources/extensions/gsd/forensics.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +3 -2
- package/src/resources/extensions/gsd/gitignore.ts +9 -63
- package/src/resources/extensions/gsd/gsd-db.ts +1 -165
- package/src/resources/extensions/gsd/guided-flow.ts +8 -5
- package/src/resources/extensions/gsd/index.ts +3 -3
- package/src/resources/extensions/gsd/md-importer.ts +3 -2
- package/src/resources/extensions/gsd/mechanical-completion.ts +430 -0
- package/src/resources/extensions/gsd/migrate/command.ts +3 -2
- package/src/resources/extensions/gsd/migrate/writer.ts +2 -1
- package/src/resources/extensions/gsd/migrate-external.ts +123 -0
- package/src/resources/extensions/gsd/paths.ts +24 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +6 -5
- package/src/resources/extensions/gsd/preferences-models.ts +7 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +2 -1
- package/src/resources/extensions/gsd/preferences.ts +10 -5
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -1
- package/src/resources/extensions/gsd/prompts/workflow-start.md +28 -0
- package/src/resources/extensions/gsd/repo-identity.ts +148 -0
- package/src/resources/extensions/gsd/resource-version.ts +99 -0
- package/src/resources/extensions/gsd/session-forensics.ts +4 -3
- package/src/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
- package/src/resources/extensions/gsd/tests/extension-selector-separator.test.ts +60 -38
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
- package/src/resources/extensions/gsd/tests/git-service.test.ts +10 -37
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +173 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +2 -1
- package/src/resources/extensions/gsd/types.ts +2 -0
- 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/gsd/worktree-command.ts +1 -11
- package/src/resources/extensions/gsd/worktree-manager.ts +3 -2
- package/src/resources/extensions/gsd/worktree.ts +42 -5
- package/src/resources/extensions/mcp-client/index.ts +459 -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/src/resources/skills/react-best-practices/SKILL.md +1 -1
- package/dist/resources/extensions/gsd/auto-worktree-sync.ts +0 -199
- package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
- package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
- package/dist/resources/extensions/mcporter/index.ts +0 -525
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -199
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
- package/src/resources/extensions/mcporter/index.ts +0 -525
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSD Workflow Template Commands — /gsd start, /gsd templates
|
|
3
|
+
*
|
|
4
|
+
* Handles the `/gsd start [template] [description]` and `/gsd templates` commands.
|
|
5
|
+
* Resolves templates by name or auto-detection, then dispatches the workflow prompt.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
9
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import {
|
|
12
|
+
resolveByName,
|
|
13
|
+
autoDetect,
|
|
14
|
+
listTemplates,
|
|
15
|
+
getTemplateInfo,
|
|
16
|
+
loadWorkflowTemplate,
|
|
17
|
+
loadRegistry,
|
|
18
|
+
type TemplateMatch,
|
|
19
|
+
} from "./workflow-templates.js";
|
|
20
|
+
import { loadPrompt } from "./prompt-loader.js";
|
|
21
|
+
import { gsdRoot } from "./paths.js";
|
|
22
|
+
import { GitServiceImpl, runGit } from "./git-service.js";
|
|
23
|
+
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
24
|
+
import { isAutoActive, isAutoPaused } from "./auto.js";
|
|
25
|
+
|
|
26
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Generate a URL-friendly slug from text.
|
|
30
|
+
*/
|
|
31
|
+
function slugify(text: string): string {
|
|
32
|
+
return text
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
35
|
+
.replace(/^-|-$/g, "")
|
|
36
|
+
.slice(0, 40)
|
|
37
|
+
.replace(/-$/, "");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the next workflow task number by scanning existing directories.
|
|
42
|
+
*/
|
|
43
|
+
function getNextWorkflowNum(workflowDir: string): number {
|
|
44
|
+
if (!existsSync(workflowDir)) return 1;
|
|
45
|
+
try {
|
|
46
|
+
const entries = readdirSync(workflowDir, { withFileTypes: true });
|
|
47
|
+
let max = 0;
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
if (!entry.isDirectory()) continue;
|
|
50
|
+
const match = entry.name.match(/^(\d{6})-(\d+)-/);
|
|
51
|
+
if (match) {
|
|
52
|
+
const num = parseInt(match[2], 10);
|
|
53
|
+
if (num > max) max = num;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return max + 1;
|
|
57
|
+
} catch {
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Format the date as YYMMDD for directory naming.
|
|
64
|
+
*/
|
|
65
|
+
function datePrefix(): string {
|
|
66
|
+
const d = new Date();
|
|
67
|
+
const yy = String(d.getFullYear()).slice(2);
|
|
68
|
+
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
69
|
+
const dd = String(d.getDate()).padStart(2, "0");
|
|
70
|
+
return `${yy}${mm}${dd}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ─── State Types ─────────────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
interface WorkflowPhaseState {
|
|
76
|
+
name: string;
|
|
77
|
+
index: number;
|
|
78
|
+
status: "pending" | "active" | "completed";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface WorkflowState {
|
|
82
|
+
template: string;
|
|
83
|
+
templateName: string;
|
|
84
|
+
description: string;
|
|
85
|
+
branch: string;
|
|
86
|
+
phases: WorkflowPhaseState[];
|
|
87
|
+
currentPhase: number;
|
|
88
|
+
startedAt: string;
|
|
89
|
+
updatedAt: string;
|
|
90
|
+
completedAt?: string;
|
|
91
|
+
artifactDir: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Write a STATE.json file to track workflow execution state.
|
|
96
|
+
*/
|
|
97
|
+
function writeWorkflowState(
|
|
98
|
+
artifactDir: string,
|
|
99
|
+
templateId: string,
|
|
100
|
+
templateName: string,
|
|
101
|
+
phases: string[],
|
|
102
|
+
description: string,
|
|
103
|
+
branch: string,
|
|
104
|
+
): void {
|
|
105
|
+
const statePath = join(artifactDir, "STATE.json");
|
|
106
|
+
const state: WorkflowState = {
|
|
107
|
+
template: templateId,
|
|
108
|
+
templateName,
|
|
109
|
+
description,
|
|
110
|
+
branch,
|
|
111
|
+
phases: phases.map((p, i) => ({
|
|
112
|
+
name: p,
|
|
113
|
+
index: i,
|
|
114
|
+
status: i === 0 ? "active" as const : "pending" as const,
|
|
115
|
+
})),
|
|
116
|
+
currentPhase: 0,
|
|
117
|
+
startedAt: new Date().toISOString(),
|
|
118
|
+
updatedAt: new Date().toISOString(),
|
|
119
|
+
artifactDir,
|
|
120
|
+
};
|
|
121
|
+
writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Scan all workflow artifact directories for in-progress STATE.json files.
|
|
126
|
+
* Returns workflows that were started but not completed.
|
|
127
|
+
*/
|
|
128
|
+
function findInProgressWorkflows(basePath: string): WorkflowState[] {
|
|
129
|
+
const workflowsRoot = join(gsdRoot(basePath), "workflows");
|
|
130
|
+
if (!existsSync(workflowsRoot)) return [];
|
|
131
|
+
|
|
132
|
+
const results: WorkflowState[] = [];
|
|
133
|
+
try {
|
|
134
|
+
// Scan each category dir (bugfixes/, features/, spikes/, etc.)
|
|
135
|
+
for (const category of readdirSync(workflowsRoot, { withFileTypes: true })) {
|
|
136
|
+
if (!category.isDirectory()) continue;
|
|
137
|
+
const categoryDir = join(workflowsRoot, category.name);
|
|
138
|
+
|
|
139
|
+
for (const workflow of readdirSync(categoryDir, { withFileTypes: true })) {
|
|
140
|
+
if (!workflow.isDirectory()) continue;
|
|
141
|
+
const statePath = join(categoryDir, workflow.name, "STATE.json");
|
|
142
|
+
if (!existsSync(statePath)) continue;
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const raw = readFileSync(statePath, "utf-8");
|
|
146
|
+
const state = JSON.parse(raw) as WorkflowState;
|
|
147
|
+
if (!state.completedAt) {
|
|
148
|
+
results.push(state);
|
|
149
|
+
}
|
|
150
|
+
} catch { /* corrupted state file — skip */ }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} catch { /* workflows dir unreadable — skip */ }
|
|
154
|
+
|
|
155
|
+
// Sort by most recently updated
|
|
156
|
+
results.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
157
|
+
return results;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── /gsd start ──────────────────────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
export async function handleStart(
|
|
163
|
+
args: string,
|
|
164
|
+
ctx: ExtensionCommandContext,
|
|
165
|
+
pi: ExtensionAPI,
|
|
166
|
+
): Promise<void> {
|
|
167
|
+
const trimmed = args.trim();
|
|
168
|
+
|
|
169
|
+
// /gsd start --list → same as /gsd templates
|
|
170
|
+
if (trimmed === "--list" || trimmed === "list") {
|
|
171
|
+
ctx.ui.notify(listTemplates(), "info");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ─── Auto-mode conflict guard ──────────────────────────────────────────
|
|
176
|
+
// Workflow templates dispatch their own messages and switch git branches,
|
|
177
|
+
// which would conflict with an active auto-mode dispatch loop.
|
|
178
|
+
if (isAutoActive()) {
|
|
179
|
+
ctx.ui.notify(
|
|
180
|
+
"Cannot start a workflow template while auto-mode is running.\n" +
|
|
181
|
+
"Run /gsd pause first, then /gsd start.",
|
|
182
|
+
"warning",
|
|
183
|
+
);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (isAutoPaused()) {
|
|
188
|
+
ctx.ui.notify(
|
|
189
|
+
"Auto-mode is paused. Starting a workflow template will run independently.\n" +
|
|
190
|
+
"The paused auto-mode session can be resumed later with /gsd auto.",
|
|
191
|
+
"info",
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ─── Resume detection ───────────────────────────────────────────────────
|
|
196
|
+
// /gsd start --resume or /gsd start resume → resume in-progress workflow
|
|
197
|
+
if (trimmed === "--resume" || trimmed === "resume") {
|
|
198
|
+
const basePath = process.cwd();
|
|
199
|
+
const inProgress = findInProgressWorkflows(basePath);
|
|
200
|
+
if (inProgress.length === 0) {
|
|
201
|
+
ctx.ui.notify("No in-progress workflows found.", "info");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Resume the most recent one
|
|
206
|
+
const wf = inProgress[0];
|
|
207
|
+
const activePhase = wf.phases.find(p => p.status === "active");
|
|
208
|
+
const completedCount = wf.phases.filter(p => p.status === "completed").length;
|
|
209
|
+
|
|
210
|
+
ctx.ui.notify(
|
|
211
|
+
`Resuming: ${wf.templateName}\n` +
|
|
212
|
+
`Description: ${wf.description}\n` +
|
|
213
|
+
`Progress: ${completedCount}/${wf.phases.length} phases completed\n` +
|
|
214
|
+
`Current phase: ${activePhase?.name ?? "unknown"}\n` +
|
|
215
|
+
`Branch: ${wf.branch}\n` +
|
|
216
|
+
`Artifacts: ${wf.artifactDir}`,
|
|
217
|
+
"info",
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
const workflowContent = loadWorkflowTemplate(wf.template);
|
|
221
|
+
if (!workflowContent) {
|
|
222
|
+
ctx.ui.notify(`Template "${wf.template}" workflow file not found.`, "warning");
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const prompt = loadPrompt("workflow-start", {
|
|
227
|
+
templateId: wf.template,
|
|
228
|
+
templateName: wf.templateName,
|
|
229
|
+
templateDescription: `RESUMING — pick up from phase "${activePhase?.name ?? "unknown"}" (${completedCount}/${wf.phases.length} phases done)`,
|
|
230
|
+
phases: wf.phases.map(p => `${p.name}${p.status === "completed" ? " ✓" : p.status === "active" ? " ←" : ""}`).join(" → "),
|
|
231
|
+
complexity: "resume",
|
|
232
|
+
artifactDir: wf.artifactDir,
|
|
233
|
+
branch: wf.branch,
|
|
234
|
+
description: wf.description,
|
|
235
|
+
issueRef: "(none)",
|
|
236
|
+
date: new Date().toISOString().split("T")[0],
|
|
237
|
+
workflowContent,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
pi.sendMessage(
|
|
241
|
+
{ customType: "gsd-workflow-template", content: prompt, display: false },
|
|
242
|
+
{ triggerTurn: true },
|
|
243
|
+
);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Show in-progress workflows when /gsd start is called with no args
|
|
248
|
+
if (!trimmed) {
|
|
249
|
+
const basePath = process.cwd();
|
|
250
|
+
const inProgress = findInProgressWorkflows(basePath);
|
|
251
|
+
if (inProgress.length > 0) {
|
|
252
|
+
const wf = inProgress[0];
|
|
253
|
+
const activePhase = wf.phases.find(p => p.status === "active");
|
|
254
|
+
const completedCount = wf.phases.filter(p => p.status === "completed").length;
|
|
255
|
+
ctx.ui.notify(
|
|
256
|
+
`In-progress workflow found:\n` +
|
|
257
|
+
` ${wf.templateName}: "${wf.description}"\n` +
|
|
258
|
+
` Phase ${completedCount + 1}/${wf.phases.length}: ${activePhase?.name ?? "unknown"}\n\n` +
|
|
259
|
+
`Run /gsd start resume to continue it.\n`,
|
|
260
|
+
"info",
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// /gsd start --dry-run <template> → preview without executing
|
|
266
|
+
const dryRun = trimmed.includes("--dry-run");
|
|
267
|
+
const cleanedArgs = trimmed.replace(/--dry-run\s*/, "").trim();
|
|
268
|
+
|
|
269
|
+
// Parse: first word might be a template name, rest is description
|
|
270
|
+
const parts = cleanedArgs.split(/\s+/);
|
|
271
|
+
const firstWord = parts[0] ?? "";
|
|
272
|
+
|
|
273
|
+
// Check for --issue flag (bugfix shortcut)
|
|
274
|
+
const issueMatch = cleanedArgs.match(/--issue\s+(\S+)/);
|
|
275
|
+
const issueRef = issueMatch ? issueMatch[1] : null;
|
|
276
|
+
|
|
277
|
+
// Try resolving first word as a template name
|
|
278
|
+
let match: TemplateMatch | null = null;
|
|
279
|
+
let description = "";
|
|
280
|
+
|
|
281
|
+
if (firstWord) {
|
|
282
|
+
match = resolveByName(firstWord);
|
|
283
|
+
if (match) {
|
|
284
|
+
// First word was a template name; rest is description
|
|
285
|
+
description = parts.slice(1).join(" ").replace(/--issue\s+\S+/, "").trim();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// If no explicit template, try auto-detection from the full input
|
|
290
|
+
if (!match && cleanedArgs) {
|
|
291
|
+
const detected = autoDetect(cleanedArgs);
|
|
292
|
+
if (detected.length === 1 || (detected.length > 0 && detected[0].confidence === "high")) {
|
|
293
|
+
match = detected[0];
|
|
294
|
+
description = cleanedArgs;
|
|
295
|
+
ctx.ui.notify(
|
|
296
|
+
`Auto-detected template: ${match.template.name} (matched: "${match.matchedTrigger}")`,
|
|
297
|
+
"info",
|
|
298
|
+
);
|
|
299
|
+
} else if (detected.length > 1) {
|
|
300
|
+
const choices = detected.slice(0, 4).map(
|
|
301
|
+
(m) => ` /gsd start ${m.id} ${cleanedArgs}`
|
|
302
|
+
);
|
|
303
|
+
ctx.ui.notify(
|
|
304
|
+
`Multiple templates could match. Pick one:\n\n${choices.join("\n")}\n\nOr specify explicitly: /gsd start <template> <description>`,
|
|
305
|
+
"info",
|
|
306
|
+
);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// No template resolved at all
|
|
312
|
+
if (!match) {
|
|
313
|
+
if (!trimmed) {
|
|
314
|
+
ctx.ui.notify(
|
|
315
|
+
"Usage: /gsd start <template> [description]\n\n" +
|
|
316
|
+
"Templates:\n" +
|
|
317
|
+
" bugfix Triage → fix → verify → ship\n" +
|
|
318
|
+
" small-feature Scope → plan → implement → verify\n" +
|
|
319
|
+
" spike Scope → research → synthesize\n" +
|
|
320
|
+
" hotfix Fix → ship (minimal ceremony)\n" +
|
|
321
|
+
" refactor Inventory → plan → migrate → verify\n" +
|
|
322
|
+
" security-audit Scan → triage → remediate → re-scan\n" +
|
|
323
|
+
" dep-upgrade Assess → upgrade → fix → verify\n" +
|
|
324
|
+
" full-project Complete GSD with full ceremony\n\n" +
|
|
325
|
+
"Examples:\n" +
|
|
326
|
+
" /gsd start bugfix fix login button not responding\n" +
|
|
327
|
+
" /gsd start spike evaluate auth libraries\n" +
|
|
328
|
+
" /gsd start hotfix critical: API returns 500\n\n" +
|
|
329
|
+
"Flags:\n" +
|
|
330
|
+
" --dry-run Preview what would happen without executing\n" +
|
|
331
|
+
" --issue <ref> Link to a GitHub issue\n\n" +
|
|
332
|
+
"Run /gsd templates for detailed template info.",
|
|
333
|
+
"info",
|
|
334
|
+
);
|
|
335
|
+
} else {
|
|
336
|
+
ctx.ui.notify(
|
|
337
|
+
`No template matched "${firstWord}". Run /gsd start to see available templates.`,
|
|
338
|
+
"warning",
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ─── Resolved template ───────────────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
const templateId = match.id;
|
|
347
|
+
const template = match.template;
|
|
348
|
+
const basePath = process.cwd();
|
|
349
|
+
const date = new Date().toISOString().split("T")[0];
|
|
350
|
+
|
|
351
|
+
// Load the workflow template content
|
|
352
|
+
const workflowContent = loadWorkflowTemplate(templateId);
|
|
353
|
+
if (!workflowContent) {
|
|
354
|
+
ctx.ui.notify(
|
|
355
|
+
`Template "${templateId}" is registered but its workflow file (${template.file}) hasn't been created yet.`,
|
|
356
|
+
"warning",
|
|
357
|
+
);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ─── Dry-run mode: preview without executing ────────────────────────────
|
|
362
|
+
|
|
363
|
+
if (dryRun) {
|
|
364
|
+
const slug = slugify(description || templateId);
|
|
365
|
+
const lines = [
|
|
366
|
+
`DRY RUN — ${template.name} (${templateId})\n`,
|
|
367
|
+
`Description: ${description || "(none)"}`,
|
|
368
|
+
`Complexity: ${template.estimated_complexity}`,
|
|
369
|
+
`Phases: ${template.phases.join(" → ")}`,
|
|
370
|
+
"",
|
|
371
|
+
];
|
|
372
|
+
if (template.artifact_dir) {
|
|
373
|
+
const prefix = datePrefix();
|
|
374
|
+
const num = getNextWorkflowNum(join(basePath, template.artifact_dir));
|
|
375
|
+
lines.push(`Artifact dir: ${template.artifact_dir}${prefix}-${num}-${slug}`);
|
|
376
|
+
} else {
|
|
377
|
+
lines.push("Artifact dir: (none — hotfix mode)");
|
|
378
|
+
}
|
|
379
|
+
lines.push(`Branch: gsd/${templateId}/${slug}`);
|
|
380
|
+
if (issueRef) lines.push(`Issue: ${issueRef}`);
|
|
381
|
+
lines.push("", "No changes made. Remove --dry-run to execute.");
|
|
382
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// ─── Route full-project to standard GSD workflow ────────────────────────
|
|
387
|
+
|
|
388
|
+
if (templateId === "full-project") {
|
|
389
|
+
const root = gsdRoot(basePath);
|
|
390
|
+
if (!existsSync(root)) {
|
|
391
|
+
ctx.ui.notify(
|
|
392
|
+
"Routing to /gsd init for full project setup...",
|
|
393
|
+
"info",
|
|
394
|
+
);
|
|
395
|
+
// Trigger /gsd init by dispatching to the handler
|
|
396
|
+
pi.sendMessage(
|
|
397
|
+
{
|
|
398
|
+
customType: "gsd-workflow-template",
|
|
399
|
+
content: "The user wants to start a full GSD project. Run `/gsd init` to bootstrap the project, then `/gsd auto` to begin execution.",
|
|
400
|
+
display: false,
|
|
401
|
+
},
|
|
402
|
+
{ triggerTurn: true },
|
|
403
|
+
);
|
|
404
|
+
} else {
|
|
405
|
+
ctx.ui.notify(
|
|
406
|
+
"Project already initialized. Use `/gsd auto` to continue or `/gsd discuss` to start a new milestone.",
|
|
407
|
+
"info",
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// ─── Create artifact directory ──────────────────────────────────────────
|
|
414
|
+
|
|
415
|
+
let artifactDir = "";
|
|
416
|
+
if (template.artifact_dir) {
|
|
417
|
+
const slug = slugify(description || templateId);
|
|
418
|
+
const prefix = datePrefix();
|
|
419
|
+
const num = getNextWorkflowNum(join(basePath, template.artifact_dir));
|
|
420
|
+
artifactDir = `${template.artifact_dir}${prefix}-${num}-${slug}`;
|
|
421
|
+
mkdirSync(join(basePath, artifactDir), { recursive: true });
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// ─── Create git branch (unless isolation: none) ─────────────────────────
|
|
425
|
+
|
|
426
|
+
const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
|
|
427
|
+
const git = new GitServiceImpl(basePath, gitPrefs);
|
|
428
|
+
const skipBranch = gitPrefs.isolation === "none";
|
|
429
|
+
const slug = slugify(description || templateId);
|
|
430
|
+
const branchName = `gsd/${templateId}/${slug}`;
|
|
431
|
+
let branchCreated = false;
|
|
432
|
+
|
|
433
|
+
if (!skipBranch) {
|
|
434
|
+
try {
|
|
435
|
+
const current = git.getCurrentBranch();
|
|
436
|
+
if (current !== branchName) {
|
|
437
|
+
try {
|
|
438
|
+
git.autoCommit("workflow-template", templateId, []);
|
|
439
|
+
} catch { /* nothing to commit */ }
|
|
440
|
+
runGit(basePath, ["checkout", "-b", branchName]);
|
|
441
|
+
branchCreated = true;
|
|
442
|
+
}
|
|
443
|
+
} catch (err) {
|
|
444
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
445
|
+
ctx.ui.notify(
|
|
446
|
+
`Could not create branch ${branchName}: ${message}. Working on current branch.`,
|
|
447
|
+
"warning",
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const actualBranch = branchCreated ? branchName : git.getCurrentBranch();
|
|
453
|
+
|
|
454
|
+
// ─── Write workflow state for resume support ────────────────────────────
|
|
455
|
+
|
|
456
|
+
if (artifactDir) {
|
|
457
|
+
writeWorkflowState(
|
|
458
|
+
join(basePath, artifactDir),
|
|
459
|
+
templateId,
|
|
460
|
+
template.name,
|
|
461
|
+
template.phases,
|
|
462
|
+
description,
|
|
463
|
+
actualBranch,
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// ─── Notify and dispatch ────────────────────────────────────────────────
|
|
468
|
+
|
|
469
|
+
const infoLines = [
|
|
470
|
+
`Starting workflow: ${template.name}`,
|
|
471
|
+
`Phases: ${template.phases.join(" → ")}`,
|
|
472
|
+
];
|
|
473
|
+
if (artifactDir) infoLines.push(`Artifacts: ${artifactDir}`);
|
|
474
|
+
infoLines.push(`Branch: ${actualBranch}`);
|
|
475
|
+
ctx.ui.notify(infoLines.join("\n"), "info");
|
|
476
|
+
|
|
477
|
+
const prompt = loadPrompt("workflow-start", {
|
|
478
|
+
templateId,
|
|
479
|
+
templateName: template.name,
|
|
480
|
+
templateDescription: template.description,
|
|
481
|
+
phases: template.phases.join(" → "),
|
|
482
|
+
complexity: template.estimated_complexity,
|
|
483
|
+
artifactDir: artifactDir || "(none)",
|
|
484
|
+
branch: actualBranch,
|
|
485
|
+
description: description || "(none provided)",
|
|
486
|
+
issueRef: issueRef || "(none)",
|
|
487
|
+
date,
|
|
488
|
+
workflowContent,
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
pi.sendMessage(
|
|
492
|
+
{
|
|
493
|
+
customType: "gsd-workflow-template",
|
|
494
|
+
content: prompt,
|
|
495
|
+
display: false,
|
|
496
|
+
},
|
|
497
|
+
{ triggerTurn: true },
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// ─── /gsd templates ──────────────────────────────────────────────────────────
|
|
502
|
+
|
|
503
|
+
export async function handleTemplates(
|
|
504
|
+
args: string,
|
|
505
|
+
ctx: ExtensionCommandContext,
|
|
506
|
+
): Promise<void> {
|
|
507
|
+
const trimmed = args.trim();
|
|
508
|
+
|
|
509
|
+
// /gsd templates info <name>
|
|
510
|
+
if (trimmed.startsWith("info ")) {
|
|
511
|
+
const name = trimmed.replace(/^info\s+/, "").trim();
|
|
512
|
+
const info = getTemplateInfo(name);
|
|
513
|
+
if (info) {
|
|
514
|
+
ctx.ui.notify(info, "info");
|
|
515
|
+
} else {
|
|
516
|
+
ctx.ui.notify(
|
|
517
|
+
`Unknown template "${name}". Run /gsd templates to see available templates.`,
|
|
518
|
+
"warning",
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// /gsd templates — list all
|
|
525
|
+
ctx.ui.notify(listTemplates(), "info");
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Return template IDs for autocomplete in /gsd templates info <name>.
|
|
530
|
+
*/
|
|
531
|
+
export function getTemplateCompletions(prefix: string): Array<{ value: string; label: string; description: string }> {
|
|
532
|
+
try {
|
|
533
|
+
const registry = loadRegistry();
|
|
534
|
+
return Object.entries(registry.templates)
|
|
535
|
+
.filter(([id]) => id.startsWith(prefix))
|
|
536
|
+
.map(([id, entry]) => ({
|
|
537
|
+
value: `info ${id}`,
|
|
538
|
+
label: id,
|
|
539
|
+
description: entry.description,
|
|
540
|
+
}));
|
|
541
|
+
} catch {
|
|
542
|
+
return [];
|
|
543
|
+
}
|
|
544
|
+
}
|
|
@@ -8,6 +8,7 @@ import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent
|
|
|
8
8
|
import type { GSDState } from "./types.js";
|
|
9
9
|
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
10
10
|
import { join } from "node:path";
|
|
11
|
+
import { gsdRoot } from "./paths.js";
|
|
11
12
|
import { enableDebug } from "./debug-logger.js";
|
|
12
13
|
import { deriveState } from "./state.js";
|
|
13
14
|
import { GSDDashboardOverlay } from "./dashboard-overlay.js";
|
|
@@ -43,6 +44,7 @@ import { handleInspect } from "./commands-inspect.js";
|
|
|
43
44
|
import { handleCleanupBranches, handleCleanupSnapshots, handleSkip, handleDryRun } from "./commands-maintenance.js";
|
|
44
45
|
import { handleDoctor, handleSteer, handleCapture, handleTriage, handleKnowledge, handleRunHook, handleUpdate, handleSkillHealth } from "./commands-handlers.js";
|
|
45
46
|
import { handleLogs } from "./commands-logs.js";
|
|
47
|
+
import { handleStart, handleTemplates, getTemplateCompletions } from "./commands-workflow-templates.js";
|
|
46
48
|
|
|
47
49
|
|
|
48
50
|
/** Resolve the effective project root, accounting for worktree paths. */
|
|
@@ -54,7 +56,7 @@ export function projectRoot(): string {
|
|
|
54
56
|
|
|
55
57
|
export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
56
58
|
pi.registerCommand("gsd", {
|
|
57
|
-
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",
|
|
59
|
+
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",
|
|
58
60
|
getArgumentCompletions: (prefix: string) => {
|
|
59
61
|
const subcommands = [
|
|
60
62
|
{ cmd: "help", desc: "Categorized command reference with descriptions" },
|
|
@@ -97,6 +99,8 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
97
99
|
{ cmd: "park", desc: "Park a milestone — skip without deleting" },
|
|
98
100
|
{ cmd: "unpark", desc: "Reactivate a parked milestone" },
|
|
99
101
|
{ cmd: "update", desc: "Update GSD to the latest version" },
|
|
102
|
+
{ cmd: "start", desc: "Start a workflow template (bugfix, spike, feature, etc.)" },
|
|
103
|
+
{ cmd: "templates", desc: "List available workflow templates" },
|
|
100
104
|
];
|
|
101
105
|
const parts = prefix.trim().split(/\s+/);
|
|
102
106
|
|
|
@@ -282,6 +286,42 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
282
286
|
.map((s) => ({ value: `knowledge ${s.cmd}`, label: s.cmd, description: s.desc }));
|
|
283
287
|
}
|
|
284
288
|
|
|
289
|
+
if (parts[0] === "start" && parts.length <= 2) {
|
|
290
|
+
const subPrefix = parts[1] ?? "";
|
|
291
|
+
const subs = [
|
|
292
|
+
{ cmd: "bugfix", desc: "Triage, fix, test, and ship a bug fix" },
|
|
293
|
+
{ cmd: "small-feature", desc: "Lightweight feature with optional discussion" },
|
|
294
|
+
{ cmd: "spike", desc: "Research, prototype, and document findings" },
|
|
295
|
+
{ cmd: "hotfix", desc: "Minimal: fix it, test it, ship it" },
|
|
296
|
+
{ cmd: "refactor", desc: "Inventory, plan waves, migrate, verify" },
|
|
297
|
+
{ cmd: "security-audit", desc: "Scan, triage, remediate, re-scan" },
|
|
298
|
+
{ cmd: "dep-upgrade", desc: "Assess, upgrade, fix breaks, verify" },
|
|
299
|
+
{ cmd: "full-project", desc: "Complete GSD workflow with full ceremony" },
|
|
300
|
+
{ cmd: "resume", desc: "Resume an in-progress workflow" },
|
|
301
|
+
{ cmd: "--list", desc: "List all available templates" },
|
|
302
|
+
{ cmd: "--dry-run", desc: "Preview workflow without executing" },
|
|
303
|
+
];
|
|
304
|
+
return subs
|
|
305
|
+
.filter((s) => s.cmd.startsWith(subPrefix))
|
|
306
|
+
.map((s) => ({ value: `start ${s.cmd}`, label: s.cmd, description: s.desc }));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (parts[0] === "templates" && parts.length <= 2) {
|
|
310
|
+
const subPrefix = parts[1] ?? "";
|
|
311
|
+
const subs = [
|
|
312
|
+
{ cmd: "info", desc: "Show detailed template info" },
|
|
313
|
+
];
|
|
314
|
+
return subs
|
|
315
|
+
.filter((s) => s.cmd.startsWith(subPrefix))
|
|
316
|
+
.map((s) => ({ value: `templates ${s.cmd}`, label: s.cmd, description: s.desc }));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (parts[0] === "templates" && parts[1] === "info" && parts.length <= 3) {
|
|
320
|
+
const namePrefix = parts[2] ?? "";
|
|
321
|
+
return getTemplateCompletions(namePrefix)
|
|
322
|
+
.map((c) => ({ value: `templates ${c.value}`, label: c.label, description: c.description }));
|
|
323
|
+
}
|
|
324
|
+
|
|
285
325
|
if (parts[0] === "doctor") {
|
|
286
326
|
const modePrefix = parts[1] ?? "";
|
|
287
327
|
const modes = [
|
|
@@ -659,7 +699,7 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
659
699
|
|
|
660
700
|
if (trimmed === "new-milestone") {
|
|
661
701
|
const basePath = projectRoot();
|
|
662
|
-
const headlessContextPath = join(basePath, "
|
|
702
|
+
const headlessContextPath = join(gsdRoot(basePath), "runtime", "headless-context.md");
|
|
663
703
|
if (existsSync(headlessContextPath)) {
|
|
664
704
|
const seedContext = readFileSync(headlessContextPath, "utf-8");
|
|
665
705
|
try { unlinkSync(headlessContextPath); } catch { /* non-fatal */ }
|
|
@@ -773,6 +813,17 @@ Examples:
|
|
|
773
813
|
return;
|
|
774
814
|
}
|
|
775
815
|
|
|
816
|
+
// ─── Workflow Templates ────────────────────────────────────────
|
|
817
|
+
if (trimmed === "start" || trimmed.startsWith("start ")) {
|
|
818
|
+
await handleStart(trimmed.replace(/^start\s*/, "").trim(), ctx, pi);
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if (trimmed === "templates" || trimmed.startsWith("templates ")) {
|
|
823
|
+
await handleTemplates(trimmed.replace(/^templates\s*/, "").trim(), ctx);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
|
|
776
827
|
if (trimmed === "") {
|
|
777
828
|
// Bare /gsd defaults to step mode
|
|
778
829
|
await startAuto(ctx, pi, projectRoot(), false, { step: true });
|
|
@@ -791,6 +842,8 @@ function showHelp(ctx: ExtensionCommandContext): void {
|
|
|
791
842
|
const lines = [
|
|
792
843
|
"GSD — Get Shit Done\n",
|
|
793
844
|
"WORKFLOW",
|
|
845
|
+
" /gsd start <tpl> Start a workflow template (bugfix, spike, feature, hotfix, etc.)",
|
|
846
|
+
" /gsd templates List available workflow templates [info <name>]",
|
|
794
847
|
" /gsd Run next unit in step mode (same as /gsd next)",
|
|
795
848
|
" /gsd next Execute next task, then pause [--dry-run] [--verbose]",
|
|
796
849
|
" /gsd auto Run all queued units continuously [--verbose]",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
|
+
import { gsdRoot } from "./paths.js";
|
|
12
13
|
|
|
13
14
|
// ─── Types ──────────────────────────────────────────────────────────────────────
|
|
14
15
|
|
|
@@ -214,7 +215,7 @@ export function detectV1Planning(basePath: string): V1Detection | null {
|
|
|
214
215
|
// ─── V2 GSD Detection ──────────────────────────────────────────────────────────
|
|
215
216
|
|
|
216
217
|
function detectV2Gsd(basePath: string): V2Detection | null {
|
|
217
|
-
const gsdPath =
|
|
218
|
+
const gsdPath = gsdRoot(basePath);
|
|
218
219
|
|
|
219
220
|
if (!existsSync(gsdPath)) return null;
|
|
220
221
|
|