gsdd-cli 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -22
- package/agents/DISTILLATION.md +2 -2
- package/agents/README.md +4 -4
- package/agents/approach-explorer.md +4 -4
- package/agents/executor.md +14 -14
- package/agents/integration-checker.md +2 -2
- package/agents/researcher.md +2 -2
- package/agents/roadmapper.md +6 -6
- package/agents/synthesizer.md +18 -18
- package/agents/verifier.md +2 -2
- package/bin/adapters/agents.mjs +3 -3
- package/bin/adapters/claude.mjs +16 -14
- package/bin/adapters/opencode.mjs +15 -12
- package/bin/gsdd.mjs +16 -13
- package/bin/lib/{models.mjs → config.mjs} +23 -16
- package/bin/lib/control-map.mjs +17 -488
- package/bin/lib/health-truth.mjs +8 -13
- package/bin/lib/health.mjs +25 -39
- package/bin/lib/init-flow.mjs +44 -38
- package/bin/lib/init-prompts.mjs +3 -3
- package/bin/lib/init-runtime.mjs +11 -30
- package/bin/lib/lifecycle-preflight.mjs +97 -410
- package/bin/lib/lifecycle-state.mjs +2 -1
- package/bin/lib/next.mjs +243 -20
- package/bin/lib/phase.mjs +10 -315
- package/bin/lib/rendering.mjs +64 -44
- package/bin/lib/runtime-freshness.mjs +18 -15
- package/bin/lib/state-dir.mjs +45 -0
- package/bin/lib/templates.mjs +59 -22
- package/bin/lib/work-context.mjs +12 -1
- package/bin/lib/workflows.mjs +0 -1
- package/bin/lib/workspace-root.mjs +11 -6
- package/distilled/DESIGN.md +58 -2
- package/distilled/EVIDENCE-INDEX.md +15 -2
- package/distilled/README.md +23 -33
- package/distilled/SKILL.md +9 -10
- package/distilled/templates/agents.block.md +5 -5
- package/distilled/templates/approach.md +3 -3
- package/distilled/templates/auth-matrix.md +2 -2
- package/distilled/templates/brownfield-change/CHANGE.md +1 -1
- package/distilled/templates/delegates/approach-explorer.md +5 -5
- package/distilled/templates/delegates/mapper-arch.md +3 -3
- package/distilled/templates/delegates/mapper-concerns.md +4 -4
- package/distilled/templates/delegates/mapper-quality.md +3 -3
- package/distilled/templates/delegates/mapper-tech.md +3 -3
- package/distilled/templates/delegates/plan-checker.md +5 -5
- package/distilled/templates/delegates/researcher-architecture.md +3 -3
- package/distilled/templates/delegates/researcher-features.md +3 -3
- package/distilled/templates/delegates/researcher-pitfalls.md +3 -3
- package/distilled/templates/delegates/researcher-stack.md +3 -3
- package/distilled/templates/delegates/researcher-synthesizer.md +7 -7
- package/distilled/templates/research/architecture.md +1 -1
- package/distilled/templates/research/pitfalls.md +1 -1
- package/distilled/templates/research/stack.md +1 -1
- package/distilled/templates/roadmap.md +4 -4
- package/distilled/templates/spec.md +2 -2
- package/distilled/workflows/audit-milestone.md +22 -22
- package/distilled/workflows/complete-milestone.md +35 -35
- package/distilled/workflows/execute.md +29 -29
- package/distilled/workflows/map-codebase.md +30 -30
- package/distilled/workflows/new-milestone.md +18 -18
- package/distilled/workflows/new-project.md +45 -45
- package/distilled/workflows/pause.md +15 -15
- package/distilled/workflows/plan.md +63 -63
- package/distilled/workflows/progress.md +40 -39
- package/distilled/workflows/quick.md +43 -43
- package/distilled/workflows/resume.md +23 -22
- package/distilled/workflows/verify-work.md +7 -7
- package/distilled/workflows/verify.md +20 -20
- package/docs/BROWNFIELD-PROOF.md +1 -1
- package/docs/RUNTIME-SUPPORT.md +13 -13
- package/docs/USER-GUIDE.md +17 -20
- package/docs/claude/context-monitor.md +1 -1
- package/docs/proof/consumer-node-cli/README.md +1 -1
- package/package.json +3 -3
- package/bin/lib/closeout-report.mjs +0 -318
- package/bin/lib/evidence-contract.mjs +0 -325
- package/bin/lib/provenance.mjs +0 -390
- package/bin/lib/session-fingerprint.mjs +0 -223
- package/bin/lib/ui-proof.mjs +0 -1007
- package/distilled/workflows/plan-milestone-gaps.md +0 -204
package/bin/adapters/claude.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
MAX_CHECKER_CYCLES,
|
|
6
6
|
CHECKER_STATUSES,
|
|
7
7
|
} from '../lib/plan-constants.mjs';
|
|
8
|
+
import { localizeStateDirReferences } from '../lib/rendering.mjs';
|
|
8
9
|
|
|
9
10
|
const CLAUDE_MODEL_PROFILES = {
|
|
10
11
|
quality: 'opus',
|
|
@@ -36,7 +37,7 @@ ${delegateContent.trim()}
|
|
|
36
37
|
`;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
function renderClaudePlanSkill({ portableContractPath = '.agents/skills/gsdd-plan/SKILL.md' } = {}) {
|
|
40
|
+
function renderClaudePlanSkill({ portableContractPath = '.agents/skills/gsdd-plan/SKILL.md', stateDirName = '.work' } = {}) {
|
|
40
41
|
const contractSection = portableContractPath
|
|
41
42
|
? `Portable contract:
|
|
42
43
|
- Read \`${portableContractPath}\` first. That file remains the canonical vendor-agnostic plan contract.
|
|
@@ -44,13 +45,13 @@ function renderClaudePlanSkill({ portableContractPath = '.agents/skills/gsdd-pla
|
|
|
44
45
|
- 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.`
|
|
45
46
|
: `Workflow contract:
|
|
46
47
|
- This globally installed skill is the canonical Claude-native \`gsdd-plan\` workflow contract.
|
|
47
|
-
- Keep the workflow portable: use repo-local \`.
|
|
48
|
+
- Keep the workflow portable: use repo-local \`.work/\` artifacts for project state and do not require repo-local \`.agents/skills/\` files to exist.
|
|
48
49
|
- Do not claim that other runtimes have the same behavior unless their own adapters explicitly implement and prove it.`;
|
|
49
50
|
const planningContract = portableContractPath
|
|
50
51
|
? `\`${portableContractPath}\``
|
|
51
52
|
: 'this skill';
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
const content = `---
|
|
54
55
|
name: gsdd-plan
|
|
55
56
|
description: Claude-native Phase planning with fresh-context plan checking for GSDD
|
|
56
57
|
argument-hint: [phase-number]
|
|
@@ -67,25 +68,25 @@ Native Claude adapter rule:
|
|
|
67
68
|
- Do NOT claim that other runtimes have the same behavior unless their own adapters explicitly implement and prove it.
|
|
68
69
|
|
|
69
70
|
Execution flow:
|
|
70
|
-
1. Read \`.
|
|
71
|
+
1. Read \`.work/SPEC.md\`, \`.work/ROADMAP.md\`, \`.work/config.json\`, relevant phase research, and any existing phase plan files.
|
|
71
72
|
2. Resolve the target phase from the command arguments. If no phase is provided, choose the first roadmap phase that is not complete.
|
|
72
73
|
3. **Approach exploration** (before planning):
|
|
73
|
-
a. Check \`.
|
|
74
|
+
a. Check \`.work/config.json\` for \`workflow.discuss\`. If \`false\` or missing, skip to step 4 and report \`reduced_alignment\` in the summary.
|
|
74
75
|
b. Check if \`{phase_dir}/{padded_phase}-APPROACH.md\` exists. If it does, offer the user: "Use existing" / "Update it" / "View it". If "Use existing", load decisions, then validate the alignment proof before step 4; proofless or invalid existing APPROACH.md must be updated, not silently trusted.
|
|
75
|
-
c. If no APPROACH.md exists (or user chose "Update"): invoke the native \`gsdd-approach-explorer\` subagent with the phase goal, requirement IDs, project config from \`.
|
|
76
|
+
c. If no APPROACH.md exists (or user chose "Update"): invoke the native \`gsdd-approach-explorer\` subagent with the phase goal, requirement IDs, project config from \`.work/config.json\` (especially \`workflow.discuss\`), SPEC locked decisions, phase research, and relevant codebase files.
|
|
76
77
|
d. The explorer runs a GSD-style interactive conversation with the user (gray areas, research, deep-dive questions, assumptions) and writes APPROACH.md.
|
|
77
78
|
e. Before planning, confirm APPROACH.md records all canonical proof fields: \`alignment_status\`, \`alignment_method\`, \`user_confirmed_at\`, \`explicit_skip_approved\`, \`skip_scope\`, \`skip_rationale\`, and \`confirmed_decisions\`. For \`alignment_status: user_confirmed\`, \`confirmed_decisions\` must name the locked decisions and skip fields may be \`false\`/\`N/A\`; for \`alignment_status: approved_skip\`, \`explicit_skip_approved: true\`, \`skip_scope\`, and \`skip_rationale\` must be substantive. Agent-only "No questions needed" is not valid proof under \`workflow.discuss: true\`.
|
|
78
79
|
f. Load APPROACH.md decisions as locked constraints alongside SPEC.md decisions.
|
|
79
80
|
4. Produce the initial phase plan according to ${planningContract}. Pass APPROACH.md decisions (if any) as locked constraints to the planner.
|
|
80
|
-
5. If \`.
|
|
81
|
+
5. If \`.work/config.json\` has \`workflow.planCheck: false\`, stop after planner self-check and explicitly report reduced assurance. This only skips the independent checker; it does not skip the step 3 alignment-proof gate when \`workflow.discuss: true\`.
|
|
81
82
|
6. If \`workflow.planCheck: true\`, invoke the native \`gsdd-plan-checker\` subagent with fresh context.
|
|
82
83
|
7. Pass only explicit inputs to the checker:
|
|
83
84
|
- target phase goal and requirement IDs
|
|
84
|
-
- relevant locked decisions / deferred items from \`.
|
|
85
|
-
- project config from \`.
|
|
86
|
-
- approach decisions from \`.
|
|
85
|
+
- relevant locked decisions / deferred items from \`.work/SPEC.md\`
|
|
86
|
+
- project config from \`.work/config.json\`, especially \`workflow.discuss\` and \`workflow.planCheck\`
|
|
87
|
+
- approach decisions from \`.work/phases/*-APPROACH.md\` (if exists)
|
|
87
88
|
- relevant phase research file(s)
|
|
88
|
-
- produced \`.
|
|
89
|
+
- produced \`.work/phases/*-PLAN.md\` file(s)
|
|
89
90
|
8. Require the checker to return a single JSON object with this shape:
|
|
90
91
|
{
|
|
91
92
|
"status": "issues_found",
|
|
@@ -115,6 +116,7 @@ Return a concise orchestration summary:
|
|
|
115
116
|
|
|
116
117
|
Never return raw checker JSON without summarizing it.
|
|
117
118
|
`;
|
|
119
|
+
return localizeStateDirReferences(content, { stateDirName });
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
function renderClaudePlanCommand({ skillPath = '.claude/skills/gsdd-plan/SKILL.md' } = {}) {
|
|
@@ -133,7 +135,7 @@ Rules:
|
|
|
133
135
|
`;
|
|
134
136
|
}
|
|
135
137
|
|
|
136
|
-
function createClaudeAdapter({ cwd, workflows, renderSkillContent, getDelegateContent, resolveRuntimeAgentModel }) {
|
|
138
|
+
function createClaudeAdapter({ cwd, workflows, stateDirName = '.work', renderSkillContent, getDelegateContent, resolveRuntimeAgentModel }) {
|
|
137
139
|
const skillsDir = join(cwd, '.claude', 'skills');
|
|
138
140
|
const commandsDir = join(cwd, '.claude', 'commands');
|
|
139
141
|
const agentsDir = join(cwd, '.claude', 'agents');
|
|
@@ -169,8 +171,8 @@ function createClaudeAdapter({ cwd, workflows, renderSkillContent, getDelegateCo
|
|
|
169
171
|
const dir = join(skillsDir, workflow.name);
|
|
170
172
|
mkdirSync(dir, { recursive: true });
|
|
171
173
|
const content = workflow.name === 'gsdd-plan'
|
|
172
|
-
? renderClaudePlanSkill()
|
|
173
|
-
: renderSkillContent(workflow);
|
|
174
|
+
? renderClaudePlanSkill({ stateDirName })
|
|
175
|
+
: renderSkillContent(workflow, { stateDirName });
|
|
174
176
|
writeFileSync(join(dir, 'SKILL.md'), content);
|
|
175
177
|
}
|
|
176
178
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
MAX_CHECKER_CYCLES,
|
|
7
7
|
CHECKER_STATUSES,
|
|
8
8
|
} from '../lib/plan-constants.mjs';
|
|
9
|
+
import { localizeStateDirReferences } from '../lib/rendering.mjs';
|
|
9
10
|
|
|
10
11
|
function expandHome(filePath) {
|
|
11
12
|
if (!filePath) return filePath;
|
|
@@ -145,8 +146,8 @@ ${delegateContent.trim()}
|
|
|
145
146
|
`;
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
function renderOpenCodePlanCommand({ skillPath = '.agents/skills/gsdd-plan/SKILL.md' } = {}) {
|
|
149
|
-
|
|
149
|
+
function renderOpenCodePlanCommand({ skillPath = '.agents/skills/gsdd-plan/SKILL.md', stateDirName = '.work' } = {}) {
|
|
150
|
+
const content = `---
|
|
150
151
|
description: OpenCode-native phase planning with fresh-context plan checking for GSDD
|
|
151
152
|
subtask: false
|
|
152
153
|
---
|
|
@@ -165,25 +166,25 @@ Native OpenCode adapter rule:
|
|
|
165
166
|
- Do NOT claim that other runtimes have the same behavior unless their own adapters explicitly implement and prove it.
|
|
166
167
|
|
|
167
168
|
Execution flow:
|
|
168
|
-
1. Read \`.
|
|
169
|
+
1. Read \`.work/SPEC.md\`, \`.work/ROADMAP.md\`, \`.work/config.json\`, relevant phase research, and any existing phase plan files.
|
|
169
170
|
2. Resolve the target phase from the command arguments. If no phase is provided, choose the first roadmap phase that is not complete.
|
|
170
171
|
3. **Approach exploration** (before planning):
|
|
171
|
-
a. Check \`.
|
|
172
|
+
a. Check \`.work/config.json\` for \`workflow.discuss\`. If \`false\` or missing, skip to step 4 and report \`reduced_alignment\` in the summary.
|
|
172
173
|
b. Check if \`{phase_dir}/{padded_phase}-APPROACH.md\` exists. If it does, offer the user: "Use existing" / "Update it" / "View it". If "Use existing", load decisions, then validate the alignment proof before step 4; proofless or invalid existing APPROACH.md must be updated, not silently trusted.
|
|
173
|
-
c. If no APPROACH.md exists (or user chose "Update"): invoke the \`gsdd-approach-explorer\` subagent with the phase goal, requirement IDs, project config from \`.
|
|
174
|
+
c. If no APPROACH.md exists (or user chose "Update"): invoke the \`gsdd-approach-explorer\` subagent with the phase goal, requirement IDs, project config from \`.work/config.json\` (especially \`workflow.discuss\`), SPEC locked decisions, phase research, and relevant codebase files.
|
|
174
175
|
d. The explorer runs a GSD-style interactive conversation with the user (gray areas, research, deep-dive questions, assumptions) and writes APPROACH.md.
|
|
175
176
|
e. Before planning, confirm APPROACH.md records all canonical proof fields: \`alignment_status\`, \`alignment_method\`, \`user_confirmed_at\`, \`explicit_skip_approved\`, \`skip_scope\`, \`skip_rationale\`, and \`confirmed_decisions\`. For \`alignment_status: user_confirmed\`, \`confirmed_decisions\` must name the locked decisions and skip fields may be \`false\`/\`N/A\`; for \`alignment_status: approved_skip\`, \`explicit_skip_approved: true\`, \`skip_scope\`, and \`skip_rationale\` must be substantive. Agent-only "No questions needed" is not valid proof under \`workflow.discuss: true\`.
|
|
176
177
|
f. Load APPROACH.md decisions as locked constraints alongside SPEC.md decisions.
|
|
177
178
|
4. Produce the initial phase plan according to \`${skillPath}\`. Pass APPROACH.md decisions (if any) as locked constraints to the planner.
|
|
178
|
-
5. If \`.
|
|
179
|
+
5. If \`.work/config.json\` has \`workflow.planCheck: false\`, stop after planner self-check and explicitly report reduced assurance. This only skips the independent checker; it does not skip the step 3 alignment-proof gate when \`workflow.discuss: true\`.
|
|
179
180
|
6. If \`workflow.planCheck: true\`, invoke the hidden \`gsdd-plan-checker\` subagent with fresh context.
|
|
180
181
|
7. Pass only explicit inputs to the checker:
|
|
181
182
|
- target phase goal and requirement IDs
|
|
182
|
-
- relevant locked decisions / deferred items from \`.
|
|
183
|
-
- project config from \`.
|
|
184
|
-
- approach decisions from \`.
|
|
183
|
+
- relevant locked decisions / deferred items from \`.work/SPEC.md\`
|
|
184
|
+
- project config from \`.work/config.json\`, especially \`workflow.discuss\` and \`workflow.planCheck\`
|
|
185
|
+
- approach decisions from \`.work/phases/*-APPROACH.md\` (if exists)
|
|
185
186
|
- relevant phase research file(s)
|
|
186
|
-
- produced \`.
|
|
187
|
+
- produced \`.work/phases/*-PLAN.md\` file(s)
|
|
187
188
|
8. Require the checker to return a single JSON object with this shape:
|
|
188
189
|
{
|
|
189
190
|
"status": "issues_found",
|
|
@@ -213,11 +214,13 @@ Return a concise orchestration summary:
|
|
|
213
214
|
|
|
214
215
|
Never return raw checker JSON without summarizing it.
|
|
215
216
|
`;
|
|
217
|
+
return localizeStateDirReferences(content, { stateDirName });
|
|
216
218
|
}
|
|
217
219
|
|
|
218
220
|
function createOpenCodeAdapter({
|
|
219
221
|
cwd,
|
|
220
222
|
workflows,
|
|
223
|
+
stateDirName = '.work',
|
|
221
224
|
renderOpenCodeCommandContent,
|
|
222
225
|
getDelegateContent,
|
|
223
226
|
getRuntimeModelOverride,
|
|
@@ -247,8 +250,8 @@ function createOpenCodeAdapter({
|
|
|
247
250
|
mkdirSync(commandsDir, { recursive: true });
|
|
248
251
|
for (const workflow of workflows) {
|
|
249
252
|
const content = workflow.name === 'gsdd-plan'
|
|
250
|
-
? renderOpenCodePlanCommand()
|
|
251
|
-
: renderOpenCodeCommandContent(workflow);
|
|
253
|
+
? renderOpenCodePlanCommand({ stateDirName })
|
|
254
|
+
: renderOpenCodeCommandContent(workflow, { stateDirName });
|
|
252
255
|
writeFileSync(
|
|
253
256
|
join(commandsDir, `${workflow.name}.md`),
|
|
254
257
|
content
|
package/bin/gsdd.mjs
CHANGED
|
@@ -11,19 +11,16 @@ import {
|
|
|
11
11
|
upsertBoundedBlock,
|
|
12
12
|
getDelegateContent,
|
|
13
13
|
} from './lib/rendering.mjs';
|
|
14
|
-
import { loadProjectModelConfig, getRuntimeModelOverride, resolveRuntimeAgentModel, cmdModels, cmdRigor } from './lib/
|
|
14
|
+
import { loadProjectModelConfig, getRuntimeModelOverride, resolveRuntimeAgentModel, cmdModels, cmdRigor } from './lib/config.mjs';
|
|
15
15
|
import { createCmdInit, createCmdUpdate, cmdHelp } from './lib/init.mjs';
|
|
16
16
|
import { createCmdInstall } from './lib/global-install.mjs';
|
|
17
17
|
import { cmdFindPhase, cmdVerify, cmdScaffold, cmdPhaseStatus } from './lib/phase.mjs';
|
|
18
18
|
import { cmdFileOp } from './lib/file-ops.mjs';
|
|
19
19
|
import { createCmdHealth } from './lib/health.mjs';
|
|
20
20
|
import { cmdLifecyclePreflight } from './lib/lifecycle-preflight.mjs';
|
|
21
|
-
import { cmdSessionFingerprint } from './lib/session-fingerprint.mjs';
|
|
22
|
-
import { cmdUiProof } from './lib/ui-proof.mjs';
|
|
23
|
-
import { cmdControlMap } from './lib/control-map.mjs';
|
|
24
|
-
import { createCmdCloseoutReport } from './lib/closeout-report.mjs';
|
|
25
21
|
import { createCmdNext } from './lib/next.mjs';
|
|
26
22
|
import { resolveWorkspaceContext } from './lib/workspace-root.mjs';
|
|
23
|
+
import { resolveStateDir } from './lib/state-dir.mjs';
|
|
27
24
|
import { FRAMEWORK_VERSION, WORKFLOWS } from './lib/workflows.mjs';
|
|
28
25
|
const __filename = fileURLToPath(import.meta.url);
|
|
29
26
|
const __dirname = dirname(__filename);
|
|
@@ -35,9 +32,11 @@ const IS_MAIN = process.argv[1] ? realpathSync(process.argv[1]) === realpathSync
|
|
|
35
32
|
const [,, command, ...args] = process.argv;
|
|
36
33
|
|
|
37
34
|
function createCliContext(cwd = process.cwd()) {
|
|
35
|
+
const state = resolveStateDir(cwd);
|
|
38
36
|
return {
|
|
39
37
|
cwd,
|
|
40
|
-
planningDir:
|
|
38
|
+
planningDir: state.dir,
|
|
39
|
+
stateDirName: state.name,
|
|
41
40
|
distilledDir: DISTILLED_DIR,
|
|
42
41
|
agentsDir: AGENTS_DIR,
|
|
43
42
|
packageName: PACKAGE_JSON.name,
|
|
@@ -50,6 +49,7 @@ function createCliContext(cwd = process.cwd()) {
|
|
|
50
49
|
adapters: createAdapterRegistry({
|
|
51
50
|
cwd,
|
|
52
51
|
workflows: WORKFLOWS,
|
|
52
|
+
stateDirName: state.name,
|
|
53
53
|
renderAgentsBoundedBlock,
|
|
54
54
|
renderAgentsFileContent,
|
|
55
55
|
renderOpenCodeCommandContent,
|
|
@@ -67,7 +67,6 @@ const INIT_CONTEXT = createCliContext(process.cwd());
|
|
|
67
67
|
const cmdInit = createCmdInit(INIT_CONTEXT);
|
|
68
68
|
const cmdInstall = createCmdInstall(INIT_CONTEXT);
|
|
69
69
|
const cmdHealth = createCmdHealth(INIT_CONTEXT);
|
|
70
|
-
const cmdCloseoutReport = createCmdCloseoutReport(INIT_CONTEXT);
|
|
71
70
|
const cmdNext = createCmdNext(INIT_CONTEXT);
|
|
72
71
|
|
|
73
72
|
const cmdUpdate = (...updateArgs) => {
|
|
@@ -89,10 +88,6 @@ const COMMANDS = {
|
|
|
89
88
|
next: cmdNext,
|
|
90
89
|
'file-op': cmdFileOp,
|
|
91
90
|
'lifecycle-preflight': cmdLifecyclePreflight,
|
|
92
|
-
'session-fingerprint': cmdSessionFingerprint,
|
|
93
|
-
'ui-proof': cmdUiProof,
|
|
94
|
-
'control-map': cmdControlMap,
|
|
95
|
-
'closeout-report': cmdCloseoutReport,
|
|
96
91
|
'find-phase': cmdFindPhase,
|
|
97
92
|
'phase-status': cmdPhaseStatus,
|
|
98
93
|
verify: cmdVerify,
|
|
@@ -118,5 +113,13 @@ async function runCli(cliCommand = command, ...cliArgs) {
|
|
|
118
113
|
await COMMANDS[cliCommand](...normalizedArgs);
|
|
119
114
|
}
|
|
120
115
|
|
|
121
|
-
if (IS_MAIN)
|
|
122
|
-
|
|
116
|
+
if (IS_MAIN) {
|
|
117
|
+
await runCli();
|
|
118
|
+
// D-47: interactive prompts (raw-mode keypress pickers) can leave stdin
|
|
119
|
+
// referenced; release it so the process exits when work is done.
|
|
120
|
+
if (process.stdin.isTTY) {
|
|
121
|
+
process.stdin.pause();
|
|
122
|
+
if (typeof process.stdin.unref === 'function') process.stdin.unref();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export { cmdHelp, cmdInit, cmdInstall, cmdUpdate, cmdModels, cmdRigor, cmdHealth, cmdNext, cmdFileOp, cmdLifecyclePreflight, cmdFindPhase, cmdPhaseStatus, cmdVerify, cmdScaffold, runCli, FRAMEWORK_VERSION, createCliContext };
|
|
@@ -8,6 +8,7 @@ import { join } from 'path';
|
|
|
8
8
|
import { CLAUDE_MODEL_PROFILES } from '../adapters/claude.mjs';
|
|
9
9
|
import { detectOpenCodeConfiguredModel } from '../adapters/opencode.mjs';
|
|
10
10
|
import { parseFlagValue, output } from './cli-utils.mjs';
|
|
11
|
+
import { resolveStateDir } from './state-dir.mjs';
|
|
11
12
|
|
|
12
13
|
export const DEFAULT_GIT_PROTOCOL = {
|
|
13
14
|
branch: 'Follow the existing repo or team branching convention. Use a feature branch for significant changes when no convention exists.',
|
|
@@ -83,11 +84,15 @@ export function buildDefaultConfig({ autoAdvance = false } = {}) {
|
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
export function isProjectInitialized(cwd = process.cwd()) {
|
|
86
|
-
return existsSync(join(cwd
|
|
87
|
+
return existsSync(join(resolveStateDir(cwd).dir, 'config.json'));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function configPathLabel(cwd = process.cwd()) {
|
|
91
|
+
return `${resolveStateDir(cwd).name}/config.json`;
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
export function loadProjectModelConfig(cwd = process.cwd()) {
|
|
90
|
-
const configPath = join(cwd
|
|
95
|
+
const configPath = join(resolveStateDir(cwd).dir, 'config.json');
|
|
91
96
|
if (!existsSync(configPath)) return buildDefaultConfig();
|
|
92
97
|
|
|
93
98
|
try {
|
|
@@ -96,35 +101,37 @@ export function loadProjectModelConfig(cwd = process.cwd()) {
|
|
|
96
101
|
...JSON.parse(readFileSync(configPath, 'utf-8')),
|
|
97
102
|
};
|
|
98
103
|
} catch (e) {
|
|
99
|
-
console.error(`WARNING:
|
|
104
|
+
console.error(`WARNING: ${configPathLabel(cwd)} is malformed (${e.message}). Using defaults.`);
|
|
100
105
|
return buildDefaultConfig();
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
function loadConfigForMutation(cwd = process.cwd()) {
|
|
105
|
-
const
|
|
110
|
+
const state = resolveStateDir(cwd);
|
|
111
|
+
const configPath = join(state.dir, 'config.json');
|
|
112
|
+
const pathLabel = `${state.name}/config.json`;
|
|
106
113
|
let raw;
|
|
107
114
|
try {
|
|
108
115
|
raw = readFileSync(configPath, 'utf-8');
|
|
109
116
|
} catch (e) {
|
|
110
|
-
return { ok: false, error: `could not read config file (${e.message})` };
|
|
117
|
+
return { ok: false, pathLabel, error: `could not read config file (${e.message})` };
|
|
111
118
|
}
|
|
112
119
|
try {
|
|
113
|
-
return { ok: true, config: { ...buildDefaultConfig(), ...JSON.parse(raw) } };
|
|
120
|
+
return { ok: true, pathLabel, config: { ...buildDefaultConfig(), ...JSON.parse(raw) } };
|
|
114
121
|
} catch (e) {
|
|
115
|
-
return { ok: false, error: `malformed JSON (${e.message})` };
|
|
122
|
+
return { ok: false, pathLabel, error: `malformed JSON (${e.message})` };
|
|
116
123
|
}
|
|
117
124
|
}
|
|
118
125
|
|
|
119
126
|
export function ensureProjectConfig(cwd = process.cwd()) {
|
|
120
|
-
mkdirSync(
|
|
127
|
+
mkdirSync(resolveStateDir(cwd).dir, { recursive: true });
|
|
121
128
|
const config = loadProjectModelConfig(cwd);
|
|
122
129
|
writeProjectConfig(config, cwd);
|
|
123
130
|
return config;
|
|
124
131
|
}
|
|
125
132
|
|
|
126
133
|
export function writeProjectConfig(config, cwd = process.cwd()) {
|
|
127
|
-
const configPath = join(cwd
|
|
134
|
+
const configPath = join(resolveStateDir(cwd).dir, 'config.json');
|
|
128
135
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
129
136
|
}
|
|
130
137
|
|
|
@@ -272,7 +279,7 @@ function cmdModelsProfile(profile) {
|
|
|
272
279
|
|
|
273
280
|
const result = loadConfigForMutation();
|
|
274
281
|
if (!result.ok) {
|
|
275
|
-
console.error(`ERROR: .
|
|
282
|
+
console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
276
283
|
process.exitCode = 1;
|
|
277
284
|
return;
|
|
278
285
|
}
|
|
@@ -306,7 +313,7 @@ function cmdModelsAgentProfile(args) {
|
|
|
306
313
|
|
|
307
314
|
const result = loadConfigForMutation();
|
|
308
315
|
if (!result.ok) {
|
|
309
|
-
console.error(`ERROR: .
|
|
316
|
+
console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
310
317
|
process.exitCode = 1;
|
|
311
318
|
return;
|
|
312
319
|
}
|
|
@@ -334,7 +341,7 @@ function cmdModelsClearAgentProfile(args) {
|
|
|
334
341
|
|
|
335
342
|
const result = loadConfigForMutation();
|
|
336
343
|
if (!result.ok) {
|
|
337
|
-
console.error(`ERROR: .
|
|
344
|
+
console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
338
345
|
process.exitCode = 1;
|
|
339
346
|
return;
|
|
340
347
|
}
|
|
@@ -384,7 +391,7 @@ function cmdModelsSetRuntimeOverride(args) {
|
|
|
384
391
|
|
|
385
392
|
const result = loadConfigForMutation();
|
|
386
393
|
if (!result.ok) {
|
|
387
|
-
console.error(`ERROR: .
|
|
394
|
+
console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
388
395
|
process.exitCode = 1;
|
|
389
396
|
return;
|
|
390
397
|
}
|
|
@@ -420,7 +427,7 @@ function cmdModelsClearRuntimeOverride(args) {
|
|
|
420
427
|
|
|
421
428
|
const result = loadConfigForMutation();
|
|
422
429
|
if (!result.ok) {
|
|
423
|
-
console.error(`ERROR: .
|
|
430
|
+
console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running model mutations.`);
|
|
424
431
|
process.exitCode = 1;
|
|
425
432
|
return;
|
|
426
433
|
}
|
|
@@ -499,7 +506,7 @@ function cmdRigorSetProfile(level) {
|
|
|
499
506
|
}
|
|
500
507
|
const result = loadConfigForMutation();
|
|
501
508
|
if (!result.ok) {
|
|
502
|
-
console.error(`ERROR: .
|
|
509
|
+
console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running rigor mutations.`);
|
|
503
510
|
process.exitCode = 1;
|
|
504
511
|
return;
|
|
505
512
|
}
|
|
@@ -529,7 +536,7 @@ function cmdRigorSetStep(step, level) {
|
|
|
529
536
|
}
|
|
530
537
|
const result = loadConfigForMutation();
|
|
531
538
|
if (!result.ok) {
|
|
532
|
-
console.error(`ERROR: .
|
|
539
|
+
console.error(`ERROR: ${result.pathLabel} is malformed (${result.error}). Fix the file manually before running rigor mutations.`);
|
|
533
540
|
process.exitCode = 1;
|
|
534
541
|
return;
|
|
535
542
|
}
|