gsd-pi 2.29.0-dev.77f06e2 → 2.29.0-dev.f08b4fe
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/dist/extension-registry.d.ts +63 -0
- package/dist/extension-registry.js +166 -0
- package/dist/headless.js +4 -0
- package/dist/loader.js +10 -1
- package/dist/resource-loader.js +11 -1
- package/dist/resources/extensions/async-jobs/extension-manifest.json +13 -0
- package/dist/resources/extensions/bg-shell/extension-manifest.json +14 -0
- package/dist/resources/extensions/browser-tools/extension-manifest.json +37 -0
- package/dist/resources/extensions/context7/extension-manifest.json +12 -0
- package/dist/resources/extensions/google-search/extension-manifest.json +12 -0
- package/dist/resources/extensions/gsd/auto-dashboard.ts +31 -0
- package/dist/resources/extensions/gsd/auto-dispatch.ts +32 -3
- package/dist/resources/extensions/gsd/auto-post-unit.ts +39 -10
- 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-extensions.ts +328 -0
- package/dist/resources/extensions/gsd/commands-handlers.ts +2 -1
- package/dist/resources/extensions/gsd/commands.ts +53 -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/extension-manifest.json +18 -0
- 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/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/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/triage-resolution.ts +2 -1
- package/dist/resources/extensions/gsd/types.ts +2 -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/mac-tools/extension-manifest.json +16 -0
- package/dist/resources/extensions/mcporter/extension-manifest.json +12 -0
- package/dist/resources/extensions/remote-questions/extension-manifest.json +11 -0
- package/dist/resources/extensions/search-the-web/extension-manifest.json +13 -0
- package/dist/resources/extensions/slash-commands/extension-manifest.json +11 -0
- package/dist/resources/extensions/subagent/extension-manifest.json +13 -0
- package/dist/resources/extensions/ttsr/extension-manifest.json +11 -0
- package/dist/resources/extensions/universal-config/extension-manifest.json +13 -0
- package/dist/resources/extensions/voice/extension-manifest.json +12 -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/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/lsp/client.ts +3 -0
- package/src/resources/extensions/async-jobs/extension-manifest.json +13 -0
- package/src/resources/extensions/bg-shell/extension-manifest.json +14 -0
- package/src/resources/extensions/browser-tools/extension-manifest.json +37 -0
- package/src/resources/extensions/context7/extension-manifest.json +12 -0
- package/src/resources/extensions/google-search/extension-manifest.json +12 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +31 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +32 -3
- package/src/resources/extensions/gsd/auto-post-unit.ts +39 -10
- 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-extensions.ts +328 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +2 -1
- package/src/resources/extensions/gsd/commands.ts +53 -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/extension-manifest.json +18 -0
- 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/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/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/triage-resolution.ts +2 -1
- package/src/resources/extensions/gsd/types.ts +2 -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/mac-tools/extension-manifest.json +16 -0
- package/src/resources/extensions/mcporter/extension-manifest.json +12 -0
- package/src/resources/extensions/remote-questions/extension-manifest.json +11 -0
- package/src/resources/extensions/search-the-web/extension-manifest.json +13 -0
- package/src/resources/extensions/slash-commands/extension-manifest.json +11 -0
- package/src/resources/extensions/subagent/extension-manifest.json +13 -0
- package/src/resources/extensions/ttsr/extension-manifest.json +11 -0
- package/src/resources/extensions/universal-config/extension-manifest.json +13 -0
- package/src/resources/extensions/voice/extension-manifest.json +12 -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/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
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSD Extensions Command — /gsd extensions
|
|
3
|
+
*
|
|
4
|
+
* Manage the extension registry: list, enable, disable, info.
|
|
5
|
+
* Self-contained — no imports outside the extensions tree (extensions are loaded
|
|
6
|
+
* via jiti at runtime from ~/.gsd/agent/, not compiled by tsc).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
|
|
14
|
+
// ─── Types (mirrored from extension-registry.ts) ────────────────────────────
|
|
15
|
+
|
|
16
|
+
interface ExtensionManifest {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
version: string;
|
|
20
|
+
description: string;
|
|
21
|
+
tier: "core" | "bundled" | "community";
|
|
22
|
+
requires: { platform: string };
|
|
23
|
+
provides?: {
|
|
24
|
+
tools?: string[];
|
|
25
|
+
commands?: string[];
|
|
26
|
+
hooks?: string[];
|
|
27
|
+
shortcuts?: string[];
|
|
28
|
+
};
|
|
29
|
+
dependencies?: {
|
|
30
|
+
extensions?: string[];
|
|
31
|
+
runtime?: string[];
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface ExtensionRegistryEntry {
|
|
36
|
+
id: string;
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
source: "bundled" | "user" | "project";
|
|
39
|
+
disabledAt?: string;
|
|
40
|
+
disabledReason?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface ExtensionRegistry {
|
|
44
|
+
version: 1;
|
|
45
|
+
entries: Record<string, ExtensionRegistryEntry>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ─── Registry I/O ───────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
function getRegistryPath(): string {
|
|
51
|
+
return join(homedir(), ".gsd", "extensions", "registry.json");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getAgentExtensionsDir(): string {
|
|
55
|
+
return join(homedir(), ".gsd", "agent", "extensions");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function loadRegistry(): ExtensionRegistry {
|
|
59
|
+
const filePath = getRegistryPath();
|
|
60
|
+
try {
|
|
61
|
+
if (!existsSync(filePath)) return { version: 1, entries: {} };
|
|
62
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
63
|
+
const parsed = JSON.parse(raw);
|
|
64
|
+
if (typeof parsed === "object" && parsed !== null && parsed.version === 1 && typeof parsed.entries === "object") {
|
|
65
|
+
return parsed as ExtensionRegistry;
|
|
66
|
+
}
|
|
67
|
+
return { version: 1, entries: {} };
|
|
68
|
+
} catch {
|
|
69
|
+
return { version: 1, entries: {} };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function saveRegistry(registry: ExtensionRegistry): void {
|
|
74
|
+
const filePath = getRegistryPath();
|
|
75
|
+
try {
|
|
76
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
77
|
+
const tmp = filePath + ".tmp";
|
|
78
|
+
writeFileSync(tmp, JSON.stringify(registry, null, 2), "utf-8");
|
|
79
|
+
renameSync(tmp, filePath);
|
|
80
|
+
} catch { /* non-fatal */ }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isEnabled(registry: ExtensionRegistry, id: string): boolean {
|
|
84
|
+
const entry = registry.entries[id];
|
|
85
|
+
if (!entry) return true;
|
|
86
|
+
return entry.enabled;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function readManifest(dir: string): ExtensionManifest | null {
|
|
90
|
+
const mPath = join(dir, "extension-manifest.json");
|
|
91
|
+
if (!existsSync(mPath)) return null;
|
|
92
|
+
try {
|
|
93
|
+
const raw = JSON.parse(readFileSync(mPath, "utf-8"));
|
|
94
|
+
if (typeof raw?.id === "string" && typeof raw?.name === "string") return raw as ExtensionManifest;
|
|
95
|
+
return null;
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function discoverManifests(): Map<string, ExtensionManifest> {
|
|
102
|
+
const extDir = getAgentExtensionsDir();
|
|
103
|
+
const manifests = new Map<string, ExtensionManifest>();
|
|
104
|
+
if (!existsSync(extDir)) return manifests;
|
|
105
|
+
for (const entry of readdirSync(extDir, { withFileTypes: true })) {
|
|
106
|
+
if (!entry.isDirectory()) continue;
|
|
107
|
+
const m = readManifest(join(extDir, entry.name));
|
|
108
|
+
if (m) manifests.set(m.id, m);
|
|
109
|
+
}
|
|
110
|
+
return manifests;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ─── Command Handler ────────────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
export async function handleExtensions(args: string, ctx: ExtensionCommandContext): Promise<void> {
|
|
116
|
+
const parts = args.split(/\s+/).filter(Boolean);
|
|
117
|
+
const subCmd = parts[0] ?? "list";
|
|
118
|
+
|
|
119
|
+
if (subCmd === "list") {
|
|
120
|
+
handleList(ctx);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (subCmd === "enable") {
|
|
125
|
+
handleEnable(parts[1], ctx);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (subCmd === "disable") {
|
|
130
|
+
handleDisable(parts[1], parts.slice(2).join(" "), ctx);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (subCmd === "info") {
|
|
135
|
+
handleInfo(parts[1], ctx);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
ctx.ui.notify(
|
|
140
|
+
`Unknown: /gsd extensions ${subCmd}. Usage: /gsd extensions [list|enable|disable|info]`,
|
|
141
|
+
"warning",
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function handleList(ctx: ExtensionCommandContext): void {
|
|
146
|
+
const manifests = discoverManifests();
|
|
147
|
+
const registry = loadRegistry();
|
|
148
|
+
|
|
149
|
+
if (manifests.size === 0) {
|
|
150
|
+
ctx.ui.notify("No extension manifests found.", "warning");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Sort: core first, then alphabetical
|
|
155
|
+
const sorted = [...manifests.values()].sort((a, b) => {
|
|
156
|
+
if (a.tier === "core" && b.tier !== "core") return -1;
|
|
157
|
+
if (b.tier === "core" && a.tier !== "core") return 1;
|
|
158
|
+
return a.id.localeCompare(b.id);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const lines: string[] = [];
|
|
162
|
+
const hdr = padRight("Extensions", 38) + padRight("Status", 10) + padRight("Tier", 10) + padRight("Tools", 7) + "Commands";
|
|
163
|
+
lines.push(hdr);
|
|
164
|
+
lines.push("─".repeat(hdr.length));
|
|
165
|
+
|
|
166
|
+
for (const m of sorted) {
|
|
167
|
+
const enabled = isEnabled(registry, m.id);
|
|
168
|
+
const status = enabled ? "enabled" : "disabled";
|
|
169
|
+
const toolCount = m.provides?.tools?.length ?? 0;
|
|
170
|
+
const cmdCount = m.provides?.commands?.length ?? 0;
|
|
171
|
+
const label = `${m.id} (${m.name})`;
|
|
172
|
+
|
|
173
|
+
lines.push(
|
|
174
|
+
padRight(label, 38) +
|
|
175
|
+
padRight(status, 10) +
|
|
176
|
+
padRight(m.tier, 10) +
|
|
177
|
+
padRight(String(toolCount), 7) +
|
|
178
|
+
String(cmdCount),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
if (!enabled) {
|
|
182
|
+
lines.push(` ↳ gsd extensions enable ${m.id}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function handleEnable(id: string | undefined, ctx: ExtensionCommandContext): void {
|
|
190
|
+
if (!id) {
|
|
191
|
+
ctx.ui.notify("Usage: /gsd extensions enable <id>", "warning");
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const manifests = discoverManifests();
|
|
196
|
+
if (!manifests.has(id)) {
|
|
197
|
+
ctx.ui.notify(`Extension "${id}" not found. Run /gsd extensions list to see available extensions.`, "warning");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const registry = loadRegistry();
|
|
202
|
+
if (isEnabled(registry, id)) {
|
|
203
|
+
ctx.ui.notify(`Extension "${id}" is already enabled.`, "info");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const entry = registry.entries[id];
|
|
208
|
+
if (entry) {
|
|
209
|
+
entry.enabled = true;
|
|
210
|
+
delete entry.disabledAt;
|
|
211
|
+
delete entry.disabledReason;
|
|
212
|
+
} else {
|
|
213
|
+
registry.entries[id] = { id, enabled: true, source: "bundled" };
|
|
214
|
+
}
|
|
215
|
+
saveRegistry(registry);
|
|
216
|
+
ctx.ui.notify(`Enabled "${id}". Restart GSD to activate.`, "info");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function handleDisable(id: string | undefined, reason: string, ctx: ExtensionCommandContext): void {
|
|
220
|
+
if (!id) {
|
|
221
|
+
ctx.ui.notify("Usage: /gsd extensions disable <id>", "warning");
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const manifests = discoverManifests();
|
|
226
|
+
const manifest = manifests.get(id) ?? null;
|
|
227
|
+
|
|
228
|
+
if (!manifests.has(id)) {
|
|
229
|
+
ctx.ui.notify(`Extension "${id}" not found. Run /gsd extensions list to see available extensions.`, "warning");
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (manifest?.tier === "core") {
|
|
234
|
+
ctx.ui.notify(`Cannot disable "${id}" — it is a core extension.`, "warning");
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const registry = loadRegistry();
|
|
239
|
+
if (!isEnabled(registry, id)) {
|
|
240
|
+
ctx.ui.notify(`Extension "${id}" is already disabled.`, "info");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const entry = registry.entries[id];
|
|
245
|
+
if (entry) {
|
|
246
|
+
entry.enabled = false;
|
|
247
|
+
entry.disabledAt = new Date().toISOString();
|
|
248
|
+
entry.disabledReason = reason || undefined;
|
|
249
|
+
} else {
|
|
250
|
+
registry.entries[id] = {
|
|
251
|
+
id,
|
|
252
|
+
enabled: false,
|
|
253
|
+
source: "bundled",
|
|
254
|
+
disabledAt: new Date().toISOString(),
|
|
255
|
+
disabledReason: reason || undefined,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
saveRegistry(registry);
|
|
259
|
+
ctx.ui.notify(`Disabled "${id}". Restart GSD to deactivate.`, "info");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function handleInfo(id: string | undefined, ctx: ExtensionCommandContext): void {
|
|
263
|
+
if (!id) {
|
|
264
|
+
ctx.ui.notify("Usage: /gsd extensions info <id>", "warning");
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const manifests = discoverManifests();
|
|
269
|
+
const manifest = manifests.get(id);
|
|
270
|
+
if (!manifest) {
|
|
271
|
+
ctx.ui.notify(`Extension "${id}" not found.`, "warning");
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const registry = loadRegistry();
|
|
276
|
+
const enabled = isEnabled(registry, id);
|
|
277
|
+
const entry = registry.entries[id];
|
|
278
|
+
|
|
279
|
+
const lines: string[] = [
|
|
280
|
+
`${manifest.name} (${manifest.id})`,
|
|
281
|
+
"",
|
|
282
|
+
` Version: ${manifest.version}`,
|
|
283
|
+
` Description: ${manifest.description}`,
|
|
284
|
+
` Tier: ${manifest.tier}`,
|
|
285
|
+
` Status: ${enabled ? "enabled" : "disabled"}`,
|
|
286
|
+
];
|
|
287
|
+
|
|
288
|
+
if (entry?.disabledAt) {
|
|
289
|
+
lines.push(` Disabled at: ${entry.disabledAt}`);
|
|
290
|
+
}
|
|
291
|
+
if (entry?.disabledReason) {
|
|
292
|
+
lines.push(` Reason: ${entry.disabledReason}`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (manifest.provides) {
|
|
296
|
+
lines.push("");
|
|
297
|
+
lines.push(" Provides:");
|
|
298
|
+
if (manifest.provides.tools?.length) {
|
|
299
|
+
lines.push(` Tools: ${manifest.provides.tools.join(", ")}`);
|
|
300
|
+
}
|
|
301
|
+
if (manifest.provides.commands?.length) {
|
|
302
|
+
lines.push(` Commands: ${manifest.provides.commands.join(", ")}`);
|
|
303
|
+
}
|
|
304
|
+
if (manifest.provides.hooks?.length) {
|
|
305
|
+
lines.push(` Hooks: ${manifest.provides.hooks.join(", ")}`);
|
|
306
|
+
}
|
|
307
|
+
if (manifest.provides.shortcuts?.length) {
|
|
308
|
+
lines.push(` Shortcuts: ${manifest.provides.shortcuts.join(", ")}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (manifest.dependencies) {
|
|
313
|
+
lines.push("");
|
|
314
|
+
lines.push(" Dependencies:");
|
|
315
|
+
if (manifest.dependencies.extensions?.length) {
|
|
316
|
+
lines.push(` Extensions: ${manifest.dependencies.extensions.join(", ")}`);
|
|
317
|
+
}
|
|
318
|
+
if (manifest.dependencies.runtime?.length) {
|
|
319
|
+
lines.push(` Runtime: ${manifest.dependencies.runtime.join(", ")}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function padRight(str: string, len: number): string {
|
|
327
|
+
return str.length >= len ? str + " " : str + " ".repeat(len - str.length);
|
|
328
|
+
}
|
|
@@ -9,6 +9,7 @@ import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent
|
|
|
9
9
|
import { existsSync, readFileSync, mkdirSync } from "node:fs";
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import { deriveState } from "./state.js";
|
|
12
|
+
import { gsdRoot } from "./paths.js";
|
|
12
13
|
import { appendCapture, hasPendingCaptures, loadPendingCaptures } from "./captures.js";
|
|
13
14
|
import { appendOverride, appendKnowledge } from "./files.js";
|
|
14
15
|
import {
|
|
@@ -136,7 +137,7 @@ export async function handleCapture(args: string, ctx: ExtensionCommandContext):
|
|
|
136
137
|
const basePath = process.cwd();
|
|
137
138
|
|
|
138
139
|
// Ensure .gsd/ exists — capture should work even without a milestone
|
|
139
|
-
const gsdDir =
|
|
140
|
+
const gsdDir = gsdRoot(basePath);
|
|
140
141
|
if (!existsSync(gsdDir)) {
|
|
141
142
|
mkdirSync(gsdDir, { recursive: true });
|
|
142
143
|
}
|
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
8
8
|
import type { GSDState } from "./types.js";
|
|
9
|
-
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
9
|
+
import { existsSync, readFileSync, readdirSync, unlinkSync } from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
10
11
|
import { join } from "node:path";
|
|
12
|
+
import { gsdRoot } from "./paths.js";
|
|
11
13
|
import { enableDebug } from "./debug-logger.js";
|
|
12
14
|
import { deriveState } from "./state.js";
|
|
13
15
|
import { GSDDashboardOverlay } from "./dashboard-overlay.js";
|
|
@@ -100,6 +102,7 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
100
102
|
{ cmd: "update", desc: "Update GSD to the latest version" },
|
|
101
103
|
{ cmd: "start", desc: "Start a workflow template (bugfix, spike, feature, etc.)" },
|
|
102
104
|
{ cmd: "templates", desc: "List available workflow templates" },
|
|
105
|
+
{ cmd: "extensions", desc: "Manage extensions (list, enable, disable, info)" },
|
|
103
106
|
];
|
|
104
107
|
const parts = prefix.trim().split(/\s+/);
|
|
105
108
|
|
|
@@ -321,6 +324,47 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
321
324
|
.map((c) => ({ value: `templates ${c.value}`, label: c.label, description: c.description }));
|
|
322
325
|
}
|
|
323
326
|
|
|
327
|
+
if (parts[0] === "extensions") {
|
|
328
|
+
if (parts.length <= 2) {
|
|
329
|
+
const subPrefix = parts[1] ?? "";
|
|
330
|
+
const subs = [
|
|
331
|
+
{ cmd: "list", desc: "List all extensions and their status" },
|
|
332
|
+
{ cmd: "enable", desc: "Enable a disabled extension" },
|
|
333
|
+
{ cmd: "disable", desc: "Disable an extension" },
|
|
334
|
+
{ cmd: "info", desc: "Show extension details" },
|
|
335
|
+
];
|
|
336
|
+
return subs
|
|
337
|
+
.filter((s) => s.cmd.startsWith(subPrefix))
|
|
338
|
+
.map((s) => ({ value: `extensions ${s.cmd}`, label: s.cmd, description: s.desc }));
|
|
339
|
+
}
|
|
340
|
+
if (parts.length === 3 && ["enable", "disable", "info"].includes(parts[1])) {
|
|
341
|
+
const idPrefix = parts[2] ?? "";
|
|
342
|
+
try {
|
|
343
|
+
const extDir = join(homedir(), ".gsd", "agent", "extensions");
|
|
344
|
+
const ids: { id: string; name: string }[] = [];
|
|
345
|
+
for (const entry of readdirSync(extDir, { withFileTypes: true })) {
|
|
346
|
+
if (!entry.isDirectory()) continue;
|
|
347
|
+
const mPath = join(extDir, entry.name, "extension-manifest.json");
|
|
348
|
+
if (!existsSync(mPath)) continue;
|
|
349
|
+
try {
|
|
350
|
+
const m = JSON.parse(readFileSync(mPath, "utf-8"));
|
|
351
|
+
if (typeof m?.id === "string") ids.push({ id: m.id, name: m.name ?? m.id });
|
|
352
|
+
} catch { /* skip malformed */ }
|
|
353
|
+
}
|
|
354
|
+
return ids
|
|
355
|
+
.filter((e) => e.id.startsWith(idPrefix))
|
|
356
|
+
.map((e) => ({
|
|
357
|
+
value: `extensions ${parts[1]} ${e.id}`,
|
|
358
|
+
label: e.id,
|
|
359
|
+
description: e.name,
|
|
360
|
+
}));
|
|
361
|
+
} catch {
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return [];
|
|
366
|
+
}
|
|
367
|
+
|
|
324
368
|
if (parts[0] === "doctor") {
|
|
325
369
|
const modePrefix = parts[1] ?? "";
|
|
326
370
|
const modes = [
|
|
@@ -698,7 +742,7 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
698
742
|
|
|
699
743
|
if (trimmed === "new-milestone") {
|
|
700
744
|
const basePath = projectRoot();
|
|
701
|
-
const headlessContextPath = join(basePath, "
|
|
745
|
+
const headlessContextPath = join(gsdRoot(basePath), "runtime", "headless-context.md");
|
|
702
746
|
if (existsSync(headlessContextPath)) {
|
|
703
747
|
const seedContext = readFileSync(headlessContextPath, "utf-8");
|
|
704
748
|
try { unlinkSync(headlessContextPath); } catch { /* non-fatal */ }
|
|
@@ -829,6 +873,12 @@ Examples:
|
|
|
829
873
|
return;
|
|
830
874
|
}
|
|
831
875
|
|
|
876
|
+
if (trimmed === "extensions" || trimmed.startsWith("extensions ")) {
|
|
877
|
+
const { handleExtensions } = await import("./commands-extensions.js");
|
|
878
|
+
await handleExtensions(trimmed.replace(/^extensions\s*/, "").trim(), ctx);
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
|
|
832
882
|
ctx.ui.notify(
|
|
833
883
|
`Unknown: /gsd ${trimmed}. Run /gsd help for available commands.`,
|
|
834
884
|
"warning",
|
|
@@ -877,6 +927,7 @@ function showHelp(ctx: ExtensionCommandContext): void {
|
|
|
877
927
|
" /gsd config Set API keys for external tools",
|
|
878
928
|
" /gsd keys API key manager [list|add|remove|test|rotate|doctor]",
|
|
879
929
|
" /gsd hooks Show post-unit hook configuration",
|
|
930
|
+
" /gsd extensions Manage extensions [list|enable|disable|info]",
|
|
880
931
|
"",
|
|
881
932
|
"MAINTENANCE",
|
|
882
933
|
" /gsd doctor Diagnose and repair .gsd/ state [audit|fix|heal] [scope]",
|
|
@@ -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
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
1
|
+
import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { join, sep } from "node:path";
|
|
3
3
|
|
|
4
4
|
import type { DoctorIssue, DoctorIssueCode } from "./doctor-types.js";
|
|
@@ -13,6 +13,7 @@ import { nativeIsRepo, nativeWorktreeRemove, nativeBranchList, nativeBranchDelet
|
|
|
13
13
|
import { readCrashLock, isLockProcessAlive, clearLock } from "./crash-recovery.js";
|
|
14
14
|
import { ensureGitignore } from "./gitignore.js";
|
|
15
15
|
import { readAllSessionStatuses, isSessionStale, removeSessionStatus } from "./session-status-io.js";
|
|
16
|
+
import { recoverFailedMigration } from "./migrate-external.js";
|
|
16
17
|
|
|
17
18
|
export async function checkGitHealth(
|
|
18
19
|
basePath: string,
|
|
@@ -508,6 +509,53 @@ export async function checkRuntimeHealth(
|
|
|
508
509
|
} catch {
|
|
509
510
|
// Non-fatal — gitignore check failed
|
|
510
511
|
}
|
|
512
|
+
|
|
513
|
+
// ── External state symlink health ──────────────────────────────────────
|
|
514
|
+
try {
|
|
515
|
+
const localGsd = join(basePath, ".gsd");
|
|
516
|
+
if (existsSync(localGsd)) {
|
|
517
|
+
const stat = lstatSync(localGsd);
|
|
518
|
+
|
|
519
|
+
// Check for .gsd.migrating (failed migration)
|
|
520
|
+
const migratingPath = join(basePath, ".gsd.migrating");
|
|
521
|
+
if (existsSync(migratingPath)) {
|
|
522
|
+
issues.push({
|
|
523
|
+
severity: "error",
|
|
524
|
+
code: "failed_migration",
|
|
525
|
+
scope: "project",
|
|
526
|
+
unitId: "project",
|
|
527
|
+
message: "Found .gsd.migrating — a previous external state migration failed. State may be incomplete.",
|
|
528
|
+
file: ".gsd.migrating",
|
|
529
|
+
fixable: true,
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
if (shouldFix("failed_migration")) {
|
|
533
|
+
if (recoverFailedMigration(basePath)) {
|
|
534
|
+
fixesApplied.push("recovered failed migration (.gsd.migrating → .gsd)");
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Check symlink target exists
|
|
540
|
+
if (stat.isSymbolicLink()) {
|
|
541
|
+
try {
|
|
542
|
+
realpathSync(localGsd);
|
|
543
|
+
} catch {
|
|
544
|
+
issues.push({
|
|
545
|
+
severity: "error",
|
|
546
|
+
code: "broken_symlink",
|
|
547
|
+
scope: "project",
|
|
548
|
+
unitId: "project",
|
|
549
|
+
message: ".gsd symlink target does not exist. External state directory may have been deleted.",
|
|
550
|
+
file: ".gsd",
|
|
551
|
+
fixable: false,
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
} catch {
|
|
557
|
+
// Non-fatal — external state check failed
|
|
558
|
+
}
|
|
511
559
|
}
|
|
512
560
|
|
|
513
561
|
/**
|
|
@@ -30,7 +30,9 @@ export type DoctorIssueCode =
|
|
|
30
30
|
| "state_file_stale"
|
|
31
31
|
| "state_file_missing"
|
|
32
32
|
| "gitignore_missing_patterns"
|
|
33
|
-
| "unresolvable_dependency"
|
|
33
|
+
| "unresolvable_dependency"
|
|
34
|
+
| "failed_migration"
|
|
35
|
+
| "broken_symlink";
|
|
34
36
|
|
|
35
37
|
/**
|
|
36
38
|
* Issue codes that represent expected completion-transition states.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "gsd",
|
|
3
|
+
"name": "GSD Workflow",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Core GSD workflow engine — milestone planning, execution, and tracking",
|
|
6
|
+
"tier": "core",
|
|
7
|
+
"requires": { "platform": ">=2.29.0" },
|
|
8
|
+
"provides": {
|
|
9
|
+
"tools": [
|
|
10
|
+
"bash", "write", "read", "edit",
|
|
11
|
+
"gsd_save_decision", "gsd_save_summary",
|
|
12
|
+
"gsd_update_requirement", "gsd_generate_milestone_id"
|
|
13
|
+
],
|
|
14
|
+
"commands": ["gsd", "kill", "worktree", "exit"],
|
|
15
|
+
"hooks": ["session_start"],
|
|
16
|
+
"shortcuts": ["Ctrl+Alt+G"]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -268,7 +268,7 @@ function resolveActivityDirs(basePath: string, activeMilestone?: string | null):
|
|
|
268
268
|
if (activeMilestone) {
|
|
269
269
|
const wtPath = getAutoWorktreePath(basePath, activeMilestone);
|
|
270
270
|
if (wtPath) {
|
|
271
|
-
const wtActivityDir = join(wtPath, "
|
|
271
|
+
const wtActivityDir = join(gsdRoot(wtPath), "activity");
|
|
272
272
|
if (existsSync(wtActivityDir)) {
|
|
273
273
|
dirs.push(wtActivityDir);
|
|
274
274
|
}
|
|
@@ -285,7 +285,7 @@ function resolveActivityDirs(basePath: string, activeMilestone?: string | null):
|
|
|
285
285
|
// ─── Completed Keys Loader ────────────────────────────────────────────────────
|
|
286
286
|
|
|
287
287
|
function loadCompletedKeys(basePath: string): string[] {
|
|
288
|
-
const file = join(basePath, "
|
|
288
|
+
const file = join(gsdRoot(basePath), "completed-units.json");
|
|
289
289
|
try {
|
|
290
290
|
if (existsSync(file)) {
|
|
291
291
|
return JSON.parse(readFileSync(file, "utf-8"));
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { execFileSync, execSync } from "node:child_process";
|
|
12
12
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
13
|
import { join } from "node:path";
|
|
14
|
+
import { gsdRoot } from "./paths.js";
|
|
14
15
|
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
|
15
16
|
|
|
16
17
|
import {
|
|
@@ -193,7 +194,7 @@ export const RUNTIME_EXCLUSION_PATHS: readonly string[] = [
|
|
|
193
194
|
* Format: .gsd/milestones/<MID>/<MID>-META.json
|
|
194
195
|
*/
|
|
195
196
|
function milestoneMetaPath(basePath: string, milestoneId: string): string {
|
|
196
|
-
return join(basePath, "
|
|
197
|
+
return join(gsdRoot(basePath), "milestones", milestoneId, `${milestoneId}-META.json`);
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
/**
|
|
@@ -237,7 +238,7 @@ export function writeIntegrationBranch(basePath: string, milestoneId: string, br
|
|
|
237
238
|
if (existingBranch === branch) return;
|
|
238
239
|
|
|
239
240
|
const metaFile = milestoneMetaPath(basePath, milestoneId);
|
|
240
|
-
mkdirSync(join(basePath, "
|
|
241
|
+
mkdirSync(join(gsdRoot(basePath), "milestones", milestoneId), { recursive: true });
|
|
241
242
|
|
|
242
243
|
// Merge with existing metadata if present
|
|
243
244
|
let existing: Record<string, unknown> = {};
|