gsdd-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +528 -0
  3. package/agents/DISTILLATION.md +306 -0
  4. package/agents/README.md +53 -0
  5. package/agents/debugger.md +82 -0
  6. package/agents/executor.md +394 -0
  7. package/agents/integration-checker.md +318 -0
  8. package/agents/mapper.md +103 -0
  9. package/agents/planner.md +296 -0
  10. package/agents/researcher.md +84 -0
  11. package/agents/roadmapper.md +296 -0
  12. package/agents/synthesizer.md +236 -0
  13. package/agents/verifier.md +337 -0
  14. package/bin/adapters/agents.mjs +33 -0
  15. package/bin/adapters/claude.mjs +145 -0
  16. package/bin/adapters/codex.mjs +58 -0
  17. package/bin/adapters/index.mjs +20 -0
  18. package/bin/adapters/opencode.mjs +237 -0
  19. package/bin/gsdd.mjs +102 -0
  20. package/bin/lib/cli-utils.mjs +28 -0
  21. package/bin/lib/health.mjs +248 -0
  22. package/bin/lib/init.mjs +379 -0
  23. package/bin/lib/manifest.mjs +134 -0
  24. package/bin/lib/models.mjs +379 -0
  25. package/bin/lib/phase.mjs +237 -0
  26. package/bin/lib/rendering.mjs +95 -0
  27. package/bin/lib/templates.mjs +207 -0
  28. package/distilled/DESIGN.md +1286 -0
  29. package/distilled/README.md +169 -0
  30. package/distilled/SKILL.md +85 -0
  31. package/distilled/templates/agents.block.md +90 -0
  32. package/distilled/templates/agents.md +13 -0
  33. package/distilled/templates/auth-matrix.md +78 -0
  34. package/distilled/templates/codebase/architecture.md +110 -0
  35. package/distilled/templates/codebase/concerns.md +95 -0
  36. package/distilled/templates/codebase/conventions.md +193 -0
  37. package/distilled/templates/codebase/stack.md +96 -0
  38. package/distilled/templates/delegates/mapper-arch.md +26 -0
  39. package/distilled/templates/delegates/mapper-concerns.md +27 -0
  40. package/distilled/templates/delegates/mapper-quality.md +28 -0
  41. package/distilled/templates/delegates/mapper-tech.md +25 -0
  42. package/distilled/templates/delegates/plan-checker.md +55 -0
  43. package/distilled/templates/delegates/researcher-architecture.md +30 -0
  44. package/distilled/templates/delegates/researcher-features.md +30 -0
  45. package/distilled/templates/delegates/researcher-pitfalls.md +30 -0
  46. package/distilled/templates/delegates/researcher-stack.md +30 -0
  47. package/distilled/templates/delegates/researcher-synthesizer.md +31 -0
  48. package/distilled/templates/research/architecture.md +57 -0
  49. package/distilled/templates/research/features.md +23 -0
  50. package/distilled/templates/research/pitfalls.md +46 -0
  51. package/distilled/templates/research/stack.md +45 -0
  52. package/distilled/templates/research/summary.md +67 -0
  53. package/distilled/templates/roadmap.md +62 -0
  54. package/distilled/templates/spec.md +110 -0
  55. package/distilled/workflows/audit-milestone.md +220 -0
  56. package/distilled/workflows/execute.md +270 -0
  57. package/distilled/workflows/map-codebase.md +246 -0
  58. package/distilled/workflows/new-project.md +418 -0
  59. package/distilled/workflows/pause.md +121 -0
  60. package/distilled/workflows/plan.md +383 -0
  61. package/distilled/workflows/progress.md +199 -0
  62. package/distilled/workflows/quick.md +187 -0
  63. package/distilled/workflows/resume.md +152 -0
  64. package/distilled/workflows/verify.md +307 -0
  65. package/package.json +45 -0
@@ -0,0 +1,145 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ const CLAUDE_MODEL_PROFILES = {
5
+ quality: 'opus',
6
+ balanced: 'sonnet',
7
+ budget: 'haiku',
8
+ };
9
+
10
+ function renderClaudePlanChecker(delegateContent, modelAlias = 'sonnet') {
11
+ return `---
12
+ name: gsdd-plan-checker
13
+ description: Fresh-context plan checker for GSDD plan drafts. Review-only; never edits plans directly.
14
+ model: ${modelAlias}
15
+ tools: Read, Grep, Glob
16
+ ---
17
+
18
+ ${delegateContent.trim()}
19
+ `;
20
+ }
21
+
22
+ function renderClaudePlanSkill() {
23
+ return `---
24
+ name: gsdd-plan
25
+ description: Claude-native Phase planning with fresh-context plan checking for GSDD
26
+ argument-hint: [phase-number]
27
+ ---
28
+
29
+ You are the Claude-native \`/gsdd-plan\` skill for GSDD phase planning.
30
+
31
+ Portable contract:
32
+ - Read \`.agents/skills/gsdd-plan/SKILL.md\` first. That file remains the canonical vendor-agnostic plan contract.
33
+ - Keep the portable contract honest: it defines the workflow, but it does not by itself prove fresh-context checker orchestration across runtimes.
34
+ - If the portable skill says plan is still a stub, treat that as a portability-status warning for the generic surface, not as a stop signal for this Claude-native adapter path.
35
+
36
+ Native Claude adapter rule:
37
+ - This skill is the canonical Claude-native entry surface for \`/gsdd-plan\`.
38
+ - Stay in the primary Claude context for orchestration. Do NOT fork this skill into a subagent, because the checker must run as its own fresh-context subagent.
39
+ - Use the native \`gsdd-plan-checker\` subagent to regain the fresh-context checker pass that portable markdown alone cannot guarantee.
40
+ - Do NOT claim that other runtimes have the same behavior unless their own adapters explicitly implement and prove it.
41
+
42
+ Execution flow:
43
+ 1. Read \`.planning/SPEC.md\`, \`.planning/ROADMAP.md\`, \`.planning/config.json\`, relevant phase research, and any existing phase plan files.
44
+ 2. Resolve the target phase from the command arguments. If no phase is provided, choose the first roadmap phase that is not complete.
45
+ 3. Produce the initial phase plan according to \`.agents/skills/gsdd-plan/SKILL.md\`.
46
+ 4. If \`.planning/config.json\` has \`workflow.planCheck: false\`, stop after planner self-check and explicitly report reduced assurance.
47
+ 5. If \`workflow.planCheck: true\`, invoke the native \`gsdd-plan-checker\` subagent with fresh context.
48
+ 6. Pass only explicit inputs to the checker:
49
+ - target phase goal and requirement IDs
50
+ - relevant locked decisions / deferred items from \`.planning/SPEC.md\`
51
+ - relevant phase research file(s)
52
+ - produced \`.planning/phases/*-PLAN.md\` file(s)
53
+ 7. Require the checker to return a single JSON object with this shape:
54
+ {
55
+ "status": "passed",
56
+ "summary": "One sentence overall assessment",
57
+ "issues": [
58
+ {
59
+ "dimension": "requirement_coverage | task_completeness | dependency_correctness | key_link_completeness | scope_sanity | must_have_quality | context_compliance",
60
+ "severity": "blocker | warning",
61
+ "description": "What is wrong",
62
+ "plan": "01-PLAN",
63
+ "task": "1-02",
64
+ "fix_hint": "Specific revision instruction"
65
+ }
66
+ ]
67
+ }
68
+ Status must be either "passed" or "issues_found".
69
+ 8. If the checker returns \`passed\`, finish and summarize.
70
+ 9. If the checker returns \`issues_found\`, revise the existing plan files only where needed, then run the checker again.
71
+ 10. Maximum 3 checker cycles total. If blockers remain after cycle 3, stop and escalate to the user instead of pretending the plan is ready.
72
+
73
+ Return a concise orchestration summary:
74
+ - target phase
75
+ - whether native plan checking ran
76
+ - checker cycle count
77
+ - final result: passed | reduced_assurance | escalated
78
+
79
+ Never return raw checker JSON without summarizing it.
80
+ `;
81
+ }
82
+
83
+ function renderClaudePlanCommand() {
84
+ return `---
85
+ description: Compatibility alias for the Claude-native \`/gsdd-plan\` skill
86
+ argument-hint: [phase-number]
87
+ allowed-tools: Read
88
+ ---
89
+
90
+ Read \`.claude/skills/gsdd-plan/SKILL.md\` and execute that skill as the canonical Claude-native \`/gsdd-plan\` entry.
91
+
92
+ Rules:
93
+ - Do NOT duplicate orchestration logic here.
94
+ - Do NOT fork into a separate planning subagent.
95
+ - Preserve the argument value and apply it when resolving the target phase.
96
+ `;
97
+ }
98
+
99
+ function createClaudeAdapter({ cwd, workflows, renderSkillContent, getDelegateContent, resolveRuntimeAgentModel }) {
100
+ const skillsDir = join(cwd, '.claude', 'skills');
101
+ const commandsDir = join(cwd, '.claude', 'commands');
102
+ const agentsDir = join(cwd, '.claude', 'agents');
103
+
104
+ return {
105
+ id: 'claude',
106
+ name: 'claude',
107
+ kind: 'native_capable',
108
+ detect() {
109
+ return existsSync(join(cwd, 'CLAUDE.md')) || existsSync(join(cwd, '.claude'));
110
+ },
111
+ isInstalled() {
112
+ return existsSync(skillsDir) || existsSync(commandsDir) || existsSync(agentsDir);
113
+ },
114
+ generate() {
115
+ const modelAlias = resolveRuntimeAgentModel({
116
+ cwd,
117
+ runtime: 'claude',
118
+ agentId: 'plan-checker',
119
+ profileMap: CLAUDE_MODEL_PROFILES,
120
+ });
121
+ for (const workflow of workflows) {
122
+ const dir = join(skillsDir, workflow.name);
123
+ mkdirSync(dir, { recursive: true });
124
+ const content = workflow.name === 'gsdd-plan'
125
+ ? renderClaudePlanSkill()
126
+ : renderSkillContent(workflow);
127
+ writeFileSync(join(dir, 'SKILL.md'), content);
128
+ }
129
+
130
+ mkdirSync(commandsDir, { recursive: true });
131
+ writeFileSync(join(commandsDir, 'gsdd-plan.md'), renderClaudePlanCommand());
132
+
133
+ mkdirSync(agentsDir, { recursive: true });
134
+ writeFileSync(
135
+ join(agentsDir, 'gsdd-plan-checker.md'),
136
+ renderClaudePlanChecker(getDelegateContent('plan-checker.md'), modelAlias)
137
+ );
138
+ },
139
+ summary(action) {
140
+ return `${action} Claude Code skills (.claude/skills/gsdd-*), native commands (.claude/commands/gsdd-*.md), and native agents (.claude/agents/gsdd-*.md)`;
141
+ },
142
+ };
143
+ }
144
+
145
+ export { createClaudeAdapter, CLAUDE_MODEL_PROFILES };
@@ -0,0 +1,58 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ function safeTomlString(value) {
5
+ return value.replace(/[\\"]/g, '\\$&').replace(/\n/g, '\\n');
6
+ }
7
+
8
+ function renderCodexPlanChecker(delegateContent, modelId = null) {
9
+ const safe = delegateContent.trim().replaceAll('"""', '"" "');
10
+ const modelLine = modelId ? `model = "${safeTomlString(modelId)}"\n` : '';
11
+ return `name = "gsdd-plan-checker"
12
+ description = "Fresh-context plan checker for GSDD plan drafts. Review-only; never edits plans directly."
13
+ sandbox_mode = "read-only"
14
+ model_reasoning_effort = "high"
15
+ ${modelLine}
16
+ developer_instructions = """
17
+ ${safe}
18
+ """
19
+ `;
20
+ }
21
+
22
+ function createCodexAdapter({
23
+ cwd,
24
+ getDelegateContent,
25
+ getRuntimeModelOverride,
26
+ loadProjectModelConfig,
27
+ }) {
28
+ const agentsDir = join(cwd, '.codex', 'agents');
29
+
30
+ return {
31
+ id: 'codex',
32
+ name: 'codex',
33
+ kind: 'native_capable',
34
+ detect() {
35
+ return existsSync(join(cwd, '.codex'));
36
+ },
37
+ isInstalled() {
38
+ return existsSync(agentsDir);
39
+ },
40
+ generate() {
41
+ const config = loadProjectModelConfig(cwd);
42
+ const checkerModelId = getRuntimeModelOverride(config, 'codex', 'plan-checker');
43
+
44
+ mkdirSync(agentsDir, { recursive: true });
45
+
46
+ // Checker agent (read-only reviewer, spawned by the portable skill's orchestration loop)
47
+ writeFileSync(
48
+ join(agentsDir, 'gsdd-plan-checker.toml'),
49
+ renderCodexPlanChecker(getDelegateContent('plan-checker.md'), checkerModelId)
50
+ );
51
+ },
52
+ summary(action) {
53
+ return `${action} Codex CLI native agent (.codex/agents/gsdd-plan-checker.toml)`;
54
+ },
55
+ };
56
+ }
57
+
58
+ export { createCodexAdapter };
@@ -0,0 +1,20 @@
1
+ import { createRootAgentsAdapter } from './agents.mjs';
2
+ import { createClaudeAdapter } from './claude.mjs';
3
+ import { createCodexAdapter } from './codex.mjs';
4
+ import { createOpenCodeAdapter } from './opencode.mjs';
5
+
6
+ function createAdapterRegistry(context) {
7
+ const agentsAdapter = createRootAgentsAdapter(context, 'agents');
8
+
9
+ return {
10
+ claude: createClaudeAdapter(context),
11
+ opencode: createOpenCodeAdapter(context),
12
+ codex: createCodexAdapter(context),
13
+ agents: agentsAdapter,
14
+ cursor: createRootAgentsAdapter(context, 'cursor'),
15
+ copilot: createRootAgentsAdapter(context, 'copilot'),
16
+ gemini: createRootAgentsAdapter(context, 'gemini'),
17
+ };
18
+ }
19
+
20
+ export { createAdapterRegistry };
@@ -0,0 +1,237 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
+ import os from 'os';
3
+ import { join } from 'path';
4
+
5
+ function expandHome(filePath) {
6
+ if (!filePath) return filePath;
7
+ if (filePath.startsWith('~/')) {
8
+ return join(os.homedir(), filePath.slice(2));
9
+ }
10
+ return filePath;
11
+ }
12
+
13
+ function parseJsonc(content) {
14
+ if (!content) return {};
15
+ if (content.charCodeAt(0) === 0xFEFF) {
16
+ content = content.slice(1);
17
+ }
18
+
19
+ let result = '';
20
+ let inString = false;
21
+ let i = 0;
22
+
23
+ while (i < content.length) {
24
+ const char = content[i];
25
+ const next = content[i + 1];
26
+
27
+ if (inString) {
28
+ result += char;
29
+ if (char === '\\' && i + 1 < content.length) {
30
+ result += next;
31
+ i += 2;
32
+ continue;
33
+ }
34
+ if (char === '"') {
35
+ inString = false;
36
+ }
37
+ i++;
38
+ continue;
39
+ }
40
+
41
+ if (char === '"') {
42
+ inString = true;
43
+ result += char;
44
+ i++;
45
+ continue;
46
+ }
47
+
48
+ if (char === '/' && next === '/') {
49
+ while (i < content.length && content[i] !== '\n') {
50
+ i++;
51
+ }
52
+ continue;
53
+ }
54
+
55
+ if (char === '/' && next === '*') {
56
+ i += 2;
57
+ while (i < content.length - 1 && !(content[i] === '*' && content[i + 1] === '/')) {
58
+ i++;
59
+ }
60
+ if (i < content.length - 1) i += 2;
61
+ continue;
62
+ }
63
+
64
+ result += char;
65
+ i++;
66
+ }
67
+
68
+ return JSON.parse(result.replace(/,(\s*[}\]])/g, '$1'));
69
+ }
70
+
71
+ function readOpenCodeConfig(configPath) {
72
+ if (!configPath || !existsSync(configPath)) return null;
73
+ try {
74
+ return parseJsonc(readFileSync(configPath, 'utf-8'));
75
+ } catch {
76
+ return null;
77
+ }
78
+ }
79
+
80
+ function getOpenCodeConfigPaths(cwd) {
81
+ const globalConfig = join(os.homedir(), '.config', 'opencode', 'opencode.json');
82
+ const customConfig = process.env.OPENCODE_CONFIG
83
+ ? expandHome(process.env.OPENCODE_CONFIG)
84
+ : null;
85
+ const projectConfig = join(cwd, 'opencode.json');
86
+
87
+ return [globalConfig, customConfig, projectConfig].filter(Boolean);
88
+ }
89
+
90
+ function detectOpenCodeConfiguredModel(cwd) {
91
+ let configuredModel = null;
92
+
93
+ for (const configPath of getOpenCodeConfigPaths(cwd)) {
94
+ const config = readOpenCodeConfig(configPath);
95
+ if (config && typeof config.model === 'string' && config.model.includes('/')) {
96
+ configuredModel = config.model.trim();
97
+ }
98
+ }
99
+
100
+ if (process.env.OPENCODE_CONFIG_CONTENT) {
101
+ try {
102
+ const inlineConfig = parseJsonc(process.env.OPENCODE_CONFIG_CONTENT);
103
+ if (typeof inlineConfig.model === 'string' && inlineConfig.model.includes('/')) {
104
+ configuredModel = inlineConfig.model.trim();
105
+ }
106
+ } catch {
107
+ // Ignore malformed inline config and keep the best file-based result.
108
+ }
109
+ }
110
+
111
+ return configuredModel;
112
+ }
113
+
114
+ function renderOpenCodePlanChecker(delegateContent, modelId = null) {
115
+ const modelLine = modelId ? `model: ${modelId}\n` : '';
116
+ return `---
117
+ description: Fresh-context plan checker for GSDD plan drafts. Review-only; never edits plans directly.
118
+ mode: subagent
119
+ hidden: true
120
+ ${modelLine}tools:
121
+ write: false
122
+ edit: false
123
+ bash: false
124
+ ---
125
+
126
+ ${delegateContent.trim()}
127
+ `;
128
+ }
129
+
130
+ function renderOpenCodePlanCommand() {
131
+ return `---
132
+ description: OpenCode-native phase planning with fresh-context plan checking for GSDD
133
+ subtask: false
134
+ ---
135
+
136
+ You are the OpenCode-native \`/gsdd-plan\` command for GSDD phase planning.
137
+
138
+ Portable contract:
139
+ - Read \`.agents/skills/gsdd-plan/SKILL.md\` first. That file remains the canonical vendor-agnostic plan contract.
140
+ - Keep the portable contract honest: it defines the workflow, but it does not by itself prove fresh-context checker orchestration across runtimes.
141
+ - If the portable skill says plan is still a stub, treat that as a portability-status warning for the generic surface, not as a stop signal for this OpenCode-native adapter path.
142
+
143
+ Native OpenCode adapter rule:
144
+ - This command is the canonical OpenCode-native entry surface for \`/gsdd-plan\`.
145
+ - Stay in the primary conversation context for orchestration so the checker can run as its own fresh-context subagent.
146
+ - Use the native \`gsdd-plan-checker\` subagent for review-only checking.
147
+ - Do NOT claim that other runtimes have the same behavior unless their own adapters explicitly implement and prove it.
148
+
149
+ Execution flow:
150
+ 1. Read \`.planning/SPEC.md\`, \`.planning/ROADMAP.md\`, \`.planning/config.json\`, relevant phase research, and any existing phase plan files.
151
+ 2. Resolve the target phase from the command arguments. If no phase is provided, choose the first roadmap phase that is not complete.
152
+ 3. Produce the initial phase plan according to \`.agents/skills/gsdd-plan/SKILL.md\`.
153
+ 4. If \`.planning/config.json\` has \`workflow.planCheck: false\`, stop after planner self-check and explicitly report reduced assurance.
154
+ 5. If \`workflow.planCheck: true\`, invoke the hidden \`gsdd-plan-checker\` subagent with fresh context.
155
+ 6. Pass only explicit inputs to the checker:
156
+ - target phase goal and requirement IDs
157
+ - relevant locked decisions / deferred items from \`.planning/SPEC.md\`
158
+ - relevant phase research file(s)
159
+ - produced \`.planning/phases/*-PLAN.md\` file(s)
160
+ 7. Require the checker to return a single JSON object with this shape:
161
+ {
162
+ "status": "passed",
163
+ "summary": "One sentence overall assessment",
164
+ "issues": [
165
+ {
166
+ "dimension": "requirement_coverage | task_completeness | dependency_correctness | key_link_completeness | scope_sanity | must_have_quality | context_compliance",
167
+ "severity": "blocker | warning",
168
+ "description": "What is wrong",
169
+ "plan": "01-PLAN",
170
+ "task": "1-02",
171
+ "fix_hint": "Specific revision instruction"
172
+ }
173
+ ]
174
+ }
175
+ Status must be either "passed" or "issues_found".
176
+ 8. If the checker returns \`passed\`, finish and summarize.
177
+ 9. If the checker returns \`issues_found\`, revise the existing plan files only where needed, then run the checker again.
178
+ 10. Maximum 3 checker cycles total. If blockers remain after cycle 3, stop and escalate to the user instead of pretending the plan is ready.
179
+
180
+ Return a concise orchestration summary:
181
+ - target phase
182
+ - whether native plan checking ran
183
+ - checker cycle count
184
+ - final result: passed | reduced_assurance | escalated
185
+
186
+ Never return raw checker JSON without summarizing it.
187
+ `;
188
+ }
189
+
190
+ function createOpenCodeAdapter({
191
+ cwd,
192
+ workflows,
193
+ renderOpenCodeCommandContent,
194
+ getDelegateContent,
195
+ getRuntimeModelOverride,
196
+ loadProjectModelConfig,
197
+ }) {
198
+ const commandsDir = join(cwd, '.opencode', 'commands');
199
+ const agentsDir = join(cwd, '.opencode', 'agents');
200
+
201
+ return {
202
+ id: 'opencode',
203
+ name: 'opencode',
204
+ kind: 'native_capable',
205
+ detect() {
206
+ return existsSync(join(cwd, '.opencode'));
207
+ },
208
+ isInstalled() {
209
+ return existsSync(commandsDir) || existsSync(agentsDir);
210
+ },
211
+ generate() {
212
+ const config = loadProjectModelConfig(cwd);
213
+ const modelId = getRuntimeModelOverride(config, 'opencode', 'plan-checker');
214
+ mkdirSync(commandsDir, { recursive: true });
215
+ for (const workflow of workflows) {
216
+ const content = workflow.name === 'gsdd-plan'
217
+ ? renderOpenCodePlanCommand()
218
+ : renderOpenCodeCommandContent(workflow);
219
+ writeFileSync(
220
+ join(commandsDir, `${workflow.name}.md`),
221
+ content
222
+ );
223
+ }
224
+
225
+ mkdirSync(agentsDir, { recursive: true });
226
+ writeFileSync(
227
+ join(agentsDir, 'gsdd-plan-checker.md'),
228
+ renderOpenCodePlanChecker(getDelegateContent('plan-checker.md'), modelId)
229
+ );
230
+ },
231
+ summary(action) {
232
+ return `${action} OpenCode slash commands (.opencode/commands/gsdd-*.md) and native agents (.opencode/agents/gsdd-*.md)`;
233
+ },
234
+ };
235
+ }
236
+
237
+ export { createOpenCodeAdapter, detectOpenCodeConfiguredModel };
package/bin/gsdd.mjs ADDED
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+
3
+ // gsdd - GSD Distilled CLI
4
+
5
+ import { realpathSync } from 'fs';
6
+ import { join, dirname } from 'path';
7
+ import { fileURLToPath } from 'url';
8
+ import { createAdapterRegistry } from './adapters/index.mjs';
9
+ import {
10
+ renderAgentsBoundedBlock,
11
+ renderAgentsFileContent,
12
+ renderOpenCodeCommandContent,
13
+ renderSkillContent,
14
+ upsertBoundedBlock,
15
+ getDelegateContent,
16
+ } from './lib/rendering.mjs';
17
+ import { loadProjectModelConfig, getRuntimeModelOverride, resolveRuntimeAgentModel, cmdModels } from './lib/models.mjs';
18
+ import { createCmdInit, createCmdUpdate, cmdHelp } from './lib/init.mjs';
19
+ import { cmdFindPhase, cmdVerify, cmdScaffold } from './lib/phase.mjs';
20
+ import { createCmdHealth } from './lib/health.mjs';
21
+
22
+ const __filename = fileURLToPath(import.meta.url);
23
+ const __dirname = dirname(__filename);
24
+ const DISTILLED_DIR = join(__dirname, '..', 'distilled');
25
+ const AGENTS_DIR = join(__dirname, '..', 'agents');
26
+ const CWD = process.cwd();
27
+ const PLANNING_DIR = join(CWD, '.planning');
28
+ const IS_MAIN = process.argv[1]
29
+ ? realpathSync(process.argv[1]) === realpathSync(__filename)
30
+ : false;
31
+
32
+ const [,, command, ...args] = process.argv;
33
+
34
+ const WORKFLOWS = [
35
+ { name: 'gsdd-new-project', workflow: 'new-project.md', description: 'New project - questioning, codebase audit, research, spec, roadmap', agent: 'Plan', opencodeType: 'plan' },
36
+ { name: 'gsdd-map-codebase', workflow: 'map-codebase.md', description: 'Map or refresh codebase - 4 parallel mappers, staleness check, secrets scan', agent: 'Plan', opencodeType: 'plan' },
37
+ { name: 'gsdd-plan', workflow: 'plan.md', description: 'Plan a phase - research check, backward planning, task creation', agent: 'Plan', opencodeType: 'plan' },
38
+ { name: 'gsdd-execute', workflow: 'execute.md', description: 'Execute a phase plan - implement tasks, verify changes, follow repo git conventions', agent: 'Code', opencodeType: 'edit' },
39
+ { name: 'gsdd-verify', workflow: 'verify.md', description: 'Verify a completed phase - 3-level checks, anti-pattern scan', agent: 'Plan', opencodeType: 'plan' },
40
+ { name: 'gsdd-audit-milestone', workflow: 'audit-milestone.md', description: 'Audit a completed milestone - cross-phase integration, requirements coverage, E2E flows', agent: 'Plan', opencodeType: 'plan' },
41
+ { name: 'gsdd-quick', workflow: 'quick.md', description: 'Quick task - plan and execute a sub-hour task outside the phase cycle', agent: 'Code', opencodeType: 'edit' },
42
+ { name: 'gsdd-pause', workflow: 'pause.md', description: 'Pause work - save session context for seamless resumption', agent: 'Plan', opencodeType: 'plan' },
43
+ { name: 'gsdd-resume', workflow: 'resume.md', description: 'Resume work - restore context and route to next action', agent: 'Plan', opencodeType: 'plan' },
44
+ { name: 'gsdd-progress', workflow: 'progress.md', description: 'Check progress - show project status and route to next action', agent: 'Plan', opencodeType: 'plan' },
45
+ ];
46
+
47
+ const FRAMEWORK_VERSION = 'v1.2';
48
+
49
+ const ADAPTERS = createAdapterRegistry({
50
+ cwd: CWD,
51
+ workflows: WORKFLOWS,
52
+ renderAgentsBoundedBlock,
53
+ renderAgentsFileContent,
54
+ renderOpenCodeCommandContent,
55
+ renderSkillContent,
56
+ upsertBoundedBlock,
57
+ getDelegateContent,
58
+ loadProjectModelConfig,
59
+ getRuntimeModelOverride,
60
+ resolveRuntimeAgentModel,
61
+ });
62
+
63
+ const INIT_CONTEXT = {
64
+ cwd: CWD,
65
+ planningDir: PLANNING_DIR,
66
+ distilledDir: DISTILLED_DIR,
67
+ agentsDir: AGENTS_DIR,
68
+ workflows: WORKFLOWS,
69
+ frameworkVersion: FRAMEWORK_VERSION,
70
+ adapters: ADAPTERS,
71
+ };
72
+
73
+ const cmdInit = createCmdInit(INIT_CONTEXT);
74
+ const cmdUpdate = createCmdUpdate(INIT_CONTEXT);
75
+ const cmdHealth = createCmdHealth({ frameworkVersion: FRAMEWORK_VERSION });
76
+
77
+ const COMMANDS = {
78
+ init: cmdInit,
79
+ update: cmdUpdate,
80
+ models: cmdModels,
81
+ health: cmdHealth,
82
+ 'find-phase': cmdFindPhase,
83
+ verify: cmdVerify,
84
+ scaffold: cmdScaffold,
85
+ help: cmdHelp,
86
+ };
87
+
88
+ async function runCli(cliCommand = command, cliArgs = args) {
89
+ if (!cliCommand || !COMMANDS[cliCommand]) {
90
+ cmdHelp();
91
+ if (cliCommand) process.exitCode = 1;
92
+ return;
93
+ }
94
+
95
+ await COMMANDS[cliCommand](...cliArgs);
96
+ }
97
+
98
+ if (IS_MAIN) {
99
+ await runCli();
100
+ }
101
+
102
+ export { cmdHelp, cmdInit, cmdUpdate, cmdModels, cmdHealth, cmdFindPhase, cmdVerify, cmdScaffold, runCli, FRAMEWORK_VERSION };
@@ -0,0 +1,28 @@
1
+ // cli-utils.mjs — Pure CLI utility functions (no external deps)
2
+
3
+ export function parseFlagValue(flagArgs, flagName) {
4
+ const idx = flagArgs.indexOf(flagName);
5
+ if (idx === -1) return { present: false, value: null, invalid: false };
6
+
7
+ const value = flagArgs[idx + 1];
8
+ if (!value || value.startsWith('--')) {
9
+ return { present: true, value: null, invalid: true };
10
+ }
11
+
12
+ return { present: true, value, invalid: false };
13
+ }
14
+
15
+ export function parseToolsFlag(flagArgs) {
16
+ const { value } = parseFlagValue(flagArgs, '--tools');
17
+ if (!value) return [];
18
+ if (value === 'all') return ['claude', 'opencode', 'codex', 'agents'];
19
+ return value.split(',').map((v) => v.trim()).filter(Boolean);
20
+ }
21
+
22
+ export function parseAutoFlag(flagArgs) {
23
+ return flagArgs.includes('--auto');
24
+ }
25
+
26
+ export function output(data) {
27
+ console.log(JSON.stringify(data, null, 2));
28
+ }