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/lib/health.mjs
CHANGED
|
@@ -10,15 +10,18 @@ import { output } from './cli-utils.mjs';
|
|
|
10
10
|
import { runTruthChecks, TRUTH_CHECK_IDS } from './health-truth.mjs';
|
|
11
11
|
import { evaluateLifecycleState } from './lifecycle-state.mjs';
|
|
12
12
|
import { evaluateRuntimeFreshness } from './runtime-freshness.mjs';
|
|
13
|
-
import { findUiProofBundleFiles, readUiProofBundleFile, validateUiProofBundle } from './ui-proof.mjs';
|
|
14
13
|
import { resolveWorkspaceContext } from './workspace-root.mjs';
|
|
15
14
|
|
|
15
|
+
function statePath(stateDirName, relativePath = '') {
|
|
16
|
+
return relativePath ? `${stateDirName}/${relativePath}` : stateDirName;
|
|
17
|
+
}
|
|
18
|
+
|
|
16
19
|
/**
|
|
17
20
|
* Build the structured health report without printing or mutating workspace
|
|
18
21
|
* state. ctx should provide: { frameworkVersion, workflows }.
|
|
19
22
|
*/
|
|
20
23
|
export function buildHealthReport(ctx, healthArgs = []) {
|
|
21
|
-
const { planningDir, workspaceRoot, invalid, error } = resolveWorkspaceContext(healthArgs);
|
|
24
|
+
const { planningDir, workspaceRoot, invalid, error, stateDirName = '.work' } = resolveWorkspaceContext(healthArgs);
|
|
22
25
|
if (invalid) {
|
|
23
26
|
return {
|
|
24
27
|
status: 'broken',
|
|
@@ -30,13 +33,13 @@ export function buildHealthReport(ctx, healthArgs = []) {
|
|
|
30
33
|
}
|
|
31
34
|
const cwd = workspaceRoot;
|
|
32
35
|
const frameworkSourceMode = isFrameworkSourceRepo(cwd);
|
|
33
|
-
const healthCheckIds = ['E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7', 'E8', 'E9', '
|
|
36
|
+
const healthCheckIds = ['E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7', 'E8', 'E9', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', ...TRUTH_CHECK_IDS, 'I1', 'I2', 'I3'];
|
|
34
37
|
|
|
35
38
|
// Pre-init guard
|
|
36
39
|
if (!existsSync(join(planningDir, 'config.json'))) {
|
|
37
40
|
return {
|
|
38
41
|
status: 'broken',
|
|
39
|
-
errors: [{ id: 'E1', severity: 'ERROR', message: '
|
|
42
|
+
errors: [{ id: 'E1', severity: 'ERROR', message: `${statePath(stateDirName, 'config.json')} missing`, fix: 'Run `npx -y gsdd-cli init`' }],
|
|
40
43
|
warnings: [],
|
|
41
44
|
info: [],
|
|
42
45
|
humanMessage: 'Not initialized. Run `npx -y gsdd-cli init`. If `gsdd` is installed globally, `gsdd init` is also fine.',
|
|
@@ -62,7 +65,7 @@ export function buildHealthReport(ctx, healthArgs = []) {
|
|
|
62
65
|
errors.push({ id: 'E2', severity: 'ERROR', message: `config.json missing required fields: ${missing.join(', ')}`, fix: 'Run `npx -y gsdd-cli init` to regenerate' });
|
|
63
66
|
}
|
|
64
67
|
} catch {
|
|
65
|
-
errors.push({ id: 'E1', severity: 'ERROR', message: '
|
|
68
|
+
errors.push({ id: 'E1', severity: 'ERROR', message: `${statePath(stateDirName, 'config.json')} is unparseable`, fix: 'Run `npx -y gsdd-cli init`' });
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
// E3: templates/ missing
|
|
@@ -77,47 +80,47 @@ export function buildHealthReport(ctx, healthArgs = []) {
|
|
|
77
80
|
const skipInstalledTemplateChecks = !hasTemplatesDir && frameworkSourceMode;
|
|
78
81
|
|
|
79
82
|
if (!hasTemplatesDir && !skipInstalledTemplateChecks) {
|
|
80
|
-
errors.push({ id: 'E3', severity: 'ERROR', message: '
|
|
83
|
+
errors.push({ id: 'E3', severity: 'ERROR', message: `${statePath(stateDirName, 'templates/')} missing`, fix: 'Run `npx -y gsdd-cli update --templates`' });
|
|
81
84
|
} else if (hasTemplatesDir) {
|
|
82
85
|
// E4: roles/ missing or empty
|
|
83
86
|
if (!hasRolesDir) {
|
|
84
|
-
errors.push({ id: 'E4', severity: 'ERROR', message: '
|
|
87
|
+
errors.push({ id: 'E4', severity: 'ERROR', message: `${statePath(stateDirName, 'templates/roles/')} missing`, fix: 'Run `npx -y gsdd-cli update --templates`' });
|
|
85
88
|
} else {
|
|
86
89
|
const roleFiles = readdirSync(rolesDir).filter((f) => f.endsWith('.md'));
|
|
87
90
|
if (roleFiles.length === 0) {
|
|
88
|
-
errors.push({ id: 'E4', severity: 'ERROR', message: '
|
|
91
|
+
errors.push({ id: 'E4', severity: 'ERROR', message: `${statePath(stateDirName, 'templates/roles/')} has 0 role files`, fix: 'Run `npx -y gsdd-cli update --templates`' });
|
|
89
92
|
}
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
// E5: delegates/ missing or empty
|
|
93
96
|
if (!hasDelegatesDir) {
|
|
94
|
-
errors.push({ id: 'E5', severity: 'ERROR', message: '
|
|
97
|
+
errors.push({ id: 'E5', severity: 'ERROR', message: `${statePath(stateDirName, 'templates/delegates/')} missing`, fix: 'Run `npx -y gsdd-cli update --templates`' });
|
|
95
98
|
} else {
|
|
96
99
|
const delegateFiles = readdirSync(delegatesDir).filter((f) => f.endsWith('.md'));
|
|
97
100
|
if (delegateFiles.length === 0) {
|
|
98
|
-
errors.push({ id: 'E5', severity: 'ERROR', message: '
|
|
101
|
+
errors.push({ id: 'E5', severity: 'ERROR', message: `${statePath(stateDirName, 'templates/delegates/')} has 0 delegate files`, fix: 'Run `npx -y gsdd-cli update --templates`' });
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
// E6: research/ missing or empty
|
|
103
106
|
const researchDir = join(templatesDir, 'research');
|
|
104
107
|
if (!existsSync(researchDir)) {
|
|
105
|
-
errors.push({ id: 'E6', severity: 'ERROR', message: '
|
|
108
|
+
errors.push({ id: 'E6', severity: 'ERROR', message: `${statePath(stateDirName, 'templates/research/')} missing`, fix: 'Run `npx -y gsdd-cli update --templates`' });
|
|
106
109
|
} else {
|
|
107
110
|
const researchFiles = readdirSync(researchDir).filter((f) => f.endsWith('.md'));
|
|
108
111
|
if (researchFiles.length === 0) {
|
|
109
|
-
errors.push({ id: 'E6', severity: 'ERROR', message: '
|
|
112
|
+
errors.push({ id: 'E6', severity: 'ERROR', message: `${statePath(stateDirName, 'templates/research/')} has 0 template files`, fix: 'Run `npx -y gsdd-cli update --templates`' });
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
// E7: codebase/ missing or empty
|
|
114
117
|
const codebaseDir = join(templatesDir, 'codebase');
|
|
115
118
|
if (!existsSync(codebaseDir)) {
|
|
116
|
-
errors.push({ id: 'E7', severity: 'ERROR', message: '
|
|
119
|
+
errors.push({ id: 'E7', severity: 'ERROR', message: `${statePath(stateDirName, 'templates/codebase/')} missing`, fix: 'Run `npx -y gsdd-cli update --templates`' });
|
|
117
120
|
} else {
|
|
118
121
|
const codebaseFiles = readdirSync(codebaseDir).filter((f) => f.endsWith('.md'));
|
|
119
122
|
if (codebaseFiles.length === 0) {
|
|
120
|
-
errors.push({ id: 'E7', severity: 'ERROR', message: '
|
|
123
|
+
errors.push({ id: 'E7', severity: 'ERROR', message: `${statePath(stateDirName, 'templates/codebase/')} has 0 template files`, fix: 'Run `npx -y gsdd-cli update --templates`' });
|
|
121
124
|
}
|
|
122
125
|
}
|
|
123
126
|
|
|
@@ -125,37 +128,20 @@ export function buildHealthReport(ctx, healthArgs = []) {
|
|
|
125
128
|
const requiredRootFiles = ['spec.md', 'roadmap.md', 'auth-matrix.md', 'ui-proof.md'];
|
|
126
129
|
const missingRoot = requiredRootFiles.filter((f) => !existsSync(join(templatesDir, f)));
|
|
127
130
|
if (missingRoot.length > 0) {
|
|
128
|
-
errors.push({ id: 'E8', severity: 'ERROR', message:
|
|
131
|
+
errors.push({ id: 'E8', severity: 'ERROR', message: `${statePath(stateDirName, 'templates/')} missing critical root files: ${missingRoot.join(', ')}`, fix: 'Run `npx -y gsdd-cli update --templates`' });
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
const brownfieldChangeDir = join(templatesDir, 'brownfield-change');
|
|
132
135
|
if (!existsSync(brownfieldChangeDir)) {
|
|
133
|
-
errors.push({ id: 'E9', severity: 'ERROR', message: '
|
|
136
|
+
errors.push({ id: 'E9', severity: 'ERROR', message: `${statePath(stateDirName, 'templates/brownfield-change/')} missing`, fix: 'Run `npx -y gsdd-cli update --templates`' });
|
|
134
137
|
} else {
|
|
135
138
|
const missingBrownfield = ['CHANGE.md', 'HANDOFF.md', 'VERIFICATION.md'].filter((file) => !existsSync(join(brownfieldChangeDir, file)));
|
|
136
139
|
if (missingBrownfield.length > 0) {
|
|
137
|
-
errors.push({ id: 'E9', severity: 'ERROR', message:
|
|
140
|
+
errors.push({ id: 'E9', severity: 'ERROR', message: `${statePath(stateDirName, 'templates/brownfield-change/')} missing critical files: ${missingBrownfield.join(', ')}`, fix: 'Run `npx -y gsdd-cli update --templates`' });
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
143
|
}
|
|
141
144
|
|
|
142
|
-
// E10: known UI proof bundles must satisfy deterministic metadata/privacy validation.
|
|
143
|
-
for (const bundlePath of findUiProofBundleFiles(planningDir)) {
|
|
144
|
-
const relativePath = relative(cwd, bundlePath).replace(/\\/g, '/');
|
|
145
|
-
const parsed = readUiProofBundleFile(bundlePath);
|
|
146
|
-
const validation = parsed.errors.length > 0
|
|
147
|
-
? { valid: false, errors: parsed.errors }
|
|
148
|
-
: validateUiProofBundle(parsed.bundle, { requireLocalArtifactExists: true, workspaceRoot: cwd });
|
|
149
|
-
if (!validation.valid) {
|
|
150
|
-
errors.push({
|
|
151
|
-
id: 'E10',
|
|
152
|
-
severity: 'ERROR',
|
|
153
|
-
message: `${relativePath} has invalid UI proof metadata (${validation.errors.map((entry) => entry.code).join(', ')})`,
|
|
154
|
-
fix: 'Run `gsdd ui-proof validate <path>` and add required privacy metadata, claim limits, fixed evidence kinds, concise tool provenance, failure classification when failed or partial, observation artifact references, existing local artifact paths, and safe-to-publish handling.',
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
145
|
// --- WARNING checks ---
|
|
160
146
|
|
|
161
147
|
// W1: generation-manifest.json missing
|
|
@@ -188,7 +174,7 @@ export function buildHealthReport(ctx, healthArgs = []) {
|
|
|
188
174
|
}
|
|
189
175
|
}
|
|
190
176
|
|
|
191
|
-
// W4: ROADMAP.md references phases not found in .
|
|
177
|
+
// W4: ROADMAP.md references phases not found in the resolved state phases directory.
|
|
192
178
|
const roadmapPath = join(planningDir, 'ROADMAP.md');
|
|
193
179
|
const phasesDir = join(planningDir, 'phases');
|
|
194
180
|
const roadmap = existsSync(roadmapPath) ? readFileSync(roadmapPath, 'utf-8') : null;
|
|
@@ -199,7 +185,7 @@ export function buildHealthReport(ctx, healthArgs = []) {
|
|
|
199
185
|
warnings.push({
|
|
200
186
|
id: 'W4',
|
|
201
187
|
severity: 'WARN',
|
|
202
|
-
message: `ROADMAP.md references active Phase ${phase.number} but no files found in
|
|
188
|
+
message: `ROADMAP.md references active Phase ${phase.number} but no files found in ${statePath(stateDirName, 'phases/')}`,
|
|
203
189
|
fix: 'Create missing phase dirs or update ROADMAP',
|
|
204
190
|
});
|
|
205
191
|
}
|
|
@@ -208,7 +194,7 @@ export function buildHealthReport(ctx, healthArgs = []) {
|
|
|
208
194
|
// W5: Phase dir has PLAN but no SUMMARY (stale in-progress)
|
|
209
195
|
if (lifecycle.incompletePlans.length > 0) {
|
|
210
196
|
for (const plan of lifecycle.incompletePlans) {
|
|
211
|
-
const expectedSummary =
|
|
197
|
+
const expectedSummary = statePath(stateDirName, `phases/${plan.dir}/${plan.baseId}-SUMMARY.md`);
|
|
212
198
|
warnings.push({
|
|
213
199
|
id: 'W5',
|
|
214
200
|
severity: 'WARN',
|
|
@@ -227,7 +213,7 @@ export function buildHealthReport(ctx, healthArgs = []) {
|
|
|
227
213
|
? evaluateRuntimeFreshness({ cwd, workflows: ctx.workflows })
|
|
228
214
|
: null;
|
|
229
215
|
|
|
230
|
-
warnings.push(...runTruthChecks(planningDir, cwd, healthCheckIds, { runtimeFreshnessReport }).map((warning) => {
|
|
216
|
+
warnings.push(...runTruthChecks(planningDir, cwd, healthCheckIds, { runtimeFreshnessReport, stateDirName }).map((warning) => {
|
|
231
217
|
if (warning.id !== 'W10') return warning;
|
|
232
218
|
return {
|
|
233
219
|
...warning,
|
|
@@ -235,7 +221,7 @@ export function buildHealthReport(ctx, healthArgs = []) {
|
|
|
235
221
|
/^ROADMAP\/SPEC requirement status drift/,
|
|
236
222
|
'ROADMAP lifecycle status drift (requirement checkbox and/or overview/detail phase status mismatch)'
|
|
237
223
|
),
|
|
238
|
-
fix:
|
|
224
|
+
fix: `Reconcile ${statePath(stateDirName, 'ROADMAP.md')} overview/detail phase markers and ${statePath(stateDirName, 'SPEC.md')} requirement checkboxes`,
|
|
239
225
|
};
|
|
240
226
|
}));
|
|
241
227
|
|
package/bin/lib/init-flow.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { dirname, join, isAbsolute } from 'path';
|
|
|
3
3
|
import { buildPlanningCliHelperEntries, renderSkillContent } from './rendering.mjs';
|
|
4
4
|
import { buildManifest, readManifest, writeManifest } from './manifest.mjs';
|
|
5
5
|
import { parseFlagValue, parseToolsFlag, parseAutoFlag } from './cli-utils.mjs';
|
|
6
|
-
import { buildDefaultConfig, COST_PROFILES, RIGOR_PROFILES } from './
|
|
6
|
+
import { buildDefaultConfig, COST_PROFILES, RIGOR_PROFILES } from './config.mjs';
|
|
7
7
|
import { installProjectTemplates, refreshTemplates } from './templates.mjs';
|
|
8
8
|
import {
|
|
9
9
|
detectPlatforms,
|
|
@@ -84,34 +84,36 @@ export function createCmdInit(ctx) {
|
|
|
84
84
|
parsedTools,
|
|
85
85
|
isAuto,
|
|
86
86
|
});
|
|
87
|
+
const { planningDir, stateDirName } = ctx;
|
|
87
88
|
|
|
88
|
-
const existed = existsSync(
|
|
89
|
-
mkdirSync(join(
|
|
90
|
-
mkdirSync(join(
|
|
89
|
+
const existed = existsSync(planningDir);
|
|
90
|
+
mkdirSync(join(planningDir, 'phases'), { recursive: true });
|
|
91
|
+
mkdirSync(join(planningDir, 'research'), { recursive: true });
|
|
91
92
|
console.log(existed
|
|
92
|
-
?
|
|
93
|
-
:
|
|
93
|
+
? ` - ${stateDirName}/ already exists (ensured subdirectories)`
|
|
94
|
+
: ` - created ${stateDirName}/ directory structure`);
|
|
94
95
|
|
|
95
96
|
installProjectTemplates(ctx);
|
|
96
97
|
await ensureConfig({
|
|
97
98
|
cwd: ctx.cwd,
|
|
98
|
-
planningDir
|
|
99
|
+
planningDir,
|
|
99
100
|
isAuto,
|
|
100
101
|
promptApi,
|
|
101
102
|
preselectedConfig: interactiveSession.config,
|
|
103
|
+
stateDirName,
|
|
102
104
|
});
|
|
103
|
-
ensureGitignoreEntry(ctx.cwd,
|
|
105
|
+
ensureGitignoreEntry(ctx.cwd, `${stateDirName}/.local/`, ` - ensured ${stateDirName}/.local/ is gitignored`);
|
|
104
106
|
|
|
105
107
|
if (briefSource) {
|
|
106
|
-
cpSync(briefSource, join(
|
|
107
|
-
console.log(
|
|
108
|
+
cpSync(briefSource, join(planningDir, 'PROJECT_BRIEF.md'));
|
|
109
|
+
console.log(` - copied project brief to ${stateDirName}/PROJECT_BRIEF.md`);
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
generateOpenStandardSkills(ctx.cwd, ctx.workflows);
|
|
112
|
+
generateOpenStandardSkills(ctx.cwd, ctx.workflows, { stateDirName });
|
|
111
113
|
console.log(' - generated open-standard skills (.agents/skills/gsdd-*)');
|
|
112
114
|
|
|
113
115
|
generatePlanningCliHelpers(ctx);
|
|
114
|
-
console.log(
|
|
116
|
+
console.log(` - generated local workflow helpers (${stateDirName}/bin/gsdd*)`);
|
|
115
117
|
|
|
116
118
|
for (const adapter of resolveAdapters(ctx.adapters, interactiveSession.adapterTargets)) {
|
|
117
119
|
adapter.generate();
|
|
@@ -119,8 +121,8 @@ export function createCmdInit(ctx) {
|
|
|
119
121
|
console.log(` - ${adapter.summary('generated')}`);
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
const manifest = buildManifest({ planningDir
|
|
123
|
-
writeManifest(
|
|
124
|
+
const manifest = buildManifest({ planningDir, frameworkVersion: ctx.frameworkVersion });
|
|
125
|
+
writeManifest(planningDir, manifest);
|
|
124
126
|
console.log(' - wrote generation manifest');
|
|
125
127
|
|
|
126
128
|
console.log('\n\x1B[1m\x1B[32m✓ GSDD initialized.\x1B[0m');
|
|
@@ -135,6 +137,7 @@ export function createCmdUpdate(ctx) {
|
|
|
135
137
|
return function cmdUpdate(...updateArgs) {
|
|
136
138
|
const isDry = updateArgs.includes('--dry');
|
|
137
139
|
const doTemplates = updateArgs.includes('--templates');
|
|
140
|
+
const { planningDir, stateDirName } = ctx;
|
|
138
141
|
|
|
139
142
|
console.log(`gsdd update - regenerating adapter files${isDry ? ' (dry run)' : ''}\n`);
|
|
140
143
|
|
|
@@ -149,22 +152,22 @@ export function createCmdUpdate(ctx) {
|
|
|
149
152
|
updated = true;
|
|
150
153
|
}
|
|
151
154
|
|
|
152
|
-
if (platforms.length > 0 || existsSync(
|
|
155
|
+
if (platforms.length > 0 || existsSync(planningDir) || hasGeneratedOpenStandardSkills(ctx.cwd)) {
|
|
153
156
|
if (isDry) {
|
|
154
157
|
console.log(' - would update open-standard skills (.agents/skills/gsdd-*)');
|
|
155
158
|
} else {
|
|
156
|
-
generateOpenStandardSkills(ctx.cwd, ctx.workflows);
|
|
159
|
+
generateOpenStandardSkills(ctx.cwd, ctx.workflows, { stateDirName });
|
|
157
160
|
console.log(' - updated open-standard skills (.agents/skills/gsdd-*)');
|
|
158
161
|
}
|
|
159
162
|
updated = true;
|
|
160
163
|
}
|
|
161
164
|
|
|
162
|
-
if (existsSync(
|
|
165
|
+
if (existsSync(planningDir)) {
|
|
163
166
|
if (isDry) {
|
|
164
|
-
console.log(
|
|
167
|
+
console.log(` - would update local workflow helpers (${stateDirName}/bin/gsdd*)`);
|
|
165
168
|
} else {
|
|
166
169
|
generatePlanningCliHelpers(ctx);
|
|
167
|
-
console.log(
|
|
170
|
+
console.log(` - updated local workflow helpers (${stateDirName}/bin/gsdd*)`);
|
|
168
171
|
}
|
|
169
172
|
updated = true;
|
|
170
173
|
}
|
|
@@ -185,14 +188,14 @@ export function createCmdUpdate(ctx) {
|
|
|
185
188
|
} else if (isDry) {
|
|
186
189
|
console.log('\nDry run complete. No files were written.\n');
|
|
187
190
|
} else {
|
|
188
|
-
if (existsSync(
|
|
191
|
+
if (existsSync(planningDir)) {
|
|
189
192
|
const manifest = buildUpdateManifest({
|
|
190
|
-
planningDir
|
|
193
|
+
planningDir,
|
|
191
194
|
frameworkVersion: ctx.frameworkVersion,
|
|
192
195
|
updateTemplates: doTemplates,
|
|
193
196
|
});
|
|
194
197
|
if (manifest) {
|
|
195
|
-
writeManifest(
|
|
198
|
+
writeManifest(planningDir, manifest);
|
|
196
199
|
console.log(' - updated generation manifest');
|
|
197
200
|
}
|
|
198
201
|
}
|
|
@@ -216,20 +219,21 @@ function hasGeneratedOpenStandardSkills(cwd) {
|
|
|
216
219
|
}
|
|
217
220
|
}
|
|
218
221
|
|
|
219
|
-
function generateOpenStandardSkills(cwd, workflows) {
|
|
222
|
+
function generateOpenStandardSkills(cwd, workflows, { stateDirName = '.work' } = {}) {
|
|
220
223
|
for (const workflow of workflows) {
|
|
221
224
|
const dir = join(cwd, '.agents', 'skills', workflow.name);
|
|
222
225
|
mkdirSync(dir, { recursive: true });
|
|
223
|
-
writeFileSync(join(dir, 'SKILL.md'), renderSkillContent(workflow));
|
|
226
|
+
writeFileSync(join(dir, 'SKILL.md'), renderSkillContent(workflow, { stateDirName }));
|
|
224
227
|
}
|
|
225
228
|
}
|
|
226
229
|
|
|
227
|
-
function generatePlanningCliHelpers(
|
|
230
|
+
function generatePlanningCliHelpers({ packageName, packageVersion, planningDir, stateDirName = '.work' }) {
|
|
228
231
|
for (const entry of buildPlanningCliHelperEntries({
|
|
229
|
-
packageName
|
|
230
|
-
packageVersion
|
|
232
|
+
packageName,
|
|
233
|
+
packageVersion,
|
|
234
|
+
stateDirName,
|
|
231
235
|
})) {
|
|
232
|
-
const absolutePath = join(
|
|
236
|
+
const absolutePath = join(planningDir, entry.relativePath);
|
|
233
237
|
mkdirSync(dirname(absolutePath), { recursive: true });
|
|
234
238
|
writeFileSync(absolutePath, entry.content);
|
|
235
239
|
if (!absolutePath.endsWith('.cmd')) {
|
|
@@ -264,33 +268,35 @@ function stripManifestTimestamp(manifest) {
|
|
|
264
268
|
return rest;
|
|
265
269
|
}
|
|
266
270
|
|
|
267
|
-
async function ensureConfig({ cwd, planningDir, isAuto, promptApi, preselectedConfig = null }) {
|
|
271
|
+
async function ensureConfig({ cwd, planningDir, stateDirName = '.work', isAuto, promptApi, preselectedConfig = null }) {
|
|
268
272
|
const configFile = join(planningDir, 'config.json');
|
|
273
|
+
const ignoreEntry = `${stateDirName}/`;
|
|
274
|
+
const ignoreMsg = ` - ensured ${stateDirName}/ is gitignored`;
|
|
269
275
|
if (existsSync(configFile)) {
|
|
270
|
-
console.log(
|
|
276
|
+
console.log(` - ${stateDirName}/config.json already exists`);
|
|
271
277
|
return;
|
|
272
278
|
}
|
|
273
279
|
|
|
274
280
|
if (preselectedConfig) {
|
|
275
281
|
writeFileSync(configFile, JSON.stringify(preselectedConfig, null, 2));
|
|
276
|
-
console.log(
|
|
277
|
-
if (!preselectedConfig.commitDocs) ensureGitignoreEntry(cwd,
|
|
282
|
+
console.log(` - saved ${stateDirName}/config.json (guided wizard)\n`);
|
|
283
|
+
if (!preselectedConfig.commitDocs) ensureGitignoreEntry(cwd, ignoreEntry, ignoreMsg);
|
|
278
284
|
return;
|
|
279
285
|
}
|
|
280
286
|
|
|
281
287
|
if (isAuto) {
|
|
282
288
|
const config = buildDefaultConfig({ autoAdvance: true });
|
|
283
289
|
writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
284
|
-
console.log(
|
|
285
|
-
if (!config.commitDocs) ensureGitignoreEntry(cwd,
|
|
290
|
+
console.log(` - wrote ${stateDirName}/config.json (auto defaults)\n`);
|
|
291
|
+
if (!config.commitDocs) ensureGitignoreEntry(cwd, ignoreEntry, ignoreMsg);
|
|
286
292
|
return;
|
|
287
293
|
}
|
|
288
294
|
|
|
289
295
|
if (!process.stdin.isTTY) {
|
|
290
296
|
const config = buildDefaultConfig({ autoAdvance: false });
|
|
291
297
|
writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
292
|
-
console.log(
|
|
293
|
-
if (!config.commitDocs) ensureGitignoreEntry(cwd,
|
|
298
|
+
console.log(` - wrote ${stateDirName}/config.json (non-interactive defaults)\n`);
|
|
299
|
+
if (!config.commitDocs) ensureGitignoreEntry(cwd, ignoreEntry, ignoreMsg);
|
|
294
300
|
return;
|
|
295
301
|
}
|
|
296
302
|
|
|
@@ -303,8 +309,8 @@ async function ensureConfig({ cwd, planningDir, isAuto, promptApi, preselectedCo
|
|
|
303
309
|
}
|
|
304
310
|
|
|
305
311
|
writeFileSync(configFile, JSON.stringify(selected, null, 2));
|
|
306
|
-
console.log(
|
|
307
|
-
if (!selected.commitDocs) ensureGitignoreEntry(cwd,
|
|
312
|
+
console.log(` - saved ${stateDirName}/config.json (guided wizard)\n`);
|
|
313
|
+
if (!selected.commitDocs) ensureGitignoreEntry(cwd, ignoreEntry, ignoreMsg);
|
|
308
314
|
}
|
|
309
315
|
|
|
310
316
|
function ensureGitignoreEntry(cwd, entry, message) {
|
package/bin/lib/init-prompts.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as readline from 'readline';
|
|
2
|
-
import { DEFAULT_GIT_PROTOCOL, resolveCost, resolveRigor } from './
|
|
2
|
+
import { DEFAULT_GIT_PROTOCOL, resolveCost, resolveRigor } from './config.mjs';
|
|
3
3
|
import { buildRuntimeChoices, INIT_VERSION, resolveWizardAdapterTargets } from './init-runtime.mjs';
|
|
4
4
|
|
|
5
5
|
const ANSI = {
|
|
@@ -79,8 +79,8 @@ export async function promptForConfig(cwd, { input = process.stdin, output = pro
|
|
|
79
79
|
output,
|
|
80
80
|
title: 'Planning docs in git',
|
|
81
81
|
choices: [
|
|
82
|
-
{ value: true, label: 'yes', description: 'Track .
|
|
83
|
-
{ value: false, label: 'no', description: 'Keep .
|
|
82
|
+
{ value: true, label: 'yes', description: 'Track .work/ in git.' },
|
|
83
|
+
{ value: false, label: 'no', description: 'Keep .work/ local only.' },
|
|
84
84
|
],
|
|
85
85
|
defaultIndex: 0,
|
|
86
86
|
});
|
package/bin/lib/init-runtime.mjs
CHANGED
|
@@ -2,19 +2,19 @@ const RUNTIME_OPTIONS = [
|
|
|
2
2
|
{
|
|
3
3
|
id: 'claude',
|
|
4
4
|
label: 'Claude Code',
|
|
5
|
-
description: '
|
|
5
|
+
description: 'Recorded proof for native skills, commands, and agents with local freshness checks',
|
|
6
6
|
kind: 'native',
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
id: 'opencode',
|
|
10
10
|
label: 'OpenCode',
|
|
11
|
-
description: '
|
|
11
|
+
description: 'Recorded proof for native slash commands and agents with local freshness checks',
|
|
12
12
|
kind: 'native',
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
id: 'codex',
|
|
16
16
|
label: 'Codex CLI',
|
|
17
|
-
description: '
|
|
17
|
+
description: 'Recorded proof for portable skills plus native checker agents with local freshness checks',
|
|
18
18
|
kind: 'native',
|
|
19
19
|
},
|
|
20
20
|
{
|
|
@@ -163,7 +163,7 @@ export function getPostInitRoutingLines(selectedRuntimes) {
|
|
|
163
163
|
export function getHelpText() {
|
|
164
164
|
return `
|
|
165
165
|
gsdd - Workspine CLI
|
|
166
|
-
|
|
166
|
+
Plan, execute, and verify AI-assisted work from files in your repo — with proof before "done".
|
|
167
167
|
|
|
168
168
|
Usage: gsdd <command> [args]
|
|
169
169
|
|
|
@@ -171,14 +171,14 @@ Commands:
|
|
|
171
171
|
init [--tools <platform>] [--auto] [--brief <file>]
|
|
172
172
|
Launch guided install wizard in TTYs, or use --tools for manual/headless setup
|
|
173
173
|
--auto: non-interactive mode with smart defaults (requires --tools)
|
|
174
|
-
--brief <file>: copy project brief to .
|
|
174
|
+
--brief <file>: copy project brief to .work/PROJECT_BRIEF.md
|
|
175
175
|
install --global [--auto] [--tools <platform>] [--dry]
|
|
176
176
|
Install reusable Workspine skills and native runtime surfaces into agent home directories
|
|
177
177
|
--auto: non-interactive mode that installs detected local agent targets
|
|
178
178
|
In TTYs, omitting --tools opens an agent picker
|
|
179
179
|
update [--tools <platform>] [--templates] [--dry]
|
|
180
180
|
Regenerate adapters from latest framework sources
|
|
181
|
-
--templates: also refresh .
|
|
181
|
+
--templates: also refresh .work/templates/ and roles
|
|
182
182
|
--dry: preview changes without writing files
|
|
183
183
|
health [--json] Check workspace integrity (healthy/degraded/broken)
|
|
184
184
|
next [--json] [--format auto|json|human] [--init]
|
|
@@ -192,18 +192,6 @@ Commands:
|
|
|
192
192
|
phase-status <N> <status> Update ROADMAP.md phase status ([ ] / [-] / [x])
|
|
193
193
|
lifecycle-preflight <surface> [phase]
|
|
194
194
|
Inspect deterministic lifecycle gate results for a workflow surface
|
|
195
|
-
session-fingerprint write [--allow-changed <ROADMAP.md,SPEC.md,config.json>]
|
|
196
|
-
Rebaseline planning-state drift after reviewing changed planning files
|
|
197
|
-
ui-proof validate <path> [--claim <public|publication|tracked|delivery|release>]
|
|
198
|
-
Validate UI proof metadata; use --claim for stronger proof uses
|
|
199
|
-
ui-proof compare <planned-slots-json> [observed-bundle-json ...]
|
|
200
|
-
Compare planned UI proof slots against observed bundles
|
|
201
|
-
control-map [--json] [--with-ignored] [--annotations <path>]
|
|
202
|
-
Report computed repo/worktree/planning state and local annotations
|
|
203
|
-
control-map annotate <set|clear>
|
|
204
|
-
Maintain optional local intent annotations under .planning/.local/
|
|
205
|
-
closeout-report [--json] [--phase <N>]
|
|
206
|
-
Replay read-only closeout status from control-map, health, preflight, verify, and UI-proof signals
|
|
207
195
|
help Show this summary
|
|
208
196
|
|
|
209
197
|
Platforms (for --tools):
|
|
@@ -226,17 +214,17 @@ Global install targets:
|
|
|
226
214
|
Notes:
|
|
227
215
|
- use \`npx -y gsdd-cli init\` for repo-local setup; use \`npx -y gsdd-cli install --global --auto\` when you want reusable skills in detected agent homes
|
|
228
216
|
- init always generates open-standard skills at .agents/skills/gsdd-*; this is the shared workflow entry surface
|
|
229
|
-
- init also generates a local .
|
|
230
|
-
- install --global never creates .
|
|
217
|
+
- init also generates a local .work/bin/gsdd* helper surface for workflow-embedded lifecycle helpers; it is internal/advanced, not the normal first-run user entrypoint
|
|
218
|
+
- install --global never creates .work/ in the current repo; it writes only selected agent-home surfaces and per-runtime Workspine manifests
|
|
231
219
|
- use \`npx -y gsdd-cli install --global --auto\` for non-interactive global install into detected agent homes; use \`--tools <targets>\` to override detection explicitly
|
|
232
220
|
- repair or refresh a global install by rerunning \`npx -y gsdd-cli install --global --auto\` or \`npx -y gsdd-cli install --global --tools <targets>\`; runtime probes stay in test harnesses
|
|
233
|
-
- Workspine is the public product name; the retained package, command, workflow, and workspace contracts stay gsdd-cli, gsdd, gsdd-*, and .planning
|
|
221
|
+
- Workspine is the public product name; the retained package, command, workflow, and workspace contracts stay gsdd-cli, gsdd, gsdd-*, and .work/ (legacy planning workspaces are still read)
|
|
234
222
|
- running \`npx -y gsdd-cli init\` in a terminal opens the guided runtime-selection wizard; bare \`gsdd init\` is equivalent only when globally installed
|
|
235
223
|
- the wizard lets you pick runtimes first, then separately decide whether repo-wide AGENTS.md governance is worth installing
|
|
236
|
-
- \`npx -y gsdd-cli health\` is for repo-local .
|
|
224
|
+
- \`npx -y gsdd-cli health\` is for repo-local .work/ workspaces; it compares local generated surfaces and points back to \`npx -y gsdd-cli update\` when they drift
|
|
237
225
|
- \`npx -y gsdd-cli next --init\` bootstraps the local .work continuity surface; plain \`next\` is read-only and emits a typed next-action packet
|
|
238
226
|
- \`gsdd next\` defaults to JSON when stdout is captured; use \`--format human\` for the compact supervisor card
|
|
239
|
-
-
|
|
227
|
+
- recorded launch proof in this repo currently covers Claude Code, OpenCode, and Codex CLI paths
|
|
240
228
|
- Cursor, Copilot, and Gemini are qualified support through the shared .agents/skills/ surface plus optional governance
|
|
241
229
|
- --tools remains the advanced/manual path and preserves legacy runtime aliases for backward compatibility
|
|
242
230
|
- --tools codex generates .codex/agents/gsdd-plan-checker.toml (portable skill is the entry surface; $gsdd-plan is plan-only until explicit $gsdd-execute)
|
|
@@ -266,8 +254,6 @@ Examples:
|
|
|
266
254
|
npx -y gsdd-cli next --init
|
|
267
255
|
npx -y gsdd-cli find-phase
|
|
268
256
|
npx -y gsdd-cli verify 1
|
|
269
|
-
npx -y gsdd-cli control-map annotate set --id canonical --write-set src/app.ts
|
|
270
|
-
npx -y gsdd-cli control-map annotate clear --id canonical
|
|
271
257
|
npx -y gsdd-cli scaffold phase 4 Payments
|
|
272
258
|
|
|
273
259
|
Workflows (run via skills/adapters generated by init, not direct CLI):
|
|
@@ -280,7 +266,6 @@ Workflows (run via skills/adapters generated by init, not direct CLI):
|
|
|
280
266
|
gsdd-audit-milestone Cross-phase integration, requirements coverage, and E2E audit
|
|
281
267
|
gsdd-complete-milestone Archive a shipped milestone and collapse roadmap state
|
|
282
268
|
gsdd-new-milestone Start the next milestone with goals, requirements, and phases
|
|
283
|
-
gsdd-plan-milestone-gaps Turn milestone-audit gaps into closure phases
|
|
284
269
|
gsdd-quick Bounded brownfield lane for sub-hour work
|
|
285
270
|
gsdd-pause Save session context to checkpoint
|
|
286
271
|
gsdd-resume Restore context and route to the next action
|
|
@@ -293,11 +278,7 @@ Starting lanes after init:
|
|
|
293
278
|
|
|
294
279
|
Advanced/internal helpers (kept available, but not the primary first-run user story):
|
|
295
280
|
lifecycle-preflight Inspect deterministic lifecycle gate results for a workflow surface
|
|
296
|
-
session-fingerprint Rebaseline the local planning-state fingerprint after review
|
|
297
281
|
phase-status Update ROADMAP.md phase status through the local helper surface
|
|
298
|
-
ui-proof Validate UI proof metadata and compare planned slots to observed bundles
|
|
299
|
-
control-map Report computed repo/worktree/planning state; annotate only records local intent
|
|
300
|
-
closeout-report Read-only post-merge closure replay; reports blockers, warnings, and next safe action
|
|
301
282
|
next Read-only \`.work\` continuity router for the next coherent agent action
|
|
302
283
|
file-op Deterministic workspace-confined file copy/delete/text mutation
|
|
303
284
|
`;
|