* chore(#191): migrate gsd-sdk query call sites to gsd-tools query Retiring the gsd-sdk shim. gsd-tools.cjs already accepts `query` as a meta-prefix (gsd-tools query <command>), so this is a behavior-preserving 1:1 swap across the runtime reference prompts, the graphify hook's commit-detection gate, and two bin/lib comment/message references. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#191): remove vestigial gsd-sdk shim code from installer + projection The gsd-sdk shim was already not wired up (no gsd-sdk bin in package.json; buildWindowsShimTriple had zero call sites). Remove the dead code: - shell-command-projection.cjs: buildWindowsShimTriple + formatSdkPathDiagnostic (+ their now-unused PACKAGE_NAME import) and exports - install.js: the re-export wrappers + imports, the #3406 stale-standalone-sdk detection (detectStaleStandaloneSdk/formatStaleStandaloneSdkWarning + its global-install call site), and the exports Preserved (retained, not gsd-sdk): buildCodexHookWindowsShimIR (#3426) — only its comments referenced the gsd-sdk pattern; reworded. Also kept the homePathCoveredByRc 'reopen your shell' branch in maybeSuggestPathExport — its logic is bin-dir-agnostic, only the message mentioned gsd-sdk; reworded to use the actual bin dir. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(#191): update tests for retired gsd-sdk shim - bug-3441/bug-3442: drop the formatSdkPathDiagnostic / buildWindowsShimTriple assertions (functions removed); retained PATH-action + drift-guard tests stay - bug-505: remove the 'still exported' assertions for detectStaleStandaloneSdk / formatStaleStandaloneSdkWarning / the shim contract surface (#505 kept them; #191 removes them) - graphify-auto-update: migrate the hook-dispatch inputs gsd-sdk query commit -> gsd-tools query commit to match the migrated commit hook Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#191): point active docs at gsd-tools query (gsd-sdk shim retired) Update the user/agent-facing docs (AGENTS, COMMANDS, CONFIGURATION, USER-GUIDE, ship-pr-body-sections) that presented gsd-sdk query as a current command to gsd-tools query. Historical docs (ADRs, PRDs, release notes) left untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#191): correct state.load vs state.json description for gsd-tools query Adversarial-review (codex) finding: the migrated USER-GUIDE line claimed both 'gsd-tools query state.json' and 'state.load' resolve to the frontmatter-rebuild handler. Verified they don't — state.load returns the CJS load shape (config + state_raw + flags), state.json returns the frontmatter shape. Both are available via gsd-tools query; corrected the text to say so. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(#191): add changeset for gsd-sdk shim retirement Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
253 lines
8.4 KiB
JavaScript
253 lines
8.4 KiB
JavaScript
'use strict';
|
|
|
|
const { STATE_SUBCOMMANDS } = require('./command-aliases.cjs');
|
|
const { routeHubCommandFamily, cjsFallbackHandler } = require('./cjs-command-router-adapter.cjs');
|
|
const { parseNamedArgs } = require('./command-arg-projection.cjs');
|
|
|
|
/**
|
|
* Manifest-backed state subcommand router.
|
|
* Keeps gsd-tools.cjs thin while preserving existing command semantics.
|
|
*
|
|
* Phase 5.1: handlers that have SDK equivalents are dispatched via
|
|
* executeForCjs (the sync bridge). CJS fallback is retained for:
|
|
* - complete-phase: no SDK counterpart.
|
|
* - Any command when GSD_WORKSTREAM is active (GSDTransport forces subprocess
|
|
* for workstream requests; subprocess is disabled in the sync bridge worker).
|
|
* - Any command when the SDK is not available (build not present).
|
|
*/
|
|
function routeStateCommand({ state, args, cwd, raw, error }) {
|
|
const parsePlans = (plans) => {
|
|
const parsedPlans = plans == null ? null : Number.parseInt(plans, 10);
|
|
if (plans != null && Number.isNaN(parsedPlans)) {
|
|
error('Invalid --plans value. Expected an integer.');
|
|
return null;
|
|
}
|
|
return parsedPlans;
|
|
};
|
|
|
|
routeHubCommandFamily({
|
|
family: 'state',
|
|
args,
|
|
subcommands: ['load', 'complete-phase', ...STATE_SUBCOMMANDS.filter((s) => s !== 'load')],
|
|
defaultSubcommand: 'load',
|
|
unsupported: {
|
|
'add-roadmap-evolution': 'state add-roadmap-evolution is SDK-only. Use: gsd-tools query state.add-roadmap-evolution ...',
|
|
},
|
|
error,
|
|
cwd,
|
|
raw,
|
|
unknownMessage: (subcommand, available) => `Unknown state subcommand: "${subcommand}". Available: ${available.join(', ')}`,
|
|
handlers: {
|
|
load: cjsFallbackHandler(
|
|
'state.load',
|
|
[],
|
|
args.slice(1),
|
|
null,
|
|
() => state.cmdStateLoad(cwd, raw),
|
|
),
|
|
json: cjsFallbackHandler(
|
|
'state.json',
|
|
[],
|
|
args.slice(1),
|
|
null,
|
|
() => state.cmdStateJson(cwd, raw),
|
|
),
|
|
get: cjsFallbackHandler(
|
|
'state.get',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => state.cmdStateGet(cwd, args[2], raw),
|
|
),
|
|
update: cjsFallbackHandler(
|
|
'state.update',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => state.cmdStateUpdate(cwd, args[2], args[3]),
|
|
),
|
|
patch: cjsFallbackHandler(
|
|
'state.patch',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => {
|
|
const patches = {};
|
|
if (args.length === 3 && typeof args[2] === 'string' && args[2].trim().startsWith('{')) {
|
|
let parsed;
|
|
try {
|
|
parsed = JSON.parse(args[2]);
|
|
} catch (err) {
|
|
error(`state patch: invalid JSON object: ${err.message}`);
|
|
}
|
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
error('state patch: JSON input must be an object of field/value pairs.');
|
|
}
|
|
for (const [key, value] of Object.entries(parsed)) {
|
|
if (key && value !== undefined) {
|
|
patches[key] = String(value);
|
|
}
|
|
}
|
|
} else {
|
|
for (let i = 2; i < args.length; i += 2) {
|
|
const key = args[i].replace(/^--/, '');
|
|
const value = args[i + 1];
|
|
if (key && value !== undefined) {
|
|
patches[key] = value;
|
|
}
|
|
}
|
|
}
|
|
state.cmdStatePatch(cwd, patches, raw);
|
|
},
|
|
),
|
|
'advance-plan': cjsFallbackHandler(
|
|
'state.advance-plan',
|
|
[],
|
|
args.slice(1),
|
|
null,
|
|
() => state.cmdStateAdvancePlan(cwd, raw),
|
|
),
|
|
'record-metric': cjsFallbackHandler(
|
|
'state.record-metric',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => {
|
|
const { phase: p, plan, duration, tasks, files } = parseNamedArgs(args, ['phase', 'plan', 'duration', 'tasks', 'files']);
|
|
state.cmdStateRecordMetric(cwd, { phase: p, plan, duration, tasks, files }, raw);
|
|
},
|
|
),
|
|
'update-progress': cjsFallbackHandler(
|
|
'state.update-progress',
|
|
[],
|
|
args.slice(1),
|
|
null,
|
|
() => state.cmdStateUpdateProgress(cwd, raw),
|
|
),
|
|
'add-decision': cjsFallbackHandler(
|
|
'state.add-decision',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => {
|
|
const { phase: p, summary, 'summary-file': summary_file, rationale, 'rationale-file': rationale_file } = parseNamedArgs(args, ['phase', 'summary', 'summary-file', 'rationale', 'rationale-file']);
|
|
state.cmdStateAddDecision(cwd, { phase: p, summary, summary_file, rationale: rationale || '', rationale_file }, raw);
|
|
},
|
|
),
|
|
'add-blocker': cjsFallbackHandler(
|
|
'state.add-blocker',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => {
|
|
const { text, 'text-file': text_file } = parseNamedArgs(args, ['text', 'text-file']);
|
|
state.cmdStateAddBlocker(cwd, { text, text_file }, raw);
|
|
},
|
|
),
|
|
'resolve-blocker': cjsFallbackHandler(
|
|
'state.resolve-blocker',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => state.cmdStateResolveBlocker(cwd, parseNamedArgs(args, ['text']).text, raw),
|
|
),
|
|
'record-session': cjsFallbackHandler(
|
|
'state.record-session',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => {
|
|
const { 'stopped-at': stopped_at, 'resume-file': resume_file } = parseNamedArgs(args, ['stopped-at', 'resume-file']);
|
|
// Pass resume_file as-is (undefined when --resume-file was not provided) so
|
|
// cmdStateRecordSession can distinguish "caller explicitly passed a value" from
|
|
// "option was not supplied" and apply the template-default-only replacement guard.
|
|
state.cmdStateRecordSession(cwd, { stopped_at, resume_file }, raw);
|
|
},
|
|
),
|
|
'begin-phase': cjsFallbackHandler(
|
|
'state.begin-phase',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => {
|
|
const { phase: p, name, plans } = parseNamedArgs(args, ['phase', 'name', 'plans']);
|
|
state.cmdStateBeginPhase(cwd, p, name, parsePlans(plans), raw);
|
|
},
|
|
),
|
|
'signal-waiting': cjsFallbackHandler(
|
|
'state.signal-waiting',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => {
|
|
const { type, question, options, phase: p } = parseNamedArgs(args, ['type', 'question', 'options', 'phase']);
|
|
state.cmdSignalWaiting(cwd, type, question, options, p, raw);
|
|
},
|
|
),
|
|
'signal-resume': cjsFallbackHandler(
|
|
'state.signal-resume',
|
|
[],
|
|
args.slice(1),
|
|
null,
|
|
() => state.cmdSignalResume(cwd, raw),
|
|
),
|
|
'planned-phase': cjsFallbackHandler(
|
|
'state.planned-phase',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => {
|
|
const { phase: p, plans } = parseNamedArgs(args, ['phase', 'name', 'plans']);
|
|
state.cmdStatePlannedPhase(cwd, p, parsePlans(plans), raw);
|
|
},
|
|
),
|
|
validate: cjsFallbackHandler(
|
|
'state.validate',
|
|
[],
|
|
args.slice(1),
|
|
null,
|
|
() => state.cmdStateValidate(cwd, raw),
|
|
),
|
|
sync: cjsFallbackHandler(
|
|
'state.sync',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => {
|
|
const { verify } = parseNamedArgs(args, [], ['verify']);
|
|
state.cmdStateSync(cwd, { verify }, raw);
|
|
},
|
|
),
|
|
prune: cjsFallbackHandler(
|
|
'state.prune',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => {
|
|
const { 'keep-recent': keepRecent, 'dry-run': dryRun } = parseNamedArgs(args, ['keep-recent'], ['dry-run']);
|
|
state.cmdStatePrune(cwd, { keepRecent: keepRecent || '3', dryRun: !!dryRun }, raw);
|
|
},
|
|
),
|
|
// complete-phase: CJS-only — no SDK counterpart.
|
|
'complete-phase': () => {
|
|
const { phase: p } = parseNamedArgs(args, ['phase']);
|
|
state.cmdStateCompletePhase(cwd, raw, p || args[2]);
|
|
},
|
|
'milestone-switch': cjsFallbackHandler(
|
|
'state.milestone-switch',
|
|
args.slice(2),
|
|
args.slice(1),
|
|
null,
|
|
() => {
|
|
const { milestone, name } = parseNamedArgs(args, ['milestone', 'name']);
|
|
state.cmdStateMilestoneSwitch(cwd, milestone, name, raw);
|
|
},
|
|
),
|
|
},
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
routeStateCommand,
|
|
};
|