gsd-pi 2.28.0-dev.e19bf89 → 2.29.0-dev.2ccf3fb
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -17
- package/dist/cli.js +15 -9
- package/dist/resource-loader.js +80 -8
- package/dist/resources/extensions/bg-shell/process-manager.ts +13 -0
- package/dist/resources/extensions/gsd/auto-dashboard.ts +186 -65
- package/dist/resources/extensions/gsd/auto-post-unit.ts +14 -6
- package/dist/resources/extensions/gsd/auto-recovery.ts +33 -23
- package/dist/resources/extensions/gsd/auto-start.ts +25 -10
- package/dist/resources/extensions/gsd/auto-verification.ts +41 -7
- package/dist/resources/extensions/gsd/auto-worktree-sync.ts +21 -6
- package/dist/resources/extensions/gsd/auto.ts +67 -22
- package/dist/resources/extensions/gsd/commands-handlers.ts +3 -11
- package/dist/resources/extensions/gsd/commands-logs.ts +536 -0
- package/dist/resources/extensions/gsd/commands-prefs-wizard.ts +90 -47
- package/dist/resources/extensions/gsd/commands-workflow-templates.ts +544 -0
- package/dist/resources/extensions/gsd/commands.ts +75 -29
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +2 -1
- package/dist/resources/extensions/gsd/doctor-types.ts +13 -0
- package/dist/resources/extensions/gsd/doctor.ts +2 -6
- package/dist/resources/extensions/gsd/export.ts +28 -2
- package/dist/resources/extensions/gsd/gsd-db.ts +19 -0
- package/dist/resources/extensions/gsd/index.ts +2 -1
- package/dist/resources/extensions/gsd/json-persistence.ts +67 -0
- package/dist/resources/extensions/gsd/metrics.ts +17 -31
- package/dist/resources/extensions/gsd/paths.ts +0 -8
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +28 -0
- package/dist/resources/extensions/gsd/queue-order.ts +10 -11
- package/dist/resources/extensions/gsd/routing-history.ts +13 -17
- package/dist/resources/extensions/gsd/session-lock.ts +284 -0
- package/dist/resources/extensions/gsd/session-status-io.ts +23 -41
- package/dist/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/auto-skip-loop.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/commands-logs.test.ts +241 -0
- package/dist/resources/extensions/gsd/tests/extension-selector-separator.test.ts +60 -38
- package/dist/resources/extensions/gsd/tests/gsd-inspect.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/session-lock.test.ts +315 -0
- package/dist/resources/extensions/gsd/tests/validate-milestone.test.ts +55 -0
- package/dist/resources/extensions/gsd/tests/verification-evidence.test.ts +26 -24
- package/dist/resources/extensions/gsd/tests/verification-gate.test.ts +136 -7
- package/dist/resources/extensions/gsd/tests/workflow-templates.test.ts +173 -0
- package/dist/resources/extensions/gsd/types.ts +1 -0
- package/dist/resources/extensions/gsd/unit-runtime.ts +16 -13
- package/dist/resources/extensions/gsd/verification-evidence.ts +2 -0
- package/dist/resources/extensions/gsd/verification-gate.ts +13 -2
- package/dist/resources/extensions/gsd/workflow-templates/bugfix.md +87 -0
- package/dist/resources/extensions/gsd/workflow-templates/dep-upgrade.md +74 -0
- package/dist/resources/extensions/gsd/workflow-templates/full-project.md +41 -0
- package/dist/resources/extensions/gsd/workflow-templates/hotfix.md +45 -0
- package/dist/resources/extensions/gsd/workflow-templates/refactor.md +83 -0
- package/dist/resources/extensions/gsd/workflow-templates/registry.json +85 -0
- package/dist/resources/extensions/gsd/workflow-templates/security-audit.md +73 -0
- package/dist/resources/extensions/gsd/workflow-templates/small-feature.md +81 -0
- package/dist/resources/extensions/gsd/workflow-templates/spike.md +69 -0
- package/dist/resources/extensions/gsd/workflow-templates.ts +241 -0
- package/dist/resources/extensions/mcp-client/index.ts +459 -0
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +9 -20
- package/dist/resources/extensions/remote-questions/http-client.ts +76 -0
- package/dist/resources/extensions/remote-questions/notify.ts +1 -2
- package/dist/resources/extensions/remote-questions/slack-adapter.ts +11 -18
- package/dist/resources/extensions/remote-questions/telegram-adapter.ts +8 -20
- package/dist/resources/extensions/remote-questions/types.ts +3 -0
- package/dist/resources/extensions/shared/mod.ts +3 -0
- package/dist/resources/skills/create-gsd-extension/SKILL.md +87 -0
- package/dist/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
- package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
- package/dist/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
- package/dist/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
- package/dist/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
- package/dist/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
- package/dist/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
- package/dist/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
- package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
- package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
- package/dist/resources/skills/create-gsd-extension/references/state-management.md +70 -0
- package/dist/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
- package/dist/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
- package/dist/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
- package/dist/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
- package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
- package/dist/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
- package/dist/resources/skills/create-skill/SKILL.md +184 -0
- package/dist/resources/skills/create-skill/references/api-security.md +226 -0
- package/dist/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
- package/dist/resources/skills/create-skill/references/common-patterns.md +595 -0
- package/dist/resources/skills/create-skill/references/core-principles.md +437 -0
- package/dist/resources/skills/create-skill/references/executable-code.md +175 -0
- package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
- package/dist/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
- package/dist/resources/skills/create-skill/references/recommended-structure.md +168 -0
- package/dist/resources/skills/create-skill/references/skill-structure.md +372 -0
- package/dist/resources/skills/create-skill/references/use-xml-tags.md +466 -0
- package/dist/resources/skills/create-skill/references/using-scripts.md +113 -0
- package/dist/resources/skills/create-skill/references/using-templates.md +112 -0
- package/dist/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
- package/dist/resources/skills/create-skill/templates/router-skill.md +73 -0
- package/dist/resources/skills/create-skill/templates/simple-skill.md +33 -0
- package/dist/resources/skills/create-skill/workflows/add-reference.md +96 -0
- package/dist/resources/skills/create-skill/workflows/add-script.md +93 -0
- package/dist/resources/skills/create-skill/workflows/add-template.md +74 -0
- package/dist/resources/skills/create-skill/workflows/add-workflow.md +120 -0
- package/dist/resources/skills/create-skill/workflows/audit-skill.md +148 -0
- package/dist/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
- package/dist/resources/skills/create-skill/workflows/get-guidance.md +121 -0
- package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
- package/dist/resources/skills/create-skill/workflows/verify-skill.md +204 -0
- package/package.json +6 -3
- package/packages/native/dist/native.d.ts +2 -0
- package/packages/native/dist/native.js +19 -5
- package/packages/native/src/native.ts +23 -9
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +13 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +10 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/scripts/copy-assets.cjs +39 -8
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +13 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +11 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +11 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +4 -1
- package/packages/pi-tui/dist/autocomplete.d.ts +3 -0
- package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
- package/packages/pi-tui/dist/autocomplete.js +14 -0
- package/packages/pi-tui/dist/autocomplete.js.map +1 -1
- package/packages/pi-tui/src/autocomplete.ts +19 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/bg-shell/process-manager.ts +13 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +186 -65
- package/src/resources/extensions/gsd/auto-post-unit.ts +14 -6
- package/src/resources/extensions/gsd/auto-recovery.ts +33 -23
- package/src/resources/extensions/gsd/auto-start.ts +25 -10
- package/src/resources/extensions/gsd/auto-verification.ts +41 -7
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +21 -6
- package/src/resources/extensions/gsd/auto.ts +67 -22
- package/src/resources/extensions/gsd/commands-handlers.ts +3 -11
- package/src/resources/extensions/gsd/commands-logs.ts +536 -0
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +90 -47
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +544 -0
- package/src/resources/extensions/gsd/commands.ts +75 -29
- package/src/resources/extensions/gsd/dashboard-overlay.ts +2 -1
- package/src/resources/extensions/gsd/doctor-types.ts +13 -0
- package/src/resources/extensions/gsd/doctor.ts +2 -6
- package/src/resources/extensions/gsd/export.ts +28 -2
- package/src/resources/extensions/gsd/gsd-db.ts +19 -0
- package/src/resources/extensions/gsd/index.ts +2 -1
- package/src/resources/extensions/gsd/json-persistence.ts +67 -0
- package/src/resources/extensions/gsd/metrics.ts +17 -31
- package/src/resources/extensions/gsd/paths.ts +0 -8
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/workflow-start.md +28 -0
- package/src/resources/extensions/gsd/queue-order.ts +10 -11
- package/src/resources/extensions/gsd/routing-history.ts +13 -17
- package/src/resources/extensions/gsd/session-lock.ts +284 -0
- package/src/resources/extensions/gsd/session-status-io.ts +23 -41
- package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-skip-loop.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/commands-logs.test.ts +241 -0
- package/src/resources/extensions/gsd/tests/extension-selector-separator.test.ts +60 -38
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/session-lock.test.ts +315 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +26 -24
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +136 -7
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +173 -0
- package/src/resources/extensions/gsd/types.ts +1 -0
- package/src/resources/extensions/gsd/unit-runtime.ts +16 -13
- package/src/resources/extensions/gsd/verification-evidence.ts +2 -0
- package/src/resources/extensions/gsd/verification-gate.ts +13 -2
- package/src/resources/extensions/gsd/workflow-templates/bugfix.md +87 -0
- package/src/resources/extensions/gsd/workflow-templates/dep-upgrade.md +74 -0
- package/src/resources/extensions/gsd/workflow-templates/full-project.md +41 -0
- package/src/resources/extensions/gsd/workflow-templates/hotfix.md +45 -0
- package/src/resources/extensions/gsd/workflow-templates/refactor.md +83 -0
- package/src/resources/extensions/gsd/workflow-templates/registry.json +85 -0
- package/src/resources/extensions/gsd/workflow-templates/security-audit.md +73 -0
- package/src/resources/extensions/gsd/workflow-templates/small-feature.md +81 -0
- package/src/resources/extensions/gsd/workflow-templates/spike.md +69 -0
- package/src/resources/extensions/gsd/workflow-templates.ts +241 -0
- package/src/resources/extensions/mcp-client/index.ts +459 -0
- package/src/resources/extensions/remote-questions/discord-adapter.ts +9 -20
- package/src/resources/extensions/remote-questions/http-client.ts +76 -0
- package/src/resources/extensions/remote-questions/notify.ts +1 -2
- package/src/resources/extensions/remote-questions/slack-adapter.ts +11 -18
- package/src/resources/extensions/remote-questions/telegram-adapter.ts +8 -20
- package/src/resources/extensions/remote-questions/types.ts +3 -0
- package/src/resources/extensions/shared/mod.ts +3 -0
- package/src/resources/skills/create-gsd-extension/SKILL.md +87 -0
- package/src/resources/skills/create-gsd-extension/references/compaction-session-control.md +77 -0
- package/src/resources/skills/create-gsd-extension/references/custom-commands.md +139 -0
- package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +108 -0
- package/src/resources/skills/create-gsd-extension/references/custom-tools.md +183 -0
- package/src/resources/skills/create-gsd-extension/references/custom-ui.md +490 -0
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +126 -0
- package/src/resources/skills/create-gsd-extension/references/extension-lifecycle.md +64 -0
- package/src/resources/skills/create-gsd-extension/references/extensionapi-reference.md +75 -0
- package/src/resources/skills/create-gsd-extension/references/extensioncontext-reference.md +53 -0
- package/src/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +36 -0
- package/src/resources/skills/create-gsd-extension/references/mode-behavior.md +32 -0
- package/src/resources/skills/create-gsd-extension/references/model-provider-management.md +89 -0
- package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +55 -0
- package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +90 -0
- package/src/resources/skills/create-gsd-extension/references/state-management.md +70 -0
- package/src/resources/skills/create-gsd-extension/references/system-prompt-modification.md +52 -0
- package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +51 -0
- package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +143 -0
- package/src/resources/skills/create-gsd-extension/workflows/add-capability.md +57 -0
- package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +156 -0
- package/src/resources/skills/create-gsd-extension/workflows/debug-extension.md +74 -0
- package/src/resources/skills/create-skill/SKILL.md +184 -0
- package/src/resources/skills/create-skill/references/api-security.md +226 -0
- package/src/resources/skills/create-skill/references/be-clear-and-direct.md +531 -0
- package/src/resources/skills/create-skill/references/common-patterns.md +595 -0
- package/src/resources/skills/create-skill/references/core-principles.md +437 -0
- package/src/resources/skills/create-skill/references/executable-code.md +175 -0
- package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +68 -0
- package/src/resources/skills/create-skill/references/iteration-and-testing.md +474 -0
- package/src/resources/skills/create-skill/references/recommended-structure.md +168 -0
- package/src/resources/skills/create-skill/references/skill-structure.md +372 -0
- package/src/resources/skills/create-skill/references/use-xml-tags.md +466 -0
- package/src/resources/skills/create-skill/references/using-scripts.md +113 -0
- package/src/resources/skills/create-skill/references/using-templates.md +112 -0
- package/src/resources/skills/create-skill/references/workflows-and-validation.md +510 -0
- package/src/resources/skills/create-skill/templates/router-skill.md +73 -0
- package/src/resources/skills/create-skill/templates/simple-skill.md +33 -0
- package/src/resources/skills/create-skill/workflows/add-reference.md +96 -0
- package/src/resources/skills/create-skill/workflows/add-script.md +93 -0
- package/src/resources/skills/create-skill/workflows/add-template.md +74 -0
- package/src/resources/skills/create-skill/workflows/add-workflow.md +120 -0
- package/src/resources/skills/create-skill/workflows/audit-skill.md +148 -0
- package/src/resources/skills/create-skill/workflows/create-new-skill.md +196 -0
- package/src/resources/skills/create-skill/workflows/get-guidance.md +121 -0
- package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +161 -0
- package/src/resources/skills/create-skill/workflows/verify-skill.md +204 -0
- package/dist/resources/extensions/gsd/preferences-hooks.ts +0 -10
- package/dist/resources/extensions/mcporter/index.ts +0 -525
- package/dist/resources/extensions/shared/progress-widget.ts +0 -282
- package/dist/resources/extensions/shared/thinking-widget.ts +0 -107
- package/src/resources/extensions/gsd/preferences-hooks.ts +0 -10
- package/src/resources/extensions/mcporter/index.ts +0 -525
- package/src/resources/extensions/shared/progress-widget.ts +0 -282
- package/src/resources/extensions/shared/thinking-widget.ts +0 -107
|
@@ -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
|
+
}
|