gsdd-cli 0.26.0 → 0.27.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 +4 -3
- package/bin/lib/global-install.mjs +16 -3
- package/bin/lib/init-runtime.mjs +6 -3
- package/bin/lib/lifecycle-preflight.mjs +211 -5
- package/docs/RUNTIME-SUPPORT.md +3 -3
- package/docs/USER-GUIDE.md +6 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ The public product name is Workspine. The retained technical contracts remain `g
|
|
|
15
15
|
npx -y gsdd-cli init
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Use `npx -y gsdd-cli init` for repo-local setup. Use `npx -y gsdd-cli install --global` to install reusable Workspine skills and native runtime surfaces into
|
|
18
|
+
Use `npx -y gsdd-cli init` for repo-local setup. Use `npx -y gsdd-cli install --global --auto` to install reusable Workspine skills and native runtime surfaces into detected agent homes so they are available across repos.
|
|
19
19
|
|
|
20
20
|
</div>
|
|
21
21
|
|
|
@@ -81,10 +81,11 @@ If `gsdd-cli` is installed globally, install reusable Workspine surfaces into yo
|
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
83
|
npx -y gsdd-cli install --global
|
|
84
|
+
npx -y gsdd-cli install --global --auto
|
|
84
85
|
npx -y gsdd-cli install --global --tools claude,opencode,codex,copilot
|
|
85
86
|
```
|
|
86
87
|
|
|
87
|
-
When run in a TTY without `--tools`, `install --global` lets you select which agents to install. It does not create `.planning/` in the current repo. It writes Workspine-managed skills, native agent surfaces, and per-runtime manifests under the selected agent homes:
|
|
88
|
+
Use `--auto` for non-interactive install into detected local agent homes. Use `--tools <targets>` when you want to override detection explicitly. When run in a TTY without `--tools` or `--auto`, `install --global` lets you select which agents to install. It does not create `.planning/` in the current repo. It writes Workspine-managed skills, native agent surfaces, and per-runtime manifests under the selected agent homes:
|
|
88
89
|
|
|
89
90
|
| Target | Global surfaces |
|
|
90
91
|
|--------|-----------------|
|
|
@@ -127,7 +128,7 @@ npx -y gsdd-cli models profile budget # minimize cost
|
|
|
127
128
|
|
|
128
129
|
## Troubleshooting
|
|
129
130
|
|
|
130
|
-
Inside a repo-local `.planning/` workspace, start with `npx -y gsdd-cli health`. It checks local generated runtime surfaces against current render output and reports whether `npx -y gsdd-cli update` can repair drift. To repair or refresh a personal global install, rerun `npx -y gsdd-cli install --global --tools <targets>`. For details, see the [User Guide](docs/USER-GUIDE.md).
|
|
131
|
+
Inside a repo-local `.planning/` workspace, start with `npx -y gsdd-cli health`. It checks local generated runtime surfaces against current render output and reports whether `npx -y gsdd-cli update` can repair drift. To repair or refresh a personal global install, rerun `npx -y gsdd-cli install --global --auto` or explicitly scope it with `npx -y gsdd-cli install --global --tools <targets>`. For details, see the [User Guide](docs/USER-GUIDE.md).
|
|
131
132
|
|
|
132
133
|
---
|
|
133
134
|
|
|
@@ -102,14 +102,22 @@ function validateGlobalTools(tools) {
|
|
|
102
102
|
return `ERROR: unsupported global install target(s): ${invalid.join(', ')}. Use --tools claude,opencode,codex,copilot or --tools all.`;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
function detectGlobalInstallTargets(roots) {
|
|
106
|
+
return GLOBAL_AGENT_IDS.filter((target) => existsSync(roots[target]));
|
|
107
|
+
}
|
|
108
|
+
|
|
105
109
|
function displayPath(filePath) {
|
|
106
110
|
return filePath.replace(/\\/g, '/');
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
async function resolveGlobalInstallTargets({ args, promptApi, output }) {
|
|
113
|
+
async function resolveGlobalInstallTargets({ args, promptApi, output, roots }) {
|
|
110
114
|
const parsedTools = normalizeGlobalTools(parseGlobalToolsFlag(args));
|
|
111
115
|
if (parsedTools.length > 0) return parsedTools;
|
|
112
116
|
|
|
117
|
+
if (args.includes('--auto')) {
|
|
118
|
+
return detectGlobalInstallTargets(roots);
|
|
119
|
+
}
|
|
120
|
+
|
|
113
121
|
if (!process.stdin.isTTY) {
|
|
114
122
|
return [];
|
|
115
123
|
}
|
|
@@ -530,6 +538,7 @@ export function createCmdInstall(ctx) {
|
|
|
530
538
|
return async function cmdInstall(...installArgs) {
|
|
531
539
|
const globalFlag = installArgs.includes('--global') || installArgs.includes('-g');
|
|
532
540
|
const localFlag = installArgs.includes('--local');
|
|
541
|
+
const autoFlag = installArgs.includes('--auto');
|
|
533
542
|
const dryRun = installArgs.includes('--dry');
|
|
534
543
|
const verifyRuntime = installArgs.includes('--verify-runtime');
|
|
535
544
|
const liveRuntime = installArgs.includes('--live-runtime');
|
|
@@ -559,14 +568,19 @@ export function createCmdInstall(ctx) {
|
|
|
559
568
|
return;
|
|
560
569
|
}
|
|
561
570
|
|
|
571
|
+
const roots = resolveGlobalInstallRoots();
|
|
562
572
|
const targets = await resolveGlobalInstallTargets({
|
|
563
573
|
args: installArgs,
|
|
564
574
|
promptApi: ctx.globalInstallPromptApi,
|
|
565
575
|
output: process.stdout,
|
|
576
|
+
roots,
|
|
566
577
|
});
|
|
567
578
|
|
|
568
579
|
if (targets.length === 0) {
|
|
569
|
-
|
|
580
|
+
const autoHint = autoFlag
|
|
581
|
+
? 'No supported agent homes were detected for --auto. Create an agent config home first or use --tools claude,opencode,codex,copilot explicitly.'
|
|
582
|
+
: 'Use --tools claude,opencode,codex,copilot or run interactively.';
|
|
583
|
+
console.error(`ERROR: no global install targets selected. ${autoHint}`);
|
|
570
584
|
process.exitCode = 1;
|
|
571
585
|
return;
|
|
572
586
|
}
|
|
@@ -578,7 +592,6 @@ export function createCmdInstall(ctx) {
|
|
|
578
592
|
return;
|
|
579
593
|
}
|
|
580
594
|
|
|
581
|
-
const roots = resolveGlobalInstallRoots();
|
|
582
595
|
console.log(`Workspine global install - installing runtime surfaces${dryRun ? ' (dry run)' : ''}\n`);
|
|
583
596
|
|
|
584
597
|
const reports = targets.map((target) => installTarget({
|
package/bin/lib/init-runtime.mjs
CHANGED
|
@@ -172,8 +172,9 @@ Commands:
|
|
|
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
174
|
--brief <file>: copy project brief to .planning/PROJECT_BRIEF.md
|
|
175
|
-
install --global [--tools <platform>] [--dry]
|
|
175
|
+
install --global [--auto] [--tools <platform>] [--dry]
|
|
176
176
|
Install reusable Workspine skills and native runtime surfaces into agent home directories
|
|
177
|
+
--auto: non-interactive mode that installs detected local agent targets
|
|
177
178
|
In TTYs, omitting --tools opens an agent picker
|
|
178
179
|
update [--tools <platform>] [--templates] [--dry]
|
|
179
180
|
Regenerate adapters from latest framework sources
|
|
@@ -223,11 +224,12 @@ Global install targets:
|
|
|
223
224
|
all Install all global targets above
|
|
224
225
|
|
|
225
226
|
Notes:
|
|
226
|
-
- use \`npx -y gsdd-cli init\` for repo-local setup; use \`npx -y gsdd-cli install --global\` when you want reusable skills in agent homes
|
|
227
|
+
- 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
|
|
227
228
|
- init always generates open-standard skills at .agents/skills/gsdd-*; this is the shared workflow entry surface
|
|
228
229
|
- init also generates a local .planning/bin/gsdd* helper surface for workflow-embedded lifecycle helpers; it is internal/advanced, not the normal first-run user entrypoint
|
|
229
230
|
- install --global never creates .planning/ in the current repo; it writes only selected agent-home surfaces and per-runtime Workspine manifests
|
|
230
|
-
-
|
|
231
|
+
- 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
|
+
- 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
|
|
231
233
|
- Workspine is the public product name; the retained package, command, workflow, and workspace contracts stay gsdd-cli, gsdd, gsdd-*, and .planning/
|
|
232
234
|
- running \`npx -y gsdd-cli init\` in a terminal opens the guided runtime-selection wizard; bare \`gsdd init\` is equivalent only when globally installed
|
|
233
235
|
- the wizard lets you pick runtimes first, then separately decide whether repo-wide AGENTS.md governance is worth installing
|
|
@@ -256,6 +258,7 @@ Examples:
|
|
|
256
258
|
npx -y gsdd-cli init --tools agents
|
|
257
259
|
npx -y gsdd-cli init --tools all
|
|
258
260
|
npx -y gsdd-cli install --global
|
|
261
|
+
npx -y gsdd-cli install --global --auto
|
|
259
262
|
npx -y gsdd-cli install --global --tools claude,opencode,codex,copilot
|
|
260
263
|
npx -y gsdd-cli update
|
|
261
264
|
npx -y gsdd-cli next --json
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'fs';
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
2
2
|
import { join, resolve } from 'path';
|
|
3
3
|
import { output } from './cli-utils.mjs';
|
|
4
4
|
import { buildControlMap } from './control-map.mjs';
|
|
@@ -76,6 +76,7 @@ const RELEASE_CONTRADICTION_CHECKS = Object.freeze([
|
|
|
76
76
|
|
|
77
77
|
const RELEASE_CONTRADICTION_STATUSES = Object.freeze(['passed', 'failed', 'not_applicable']);
|
|
78
78
|
const PREFLIGHT_CONTROL_MAP_SKIP_CODES = Object.freeze(['planning_state_drift']);
|
|
79
|
+
const WORK_PHASE_LINE_RE = /^\s*[-*]\s*\[([ x-])\]\s*\*\*Phase\s+(\d+(?:\.\d+)*[A-Za-z]?):\s*(.+?)\*\*/i;
|
|
79
80
|
|
|
80
81
|
export function evaluateLifecyclePreflight({
|
|
81
82
|
planningDir,
|
|
@@ -95,7 +96,12 @@ export function evaluateLifecyclePreflight({
|
|
|
95
96
|
|
|
96
97
|
const lifecycle = evaluateLifecycleState({ planningDir });
|
|
97
98
|
const normalizedPhase = phaseNumber ? normalizePhaseToken(phaseNumber) : null;
|
|
99
|
+
const workMilestone = normalizedPhase ? evaluateWorkMilestoneState({ planningDir, phaseToken: normalizedPhase }) : null;
|
|
98
100
|
const checkpointPath = join(planningDir, '.continue-here.md');
|
|
101
|
+
const resumeWorkCheckpoint = surface === 'resume'
|
|
102
|
+
? evaluateResumeWorkCheckpoint({ planningDir, checkpointPath })
|
|
103
|
+
: null;
|
|
104
|
+
const usesWorkAuthority = Boolean(workMilestone?.phaseEntry || resumeWorkCheckpoint);
|
|
99
105
|
const specPath = join(planningDir, 'SPEC.md');
|
|
100
106
|
const milestonesPath = join(planningDir, 'MILESTONES.md');
|
|
101
107
|
const blockers = [];
|
|
@@ -119,7 +125,11 @@ export function evaluateLifecyclePreflight({
|
|
|
119
125
|
}
|
|
120
126
|
|
|
121
127
|
if (normalizedPhase) {
|
|
122
|
-
blockers.push(
|
|
128
|
+
blockers.push(
|
|
129
|
+
...(usesWorkAuthority
|
|
130
|
+
? buildWorkPhaseBlockers({ workMilestone, phaseToken: normalizedPhase, surface })
|
|
131
|
+
: buildPhaseBlockers({ lifecycle, phaseToken: normalizedPhase, surface }))
|
|
132
|
+
);
|
|
123
133
|
}
|
|
124
134
|
|
|
125
135
|
if (surface === 'audit-milestone') {
|
|
@@ -176,12 +186,17 @@ export function evaluateLifecyclePreflight({
|
|
|
176
186
|
details: drift.details,
|
|
177
187
|
files: drift.files,
|
|
178
188
|
};
|
|
179
|
-
if (policy.classification === 'owned_write') {
|
|
189
|
+
if (policy.classification === 'owned_write' && !usesWorkAuthority) {
|
|
180
190
|
blockers.push(driftNotice);
|
|
181
191
|
} else {
|
|
192
|
+
const workWarningContext = resumeWorkCheckpoint
|
|
193
|
+
? 'because the checkpoint points at .work/milestone continuity'
|
|
194
|
+
: `by .work/milestone for Phase ${normalizedPhase}`;
|
|
182
195
|
warnings.push({
|
|
183
196
|
...driftNotice,
|
|
184
|
-
message:
|
|
197
|
+
message: usesWorkAuthority
|
|
198
|
+
? `Planning state has drifted since the last recorded session, but ${surface} is using work_milestone authority ${workWarningContext}: ${drift.details.join('; ')}`
|
|
199
|
+
: `Planning state has drifted since the last recorded session: ${drift.details.join('; ')}`,
|
|
185
200
|
});
|
|
186
201
|
}
|
|
187
202
|
}
|
|
@@ -198,7 +213,7 @@ export function evaluateLifecyclePreflight({
|
|
|
198
213
|
else warnings.push(notice);
|
|
199
214
|
}
|
|
200
215
|
|
|
201
|
-
if (lifecycle.phaseStatusAlignment.mismatches.length > 0) {
|
|
216
|
+
if (!usesWorkAuthority && lifecycle.phaseStatusAlignment.mismatches.length > 0) {
|
|
202
217
|
warnings.push({
|
|
203
218
|
code: 'roadmap_phase_status_mismatch',
|
|
204
219
|
message: `ROADMAP.md overview/detail phase statuses disagree: ${lifecycle.phaseStatusAlignment.mismatches.join('; ')}`,
|
|
@@ -214,6 +229,7 @@ export function evaluateLifecyclePreflight({
|
|
|
214
229
|
explicitLifecycleMutation: policy.explicitLifecycleMutation,
|
|
215
230
|
closureEvidence: describeEvidenceSurface(surface),
|
|
216
231
|
mutationRequest: expectsMutation,
|
|
232
|
+
authority: usesWorkAuthority ? 'work_milestone' : 'planning',
|
|
217
233
|
allowed: blockers.length === 0,
|
|
218
234
|
status: blockers.length === 0 ? 'allowed' : 'blocked',
|
|
219
235
|
reason: blockers[0]?.code ?? null,
|
|
@@ -222,10 +238,20 @@ export function evaluateLifecyclePreflight({
|
|
|
222
238
|
planningState,
|
|
223
239
|
controlMap: controlMap.summary,
|
|
224
240
|
lifecycle: {
|
|
241
|
+
authority: usesWorkAuthority ? 'work_milestone' : 'planning',
|
|
225
242
|
currentMilestone: lifecycle.currentMilestone,
|
|
226
243
|
currentPhase: lifecycle.currentPhase ? lifecycle.currentPhase.number : null,
|
|
227
244
|
nextPhase: lifecycle.nextPhase ? lifecycle.nextPhase.number : null,
|
|
228
245
|
counts: lifecycle.counts,
|
|
246
|
+
workMilestone: usesWorkAuthority
|
|
247
|
+
? {
|
|
248
|
+
phase: workMilestone?.phaseEntry?.number ?? null,
|
|
249
|
+
status: workMilestone?.phaseEntry?.status ?? null,
|
|
250
|
+
roadmapPath: '.work/milestone/ROADMAP.md',
|
|
251
|
+
milestoneDir: '.work/milestone',
|
|
252
|
+
source: resumeWorkCheckpoint ? 'checkpoint' : 'phase',
|
|
253
|
+
}
|
|
254
|
+
: null,
|
|
229
255
|
},
|
|
230
256
|
};
|
|
231
257
|
}
|
|
@@ -346,6 +372,186 @@ function buildPhaseBlockers({ lifecycle, phaseToken, surface }) {
|
|
|
346
372
|
return blockers;
|
|
347
373
|
}
|
|
348
374
|
|
|
375
|
+
function evaluateWorkMilestoneState({ planningDir, phaseToken }) {
|
|
376
|
+
const workspaceRoot = resolve(planningDir, '..');
|
|
377
|
+
const milestoneDir = join(workspaceRoot, '.work', 'milestone');
|
|
378
|
+
const roadmapPath = join(milestoneDir, 'ROADMAP.md');
|
|
379
|
+
const phasesDir = join(milestoneDir, 'phases');
|
|
380
|
+
|
|
381
|
+
if (!existsSync(roadmapPath)) {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const phases = parseWorkRoadmapPhases(readFileSync(roadmapPath, 'utf-8'));
|
|
386
|
+
const phaseEntry = phases.find((phase) => phase.number === phaseToken);
|
|
387
|
+
if (!phaseEntry) {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
milestoneDir,
|
|
393
|
+
roadmapPath,
|
|
394
|
+
phasesDir,
|
|
395
|
+
phases,
|
|
396
|
+
phaseEntry,
|
|
397
|
+
phaseArtifacts: collectWorkPhaseArtifacts({ workspaceRoot, phasesDir, phaseToken }),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function parseWorkRoadmapPhases(content) {
|
|
402
|
+
return String(content || '')
|
|
403
|
+
.replace(/\r\n/g, '\n')
|
|
404
|
+
.split('\n')
|
|
405
|
+
.map((line) => {
|
|
406
|
+
const match = line.match(WORK_PHASE_LINE_RE);
|
|
407
|
+
if (!match) return null;
|
|
408
|
+
return {
|
|
409
|
+
status: parseWorkPhaseStatus(match[1]),
|
|
410
|
+
number: normalizePhaseToken(match[2]),
|
|
411
|
+
title: match[3].trim(),
|
|
412
|
+
};
|
|
413
|
+
})
|
|
414
|
+
.filter(Boolean);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function parseWorkPhaseStatus(rawStatus) {
|
|
418
|
+
const status = String(rawStatus || '').trim().toLowerCase();
|
|
419
|
+
if (status === 'x') return 'done';
|
|
420
|
+
if (status === '-') return 'in_progress';
|
|
421
|
+
return 'pending';
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function evaluateResumeWorkCheckpoint({ planningDir, checkpointPath }) {
|
|
425
|
+
if (!existsSync(checkpointPath)) return null;
|
|
426
|
+
|
|
427
|
+
const workspaceRoot = resolve(planningDir, '..');
|
|
428
|
+
const milestoneDir = join(workspaceRoot, '.work', 'milestone');
|
|
429
|
+
const roadmapPath = join(milestoneDir, 'ROADMAP.md');
|
|
430
|
+
if (!existsSync(roadmapPath)) return null;
|
|
431
|
+
|
|
432
|
+
let content = '';
|
|
433
|
+
try {
|
|
434
|
+
content = readFileSync(checkpointPath, 'utf-8');
|
|
435
|
+
} catch {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (!/(^|[`"'(\s])\.work[\\/]+milestone([`"')\s/]|$)/i.test(content)) {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return {
|
|
444
|
+
milestoneDir,
|
|
445
|
+
roadmapPath,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function collectWorkPhaseArtifacts({ workspaceRoot, phasesDir, phaseToken }) {
|
|
450
|
+
if (!existsSync(phasesDir)) return [];
|
|
451
|
+
|
|
452
|
+
return collectMarkdownFiles(phasesDir)
|
|
453
|
+
.map((filePath) => {
|
|
454
|
+
const filename = filePath.split(/[\\/]/).pop();
|
|
455
|
+
const match = filename.match(/^(\d+(?:\.\d+)*[A-Za-z]?)-(.+?)\.md$/i);
|
|
456
|
+
if (!match || normalizePhaseToken(match[1]) !== phaseToken) return null;
|
|
457
|
+
|
|
458
|
+
const kind = parseWorkArtifactKind(match[2]);
|
|
459
|
+
if (!kind) return null;
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
phaseToken,
|
|
463
|
+
kind,
|
|
464
|
+
path: filePath,
|
|
465
|
+
displayPath: relativeDisplayPath(workspaceRoot, filePath),
|
|
466
|
+
};
|
|
467
|
+
})
|
|
468
|
+
.filter(Boolean);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function collectMarkdownFiles(dir) {
|
|
472
|
+
const files = [];
|
|
473
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
474
|
+
const fullPath = join(dir, entry.name);
|
|
475
|
+
if (entry.isDirectory()) {
|
|
476
|
+
files.push(...collectMarkdownFiles(fullPath));
|
|
477
|
+
} else if (entry.isFile() && entry.name.toLowerCase().endsWith('.md')) {
|
|
478
|
+
files.push(fullPath);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return files;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function parseWorkArtifactKind(rawKind) {
|
|
485
|
+
const kind = String(rawKind || '').toLowerCase();
|
|
486
|
+
if (kind === 'plan') return 'plan';
|
|
487
|
+
if (kind === 'execute') return 'execute';
|
|
488
|
+
if (kind === 'verify' || kind === 'verification') return 'verification';
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function relativeDisplayPath(root, filePath) {
|
|
493
|
+
return filePath.slice(root.length + 1).replace(/\\/g, '/');
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function buildWorkPhaseBlockers({ workMilestone, phaseToken, surface }) {
|
|
497
|
+
const blockers = [];
|
|
498
|
+
const planArtifacts = workMilestone.phaseArtifacts.filter((artifact) => artifact.kind === 'plan');
|
|
499
|
+
const executeArtifacts = workMilestone.phaseArtifacts.filter((artifact) => artifact.kind === 'execute');
|
|
500
|
+
|
|
501
|
+
if (surface === 'execute') {
|
|
502
|
+
if (planArtifacts.length === 0) {
|
|
503
|
+
blockers.push(
|
|
504
|
+
blocker(
|
|
505
|
+
'missing_plan',
|
|
506
|
+
`Phase ${phaseToken} cannot execute because no .work PLAN artifact exists.`,
|
|
507
|
+
['.work/milestone/phases/']
|
|
508
|
+
)
|
|
509
|
+
);
|
|
510
|
+
} else if (executeArtifacts.length > 0) {
|
|
511
|
+
blockers.push(
|
|
512
|
+
blocker(
|
|
513
|
+
'no_pending_plan',
|
|
514
|
+
`Phase ${phaseToken} has already been executed in .work/milestone.`,
|
|
515
|
+
executeArtifacts.map((artifact) => artifact.displayPath)
|
|
516
|
+
)
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (surface === 'plan' && workMilestone.phaseEntry.status === 'done') {
|
|
522
|
+
blockers.push(
|
|
523
|
+
blocker(
|
|
524
|
+
'phase_already_complete',
|
|
525
|
+
`Phase ${phaseToken} is already complete in .work/milestone and should not be planned again.`,
|
|
526
|
+
['.work/milestone/ROADMAP.md']
|
|
527
|
+
)
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (surface === 'verify') {
|
|
532
|
+
if (planArtifacts.length === 0) {
|
|
533
|
+
blockers.push(
|
|
534
|
+
blocker(
|
|
535
|
+
'missing_plan',
|
|
536
|
+
`Phase ${phaseToken} cannot be verified because no .work PLAN artifact exists.`,
|
|
537
|
+
['.work/milestone/phases/']
|
|
538
|
+
)
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
if (executeArtifacts.length === 0) {
|
|
542
|
+
blockers.push(
|
|
543
|
+
blocker(
|
|
544
|
+
'missing_execute',
|
|
545
|
+
`Phase ${phaseToken} cannot be verified because no .work EXECUTE artifact exists yet.`,
|
|
546
|
+
['.work/milestone/phases/']
|
|
547
|
+
)
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
return blockers;
|
|
553
|
+
}
|
|
554
|
+
|
|
349
555
|
function buildRoadmapAlignmentBlockers(lifecycle) {
|
|
350
556
|
if (lifecycle.phaseStatusAlignment.mismatches.length === 0) return [];
|
|
351
557
|
return [
|
package/docs/RUNTIME-SUPPORT.md
CHANGED
|
@@ -4,7 +4,7 @@ Workspine is a repo-native delivery spine with portable multi-runtime workflow s
|
|
|
4
4
|
|
|
5
5
|
This matrix is the release-floor truth surface.
|
|
6
6
|
|
|
7
|
-
Human repo setup and repair commands in this document use `npx -y gsdd-cli ...` because that works without a global install. If you installed `gsdd-cli` globally, the equivalent bare `gsdd ...` command is fine. For cross-repo personal use, `npx -y gsdd-cli install --global` installs reusable Workspine skills and native runtime surfaces into
|
|
7
|
+
Human repo setup and repair commands in this document use `npx -y gsdd-cli ...` because that works without a global install. If you installed `gsdd-cli` globally, the equivalent bare `gsdd ...` command is fine. For cross-repo personal use, `npx -y gsdd-cli install --global --auto` installs reusable Workspine skills and native runtime surfaces into detected agent homes.
|
|
8
8
|
|
|
9
9
|
The install contract is deliberately skills-first: `npx -y gsdd-cli init` always creates `.agents/skills/gsdd-*` and `.planning/bin/gsdd*`; runtime-specific adapters are optional discovery or orchestration helpers layered on top.
|
|
10
10
|
|
|
@@ -55,7 +55,7 @@ Two surfaces matter for users:
|
|
|
55
55
|
|
|
56
56
|
## Global install surfaces
|
|
57
57
|
|
|
58
|
-
`npx -y gsdd-cli install --global` can install personal cross-repo surfaces for
|
|
58
|
+
`npx -y gsdd-cli install --global --auto` can install personal cross-repo surfaces for detected agent homes. Use `npx -y gsdd-cli install --global --tools <targets>` to override detection explicitly. Supported targets:
|
|
59
59
|
|
|
60
60
|
| Target | Global surfaces |
|
|
61
61
|
| --- | --- |
|
|
@@ -77,7 +77,7 @@ The authored source contract stays in `distilled/workflows/*`. Generated runtime
|
|
|
77
77
|
- `npx -y gsdd-cli update` regenerates drifted generated surfaces from the authored workflow and delegate sources.
|
|
78
78
|
- Bare `gsdd health` and `gsdd update` are equivalent only when `gsdd-cli` is globally installed.
|
|
79
79
|
- Missing generated surfaces are not treated as drift unless the corresponding runtime surface is actually installed locally.
|
|
80
|
-
- Global user-home installs are refreshed by rerunning `npx -y gsdd-cli install --global --tools <targets>`; global runtime probes remain an internal pressure-harness concern, not a public install flag.
|
|
80
|
+
- Global user-home installs are refreshed by rerunning `npx -y gsdd-cli install --global --auto` or explicitly scoped with `npx -y gsdd-cli install --global --tools <targets>`; global runtime probes remain an internal pressure-harness concern, not a public install flag.
|
|
81
81
|
|
|
82
82
|
## Entry and helper surfaces
|
|
83
83
|
|
package/docs/USER-GUIDE.md
CHANGED
|
@@ -13,7 +13,7 @@ For a new project or a broad brownfield effort:
|
|
|
13
13
|
3. Review the plan from `gsdd-plan` before starting `gsdd-execute`.
|
|
14
14
|
4. Run `gsdd-verify` before calling the phase done.
|
|
15
15
|
|
|
16
|
-
Use `npx -y gsdd-cli init` for repo-local setup. Use `npx -y gsdd-cli install --global` to install reusable Workspine skills and native runtime surfaces into
|
|
16
|
+
Use `npx -y gsdd-cli init` for repo-local setup. Use `npx -y gsdd-cli install --global --auto` to install reusable Workspine skills and native runtime surfaces into detected agent homes without creating `.planning/` in the current repo.
|
|
17
17
|
|
|
18
18
|
For a bounded existing-code change, use `gsdd-quick`. For an unfamiliar or risky repo, use `gsdd-map-codebase` before choosing between `gsdd-quick` and `gsdd-new-project`.
|
|
19
19
|
|
|
@@ -188,9 +188,12 @@ Use global agent install when you want Workspine workflows available across repo
|
|
|
188
188
|
|
|
189
189
|
```bash
|
|
190
190
|
npx -y gsdd-cli install --global
|
|
191
|
+
npx -y gsdd-cli install --global --auto
|
|
191
192
|
npx -y gsdd-cli install --global --tools claude,opencode,codex,copilot
|
|
192
193
|
```
|
|
193
194
|
|
|
195
|
+
Use `--auto` for non-interactive global install into detected local agent homes. Use `--tools <targets>` when you want to install a specific target set regardless of detection.
|
|
196
|
+
|
|
194
197
|
Global install writes Workspine-managed files under selected agent homes and records per-runtime manifests. It does not bootstrap project planning state.
|
|
195
198
|
|
|
196
199
|
### Workflows (run via generated skills or adapters)
|
|
@@ -239,7 +242,7 @@ Normal user flow:
|
|
|
239
242
|
2. Enter workflows through your runtime surface: `/gsdd-*` or `$gsdd-*`.
|
|
240
243
|
3. Use `npx -y gsdd-cli health` to check repo-local generated surfaces.
|
|
241
244
|
4. Use `npx -y gsdd-cli update` when repo-local generated surfaces drift or you want the latest shipped output.
|
|
242
|
-
5. For personal global installs, rerun `npx -y gsdd-cli install --global --
|
|
245
|
+
5. For personal global installs, rerun `npx -y gsdd-cli install --global --auto` to repair or refresh detected agent homes, or use `npx -y gsdd-cli install --global --tools <targets>` to scope the target set explicitly.
|
|
243
246
|
|
|
244
247
|
Surface split:
|
|
245
248
|
|
|
@@ -446,7 +449,7 @@ If you've modified any templates, the generation manifest detects this and warns
|
|
|
446
449
|
|
|
447
450
|
### Generated Surfaces Drift Or A Runtime Command Goes Missing
|
|
448
451
|
|
|
449
|
-
In a repo-local `.planning/` workspace, start with `npx -y gsdd-cli health`. If it reports drift or missing installed generated surfaces, run `npx -y gsdd-cli update` for the whole workspace or `npx -y gsdd-cli update --tools <runtime>` for a specific runtime. For global personal installs, rerun `npx -y gsdd-cli install --global --tools <targets>`.
|
|
452
|
+
In a repo-local `.planning/` workspace, start with `npx -y gsdd-cli health`. If it reports drift or missing installed generated surfaces, run `npx -y gsdd-cli update` for the whole workspace or `npx -y gsdd-cli update --tools <runtime>` for a specific runtime. For global personal installs, rerun `npx -y gsdd-cli install --global --auto` or scope it explicitly with `npx -y gsdd-cli install --global --tools <targets>`.
|
|
450
453
|
|
|
451
454
|
That repair path is deterministic for generated files. It does not imply that every runtime has equal native ergonomics or equal validation depth.
|
|
452
455
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gsdd-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Workspine — a repo-native delivery spine for long-horizon AI-assisted work, with directly validated support for Claude Code, Codex CLI, and OpenCode, published as gsdd-cli.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|