refactor(query): manifest-backed routing seam + family adapters (#2908)
Merging validated command-seam foundation.
This commit is contained in:
@@ -246,6 +246,7 @@
|
||||
"cli_modules": [
|
||||
"artifacts.cjs",
|
||||
"audit.cjs",
|
||||
"command-aliases.generated.cjs",
|
||||
"commands.cjs",
|
||||
"config-schema.cjs",
|
||||
"config.cjs",
|
||||
@@ -258,23 +259,30 @@
|
||||
"gap-checker.cjs",
|
||||
"graphify.cjs",
|
||||
"gsd2-import.cjs",
|
||||
"init-command-router.cjs",
|
||||
"init.cjs",
|
||||
"install-profiles.cjs",
|
||||
"intel.cjs",
|
||||
"learnings.cjs",
|
||||
"milestone.cjs",
|
||||
"model-profiles.cjs",
|
||||
"phase-command-router.cjs",
|
||||
"phase.cjs",
|
||||
"phases-command-router.cjs",
|
||||
"planning-workspace.cjs",
|
||||
"profile-output.cjs",
|
||||
"profile-pipeline.cjs",
|
||||
"roadmap-command-router.cjs",
|
||||
"roadmap.cjs",
|
||||
"schema-detect.cjs",
|
||||
"secrets.cjs",
|
||||
"security.cjs",
|
||||
"state-command-router.cjs",
|
||||
"state.cjs",
|
||||
"template.cjs",
|
||||
"uat.cjs",
|
||||
"validate-command-router.cjs",
|
||||
"verify-command-router.cjs",
|
||||
"verify.cjs",
|
||||
"workstream.cjs"
|
||||
],
|
||||
|
||||
@@ -348,7 +348,7 @@ The `gsd-planner` agent is decomposed into a core agent plus reference modules t
|
||||
|
||||
---
|
||||
|
||||
## CLI Modules (33 shipped)
|
||||
## CLI Modules (41 shipped)
|
||||
|
||||
Full listing: `get-shit-done/bin/lib/*.cjs`.
|
||||
|
||||
@@ -356,6 +356,7 @@ Full listing: `get-shit-done/bin/lib/*.cjs`.
|
||||
|--------|----------------|
|
||||
| `artifacts.cjs` | Canonical artifact registry — known `.planning/` root file names; used by `gsd-health` W019 lint |
|
||||
| `audit.cjs` | Audit dispatch, audit open sessions, audit storage helpers |
|
||||
| `command-aliases.generated.cjs` | Generated CJS alias/subcommand metadata for manifest-backed family routers |
|
||||
| `commands.cjs` | Misc CLI commands (slug, timestamp, todos, scaffolding, stats) |
|
||||
| `config-schema.cjs` | Single source of truth for `VALID_CONFIG_KEYS` and dynamic key patterns; imported by both the validator and the config-schema-docs parity test |
|
||||
| `config.cjs` | `config.json` read/write, section initialization; imports validator from `config-schema.cjs` |
|
||||
@@ -368,23 +369,30 @@ Full listing: `get-shit-done/bin/lib/*.cjs`.
|
||||
| `gap-checker.cjs` | Post-planning gap analysis (#2493): unified REQUIREMENTS.md + CONTEXT.md decisions vs PLAN.md coverage report (`gsd-tools gap-analysis`) |
|
||||
| `graphify.cjs` | Knowledge-graph build/query/status/diff for `/gsd-graphify` |
|
||||
| `gsd2-import.cjs` | External-plan ingest for `/gsd-from-gsd2` |
|
||||
| `init-command-router.cjs` | Thin CJS subcommand router adapter for `gsd-tools init` |
|
||||
| `init.cjs` | Compound context loading for each workflow type |
|
||||
| `install-profiles.cjs` | Install profile allowlist + skill staging for `--minimal` install (#2762); single source of truth for which `gsd-*` skills/agents land in runtime config dirs |
|
||||
| `intel.cjs` | Codebase intel store backing `/gsd-intel` and `gsd-intel-updater` |
|
||||
| `learnings.cjs` | Cross-phase learnings extraction for `/gsd-extract-learnings` |
|
||||
| `milestone.cjs` | Milestone archival, requirements marking |
|
||||
| `model-profiles.cjs` | Model profile resolution table (authoritative profile data) |
|
||||
| `phase-command-router.cjs` | Thin CJS subcommand router adapter for `gsd-tools phase` |
|
||||
| `phase.cjs` | Phase directory operations, decimal numbering, plan indexing |
|
||||
| `phases-command-router.cjs` | Thin CJS subcommand router adapter for `gsd-tools phases` |
|
||||
| `planning-workspace.cjs` | Planning path/workstream seam (`planningDir`, `planningPaths`, active-workstream routing, `.planning/.lock` orchestration) |
|
||||
| `profile-output.cjs` | Profile rendering, USER-PROFILE.md and dev-preferences.md generation |
|
||||
| `profile-pipeline.cjs` | User behavioral profiling data pipeline, session file scanning |
|
||||
| `roadmap-command-router.cjs` | Thin CJS subcommand router adapter for `gsd-tools roadmap` |
|
||||
| `roadmap.cjs` | ROADMAP.md parsing, phase extraction, plan progress |
|
||||
| `schema-detect.cjs` | Schema-drift detection for ORM patterns (Prisma, Drizzle, etc.) |
|
||||
| `secrets.cjs` | Secret-config masking convention (`****<last-4>`) for integration keys managed by `/gsd-settings-integrations` — keeps plaintext out of `config-set` output |
|
||||
| `security.cjs` | Path traversal prevention, prompt injection detection, safe JSON/shell helpers |
|
||||
| `state-command-router.cjs` | Thin CJS subcommand router adapter for `gsd-tools state` |
|
||||
| `state.cjs` | STATE.md parsing, updating, progression, metrics |
|
||||
| `template.cjs` | Template selection and filling with variable substitution |
|
||||
| `uat.cjs` | UAT file parsing, verification debt tracking, audit-uat support |
|
||||
| `validate-command-router.cjs` | Thin CJS subcommand router adapter for `gsd-tools validate` |
|
||||
| `verify-command-router.cjs` | Thin CJS subcommand router adapter for `gsd-tools verify` |
|
||||
| `verify.cjs` | Plan structure, phase completeness, reference, commit validation |
|
||||
| `workstream.cjs` | Workstream CRUD, migration, session-scoped active pointer |
|
||||
|
||||
|
||||
@@ -190,6 +190,13 @@ const workstream = require('./lib/workstream.cjs');
|
||||
const docs = require('./lib/docs.cjs');
|
||||
const learnings = require('./lib/learnings.cjs');
|
||||
const gapChecker = require('./lib/gap-checker.cjs');
|
||||
const { routeStateCommand } = require('./lib/state-command-router.cjs');
|
||||
const { routeVerifyCommand } = require('./lib/verify-command-router.cjs');
|
||||
const { routeInitCommand } = require('./lib/init-command-router.cjs');
|
||||
const { routePhaseCommand } = require('./lib/phase-command-router.cjs');
|
||||
const { routePhasesCommand } = require('./lib/phases-command-router.cjs');
|
||||
const { routeValidateCommand } = require('./lib/validate-command-router.cjs');
|
||||
const { routeRoadmapCommand } = require('./lib/roadmap-command-router.cjs');
|
||||
|
||||
// ─── Arg parsing helpers ──────────────────────────────────────────────────────
|
||||
|
||||
@@ -430,73 +437,14 @@ function extractField(obj, fieldPath) {
|
||||
async function runCommand(command, args, cwd, raw, defaultValue) {
|
||||
switch (command) {
|
||||
case 'state': {
|
||||
const subcommand = args[1];
|
||||
if (subcommand === 'json') {
|
||||
state.cmdStateJson(cwd, raw);
|
||||
} else if (subcommand === 'update') {
|
||||
state.cmdStateUpdate(cwd, args[2], args[3]);
|
||||
} else if (subcommand === 'get') {
|
||||
state.cmdStateGet(cwd, args[2], raw);
|
||||
} else if (subcommand === 'patch') {
|
||||
const patches = {};
|
||||
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);
|
||||
} else if (subcommand === 'advance-plan') {
|
||||
state.cmdStateAdvancePlan(cwd, raw);
|
||||
} else if (subcommand === 'record-metric') {
|
||||
const { phase: p, plan, duration, tasks, files } = parseNamedArgs(args, ['phase', 'plan', 'duration', 'tasks', 'files']);
|
||||
state.cmdStateRecordMetric(cwd, { phase: p, plan, duration, tasks, files }, raw);
|
||||
} else if (subcommand === 'update-progress') {
|
||||
state.cmdStateUpdateProgress(cwd, raw);
|
||||
} else if (subcommand === 'add-decision') {
|
||||
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);
|
||||
} else if (subcommand === 'add-blocker') {
|
||||
const { text, 'text-file': text_file } = parseNamedArgs(args, ['text', 'text-file']);
|
||||
state.cmdStateAddBlocker(cwd, { text, text_file }, raw);
|
||||
} else if (subcommand === 'resolve-blocker') {
|
||||
state.cmdStateResolveBlocker(cwd, parseNamedArgs(args, ['text']).text, raw);
|
||||
} else if (subcommand === 'record-session') {
|
||||
const { 'stopped-at': stopped_at, 'resume-file': resume_file } = parseNamedArgs(args, ['stopped-at', 'resume-file']);
|
||||
state.cmdStateRecordSession(cwd, { stopped_at, resume_file: resume_file || 'None' }, raw);
|
||||
} else if (subcommand === 'begin-phase') {
|
||||
const { phase: p, name, plans } = parseNamedArgs(args, ['phase', 'name', 'plans']);
|
||||
state.cmdStateBeginPhase(cwd, p, name, plans !== null ? parseInt(plans, 10) : null, raw);
|
||||
} else if (subcommand === 'signal-waiting') {
|
||||
const { type, question, options, phase: p } = parseNamedArgs(args, ['type', 'question', 'options', 'phase']);
|
||||
state.cmdSignalWaiting(cwd, type, question, options, p, raw);
|
||||
} else if (subcommand === 'signal-resume') {
|
||||
state.cmdSignalResume(cwd, raw);
|
||||
} else if (subcommand === 'planned-phase') {
|
||||
const { phase: p, name, plans } = parseNamedArgs(args, ['phase', 'name', 'plans']);
|
||||
state.cmdStatePlannedPhase(cwd, p, plans !== null ? parseInt(plans, 10) : null, raw);
|
||||
} else if (subcommand === 'validate') {
|
||||
state.cmdStateValidate(cwd, raw);
|
||||
} else if (subcommand === 'sync') {
|
||||
const { verify } = parseNamedArgs(args, [], ['verify']);
|
||||
state.cmdStateSync(cwd, { verify }, raw);
|
||||
} else if (subcommand === 'prune') {
|
||||
const { 'keep-recent': keepRecent, 'dry-run': dryRun } = parseNamedArgs(args, ['keep-recent'], ['dry-run']);
|
||||
state.cmdStatePrune(cwd, { keepRecent: keepRecent || '3', dryRun: !!dryRun }, raw);
|
||||
} else if (subcommand === 'complete-phase') {
|
||||
state.cmdStateCompletePhase(cwd, raw);
|
||||
} else if (subcommand === 'milestone-switch') {
|
||||
// Bug #2630: reset STATE.md frontmatter + Current Position for new milestone.
|
||||
// NB: the flag is `--milestone`, not `--version` — gsd-tools reserves
|
||||
// `--version` as a globally-invalid help flag (see NEVER_VALID_FLAGS above).
|
||||
const { milestone, name } = parseNamedArgs(args, ['milestone', 'name']);
|
||||
state.cmdStateMilestoneSwitch(cwd, milestone, name, raw);
|
||||
} else if (subcommand === undefined || subcommand === 'load') {
|
||||
state.cmdStateLoad(cwd, raw);
|
||||
} else {
|
||||
error(`Unknown state subcommand: "${subcommand}". Available: load, json, get, patch, update, advance-plan, record-metric, update-progress, add-decision, add-blocker, resolve-blocker, record-session, begin-phase, signal-waiting, signal-resume, planned-phase, validate, sync, prune, complete-phase, milestone-switch`);
|
||||
}
|
||||
routeStateCommand({
|
||||
state,
|
||||
args,
|
||||
cwd,
|
||||
raw,
|
||||
parseNamedArgs,
|
||||
error,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -590,27 +538,13 @@ async function runCommand(command, args, cwd, raw, defaultValue) {
|
||||
}
|
||||
|
||||
case 'verify': {
|
||||
const subcommand = args[1];
|
||||
if (subcommand === 'plan-structure') {
|
||||
verify.cmdVerifyPlanStructure(cwd, args[2], raw);
|
||||
} else if (subcommand === 'phase-completeness') {
|
||||
verify.cmdVerifyPhaseCompleteness(cwd, args[2], raw);
|
||||
} else if (subcommand === 'references') {
|
||||
verify.cmdVerifyReferences(cwd, args[2], raw);
|
||||
} else if (subcommand === 'commits') {
|
||||
verify.cmdVerifyCommits(cwd, args.slice(2), raw);
|
||||
} else if (subcommand === 'artifacts') {
|
||||
verify.cmdVerifyArtifacts(cwd, args[2], raw);
|
||||
} else if (subcommand === 'key-links') {
|
||||
verify.cmdVerifyKeyLinks(cwd, args[2], raw);
|
||||
} else if (subcommand === 'schema-drift') {
|
||||
const skipFlag = args.includes('--skip');
|
||||
verify.cmdVerifySchemaDrift(cwd, args[2], skipFlag, raw);
|
||||
} else if (subcommand === 'codebase-drift') {
|
||||
verify.cmdVerifyCodebaseDrift(cwd, raw);
|
||||
} else {
|
||||
error('Unknown verify subcommand. Available: plan-structure, phase-completeness, references, commits, artifacts, key-links, schema-drift, codebase-drift');
|
||||
}
|
||||
routeVerifyCommand({
|
||||
verify,
|
||||
args,
|
||||
cwd,
|
||||
raw,
|
||||
error,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -680,37 +614,25 @@ async function runCommand(command, args, cwd, raw, defaultValue) {
|
||||
}
|
||||
|
||||
case 'phases': {
|
||||
const subcommand = args[1];
|
||||
if (subcommand === 'list') {
|
||||
const typeIndex = args.indexOf('--type');
|
||||
const phaseIndex = args.indexOf('--phase');
|
||||
const options = {
|
||||
type: typeIndex !== -1 ? args[typeIndex + 1] : null,
|
||||
phase: phaseIndex !== -1 ? args[phaseIndex + 1] : null,
|
||||
includeArchived: args.includes('--include-archived'),
|
||||
};
|
||||
phase.cmdPhasesList(cwd, options, raw);
|
||||
} else if (subcommand === 'clear') {
|
||||
milestone.cmdPhasesClear(cwd, raw, args.slice(2));
|
||||
} else {
|
||||
error('Unknown phases subcommand. Available: list, clear');
|
||||
}
|
||||
routePhasesCommand({
|
||||
phase,
|
||||
milestone,
|
||||
args,
|
||||
cwd,
|
||||
raw,
|
||||
error,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'roadmap': {
|
||||
const subcommand = args[1];
|
||||
if (subcommand === 'get-phase') {
|
||||
roadmap.cmdRoadmapGetPhase(cwd, args[2], raw);
|
||||
} else if (subcommand === 'analyze') {
|
||||
roadmap.cmdRoadmapAnalyze(cwd, raw);
|
||||
} else if (subcommand === 'update-plan-progress') {
|
||||
roadmap.cmdRoadmapUpdatePlanProgress(cwd, args[2], raw);
|
||||
} else if (subcommand === 'annotate-dependencies') {
|
||||
roadmap.cmdRoadmapAnnotateDependencies(cwd, args[2], raw);
|
||||
} else {
|
||||
error('Unknown roadmap subcommand. Available: get-phase, analyze, update-plan-progress, annotate-dependencies');
|
||||
}
|
||||
routeRoadmapCommand({
|
||||
roadmap,
|
||||
args,
|
||||
cwd,
|
||||
raw,
|
||||
error,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -732,42 +654,13 @@ async function runCommand(command, args, cwd, raw, defaultValue) {
|
||||
}
|
||||
|
||||
case 'phase': {
|
||||
const subcommand = args[1];
|
||||
if (subcommand === 'next-decimal') {
|
||||
phase.cmdPhaseNextDecimal(cwd, args[2], raw);
|
||||
} else if (subcommand === 'add') {
|
||||
const idIdx = args.indexOf('--id');
|
||||
let customId = null;
|
||||
const descArgs = [];
|
||||
for (let i = 2; i < args.length; i++) {
|
||||
if (args[i] === '--id' && i + 1 < args.length) {
|
||||
customId = args[i + 1];
|
||||
i++; // skip value
|
||||
} else {
|
||||
descArgs.push(args[i]);
|
||||
}
|
||||
}
|
||||
phase.cmdPhaseAdd(cwd, descArgs.join(' '), raw, customId);
|
||||
} else if (subcommand === 'add-batch') {
|
||||
// Accepts JSON array of descriptions via --descriptions '[...]' or positional args
|
||||
const descFlagIdx = args.indexOf('--descriptions');
|
||||
let descriptions;
|
||||
if (descFlagIdx !== -1 && args[descFlagIdx + 1]) {
|
||||
try { descriptions = JSON.parse(args[descFlagIdx + 1]); } catch (e) { error('--descriptions must be a JSON array'); }
|
||||
} else {
|
||||
descriptions = args.slice(2).filter(a => a !== '--raw');
|
||||
}
|
||||
phase.cmdPhaseAddBatch(cwd, descriptions, raw);
|
||||
} else if (subcommand === 'insert') {
|
||||
phase.cmdPhaseInsert(cwd, args[2], args.slice(3).join(' '), raw);
|
||||
} else if (subcommand === 'remove') {
|
||||
const forceFlag = args.includes('--force');
|
||||
phase.cmdPhaseRemove(cwd, args[2], { force: forceFlag }, raw);
|
||||
} else if (subcommand === 'complete') {
|
||||
phase.cmdPhaseComplete(cwd, args[2], raw);
|
||||
} else {
|
||||
error('Unknown phase subcommand. Available: next-decimal, add, add-batch, insert, remove, complete');
|
||||
}
|
||||
routePhaseCommand({
|
||||
phase,
|
||||
args,
|
||||
cwd,
|
||||
raw,
|
||||
error,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -784,58 +677,15 @@ async function runCommand(command, args, cwd, raw, defaultValue) {
|
||||
}
|
||||
|
||||
case 'validate': {
|
||||
const subcommand = args[1];
|
||||
if (subcommand === 'consistency') {
|
||||
verify.cmdValidateConsistency(cwd, raw);
|
||||
} else if (subcommand === 'health') {
|
||||
const repairFlag = args.includes('--repair');
|
||||
const backfillFlag = args.includes('--backfill');
|
||||
verify.cmdValidateHealth(cwd, { repair: repairFlag, backfill: backfillFlag }, raw);
|
||||
} else if (subcommand === 'agents') {
|
||||
verify.cmdValidateAgents(cwd, raw);
|
||||
} else if (subcommand === 'context') {
|
||||
// The model self-reports tokensUsed and contextWindow — the SDK has
|
||||
// no privileged access to either. Recommendation copy lives here
|
||||
// (the renderer), not in the classifier, so it can change without
|
||||
// re-validating the math layer.
|
||||
const opts = parseNamedArgs(args, ['tokens-used', 'context-window']);
|
||||
if (opts['tokens-used'] === null) {
|
||||
error('--tokens-used <integer> is required for `validate context`');
|
||||
break;
|
||||
}
|
||||
if (opts['context-window'] === null) {
|
||||
error('--context-window <integer> is required for `validate context`');
|
||||
break;
|
||||
}
|
||||
const { classifyContextUtilization, STATES } = require('./lib/context-utilization.cjs');
|
||||
const RECOMMENDATIONS = {
|
||||
[STATES.HEALTHY]: null,
|
||||
[STATES.WARNING]: 'Context is approaching the fracture zone — consider /gsd-thread to continue in a fresh window.',
|
||||
[STATES.CRITICAL]: 'Reasoning quality may degrade past 70% utilization (fracture point). Run /gsd-thread now to preserve output quality.',
|
||||
};
|
||||
let classified;
|
||||
try {
|
||||
classified = classifyContextUtilization(Number(opts['tokens-used']), Number(opts['context-window']));
|
||||
} catch (e) {
|
||||
// Translate the classifier's TypeError into a CLI-shaped error
|
||||
// message that names the offending flag.
|
||||
const flag = /tokensUsed/.test(e.message) ? '--tokens-used' : '--context-window';
|
||||
error(`${flag} must be a non-negative integer (window > 0), got the values supplied`);
|
||||
break;
|
||||
}
|
||||
const result = { ...classified, recommendation: RECOMMENDATIONS[classified.state] };
|
||||
if (args.includes('--json')) {
|
||||
core.output(result, raw);
|
||||
} else {
|
||||
const lines = [`Context utilization: ${result.percent}% (${result.state})`];
|
||||
if (result.recommendation) lines.push(result.recommendation);
|
||||
// Use core.output's rawValue path for the sync-flush guarantee
|
||||
// — process.stdout.write can be truncated on process exit.
|
||||
core.output(result, true, lines.join('\n'));
|
||||
}
|
||||
} else {
|
||||
error('Unknown validate subcommand. Available: consistency, health, agents, context');
|
||||
}
|
||||
routeValidateCommand({
|
||||
verify,
|
||||
args,
|
||||
cwd,
|
||||
raw,
|
||||
parseNamedArgs,
|
||||
output: core.output,
|
||||
error,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -904,66 +754,14 @@ async function runCommand(command, args, cwd, raw, defaultValue) {
|
||||
}
|
||||
|
||||
case 'init': {
|
||||
const workflow = args[1];
|
||||
switch (workflow) {
|
||||
case 'execute-phase': {
|
||||
const { validate: epValidate, tdd: epTdd } = parseNamedArgs(args, [], ['validate', 'tdd']);
|
||||
init.cmdInitExecutePhase(cwd, args[2], raw, { validate: epValidate, tdd: epTdd });
|
||||
break;
|
||||
}
|
||||
case 'plan-phase': {
|
||||
const { validate: ppValidate, tdd: ppTdd } = parseNamedArgs(args, [], ['validate', 'tdd']);
|
||||
init.cmdInitPlanPhase(cwd, args[2], raw, { validate: ppValidate, tdd: ppTdd });
|
||||
break;
|
||||
}
|
||||
case 'new-project':
|
||||
init.cmdInitNewProject(cwd, raw);
|
||||
break;
|
||||
case 'new-milestone':
|
||||
init.cmdInitNewMilestone(cwd, raw);
|
||||
break;
|
||||
case 'quick':
|
||||
init.cmdInitQuick(cwd, args.slice(2).join(' '), raw);
|
||||
break;
|
||||
case 'ingest-docs':
|
||||
init.cmdInitIngestDocs(cwd, raw);
|
||||
break;
|
||||
case 'resume':
|
||||
init.cmdInitResume(cwd, raw);
|
||||
break;
|
||||
case 'verify-work':
|
||||
init.cmdInitVerifyWork(cwd, args[2], raw);
|
||||
break;
|
||||
case 'phase-op':
|
||||
init.cmdInitPhaseOp(cwd, args[2], raw);
|
||||
break;
|
||||
case 'todos':
|
||||
init.cmdInitTodos(cwd, args[2], raw);
|
||||
break;
|
||||
case 'milestone-op':
|
||||
init.cmdInitMilestoneOp(cwd, raw);
|
||||
break;
|
||||
case 'map-codebase':
|
||||
init.cmdInitMapCodebase(cwd, raw);
|
||||
break;
|
||||
case 'progress':
|
||||
init.cmdInitProgress(cwd, raw);
|
||||
break;
|
||||
case 'manager':
|
||||
init.cmdInitManager(cwd, raw);
|
||||
break;
|
||||
case 'new-workspace':
|
||||
init.cmdInitNewWorkspace(cwd, raw);
|
||||
break;
|
||||
case 'list-workspaces':
|
||||
init.cmdInitListWorkspaces(cwd, raw);
|
||||
break;
|
||||
case 'remove-workspace':
|
||||
init.cmdInitRemoveWorkspace(cwd, args[2], raw);
|
||||
break;
|
||||
default:
|
||||
error(`Unknown init workflow: ${workflow}\nAvailable: execute-phase, plan-phase, new-project, new-milestone, quick, ingest-docs, resume, verify-work, phase-op, todos, milestone-op, map-codebase, progress, manager, new-workspace, list-workspaces, remove-workspace`);
|
||||
}
|
||||
routeInitCommand({
|
||||
init,
|
||||
args,
|
||||
cwd,
|
||||
raw,
|
||||
parseNamedArgs,
|
||||
error,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
118
get-shit-done/bin/lib/command-aliases.generated.cjs
Normal file
118
get-shit-done/bin/lib/command-aliases.generated.cjs
Normal file
@@ -0,0 +1,118 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* GENERATED FILE — state.*, verify.*, init.*, phase.*, phases.*, validate.*, and roadmap.* alias/subcommand metadata for CJS routing.
|
||||
* Source: sdk/src/query/command-manifest.{state,verify,init,phase,phases,validate,roadmap}.ts
|
||||
*/
|
||||
|
||||
const STATE_COMMAND_ALIASES = [
|
||||
{ canonical: 'state.load', aliases: [], subcommand: 'load', mutation: false },
|
||||
{ canonical: 'state.json', aliases: ['state json'], subcommand: 'json', mutation: false },
|
||||
{ canonical: 'state.get', aliases: ['state get'], subcommand: 'get', mutation: false },
|
||||
{ canonical: 'state.update', aliases: ['state update'], subcommand: 'update', mutation: true },
|
||||
{ canonical: 'state.patch', aliases: ['state patch'], subcommand: 'patch', mutation: true },
|
||||
{ canonical: 'state.begin-phase', aliases: ['state begin-phase'], subcommand: 'begin-phase', mutation: true },
|
||||
{ canonical: 'state.advance-plan', aliases: ['state advance-plan'], subcommand: 'advance-plan', mutation: true },
|
||||
{ canonical: 'state.record-metric', aliases: ['state record-metric'], subcommand: 'record-metric', mutation: true },
|
||||
{ canonical: 'state.update-progress', aliases: ['state update-progress'], subcommand: 'update-progress', mutation: true },
|
||||
{ canonical: 'state.add-decision', aliases: ['state add-decision'], subcommand: 'add-decision', mutation: true },
|
||||
{ canonical: 'state.add-blocker', aliases: ['state add-blocker'], subcommand: 'add-blocker', mutation: true },
|
||||
{ canonical: 'state.resolve-blocker', aliases: ['state resolve-blocker'], subcommand: 'resolve-blocker', mutation: true },
|
||||
{ canonical: 'state.record-session', aliases: ['state record-session'], subcommand: 'record-session', mutation: true },
|
||||
{ canonical: 'state.signal-waiting', aliases: ['state signal-waiting'], subcommand: 'signal-waiting', mutation: true },
|
||||
{ canonical: 'state.signal-resume', aliases: ['state signal-resume'], subcommand: 'signal-resume', mutation: true },
|
||||
{ canonical: 'state.planned-phase', aliases: ['state planned-phase'], subcommand: 'planned-phase', mutation: true },
|
||||
{ canonical: 'state.validate', aliases: ['state validate'], subcommand: 'validate', mutation: false },
|
||||
{ canonical: 'state.sync', aliases: ['state sync'], subcommand: 'sync', mutation: true },
|
||||
{ canonical: 'state.prune', aliases: ['state prune'], subcommand: 'prune', mutation: true },
|
||||
{ canonical: 'state.milestone-switch', aliases: ['state milestone-switch'], subcommand: 'milestone-switch', mutation: true },
|
||||
{ canonical: 'state.add-roadmap-evolution', aliases: ['state add-roadmap-evolution'], subcommand: 'add-roadmap-evolution', mutation: true },
|
||||
];
|
||||
|
||||
const VERIFY_COMMAND_ALIASES = [
|
||||
{ canonical: 'verify.plan-structure', aliases: ['verify plan-structure'], subcommand: 'plan-structure', mutation: false },
|
||||
{ canonical: 'verify.phase-completeness', aliases: ['verify phase-completeness'], subcommand: 'phase-completeness', mutation: false },
|
||||
{ canonical: 'verify.references', aliases: ['verify references'], subcommand: 'references', mutation: false },
|
||||
{ canonical: 'verify.commits', aliases: ['verify commits'], subcommand: 'commits', mutation: false },
|
||||
{ canonical: 'verify.artifacts', aliases: ['verify artifacts'], subcommand: 'artifacts', mutation: false },
|
||||
{ canonical: 'verify.key-links', aliases: ['verify key-links'], subcommand: 'key-links', mutation: false },
|
||||
{ canonical: 'verify.schema-drift', aliases: ['verify schema-drift'], subcommand: 'schema-drift', mutation: false },
|
||||
{ canonical: 'verify.codebase-drift', aliases: ['verify codebase-drift'], subcommand: 'codebase-drift', mutation: false },
|
||||
];
|
||||
|
||||
const INIT_COMMAND_ALIASES = [
|
||||
{ canonical: 'init.execute-phase', aliases: ['init execute-phase'], subcommand: 'execute-phase', mutation: false },
|
||||
{ canonical: 'init.plan-phase', aliases: ['init plan-phase'], subcommand: 'plan-phase', mutation: false },
|
||||
{ canonical: 'init.new-project', aliases: ['init new-project'], subcommand: 'new-project', mutation: false },
|
||||
{ canonical: 'init.new-milestone', aliases: ['init new-milestone'], subcommand: 'new-milestone', mutation: false },
|
||||
{ canonical: 'init.quick', aliases: ['init quick'], subcommand: 'quick', mutation: false },
|
||||
{ canonical: 'init.ingest-docs', aliases: ['init ingest-docs'], subcommand: 'ingest-docs', mutation: false },
|
||||
{ canonical: 'init.resume', aliases: ['init resume'], subcommand: 'resume', mutation: false },
|
||||
{ canonical: 'init.verify-work', aliases: ['init verify-work'], subcommand: 'verify-work', mutation: false },
|
||||
{ canonical: 'init.phase-op', aliases: ['init phase-op'], subcommand: 'phase-op', mutation: false },
|
||||
{ canonical: 'init.todos', aliases: ['init todos'], subcommand: 'todos', mutation: false },
|
||||
{ canonical: 'init.milestone-op', aliases: ['init milestone-op'], subcommand: 'milestone-op', mutation: false },
|
||||
{ canonical: 'init.map-codebase', aliases: ['init map-codebase'], subcommand: 'map-codebase', mutation: false },
|
||||
{ canonical: 'init.progress', aliases: ['init progress'], subcommand: 'progress', mutation: false },
|
||||
{ canonical: 'init.manager', aliases: ['init manager'], subcommand: 'manager', mutation: false },
|
||||
{ canonical: 'init.new-workspace', aliases: ['init new-workspace'], subcommand: 'new-workspace', mutation: false },
|
||||
{ canonical: 'init.list-workspaces', aliases: ['init list-workspaces'], subcommand: 'list-workspaces', mutation: false },
|
||||
{ canonical: 'init.remove-workspace', aliases: ['init remove-workspace'], subcommand: 'remove-workspace', mutation: false },
|
||||
];
|
||||
|
||||
const PHASE_COMMAND_ALIASES = [
|
||||
{ canonical: 'phase.list-plans', aliases: ['phase list-plans'], subcommand: 'list-plans', mutation: false },
|
||||
{ canonical: 'phase.list-artifacts', aliases: ['phase list-artifacts'], subcommand: 'list-artifacts', mutation: false },
|
||||
{ canonical: 'phase.next-decimal', aliases: ['phase next-decimal'], subcommand: 'next-decimal', mutation: false },
|
||||
{ canonical: 'phase.add', aliases: ['phase add'], subcommand: 'add', mutation: true },
|
||||
{ canonical: 'phase.add-batch', aliases: ['phase add-batch'], subcommand: 'add-batch', mutation: true },
|
||||
{ canonical: 'phase.insert', aliases: ['phase insert'], subcommand: 'insert', mutation: true },
|
||||
{ canonical: 'phase.remove', aliases: ['phase remove'], subcommand: 'remove', mutation: true },
|
||||
{ canonical: 'phase.complete', aliases: ['phase complete'], subcommand: 'complete', mutation: true },
|
||||
{ canonical: 'phase.scaffold', aliases: ['phase scaffold'], subcommand: 'scaffold', mutation: true },
|
||||
];
|
||||
|
||||
const PHASES_COMMAND_ALIASES = [
|
||||
{ canonical: 'phases.list', aliases: ['phases list'], subcommand: 'list', mutation: false },
|
||||
{ canonical: 'phases.clear', aliases: ['phases clear'], subcommand: 'clear', mutation: true },
|
||||
{ canonical: 'phases.archive', aliases: ['phases archive'], subcommand: 'archive', mutation: true },
|
||||
];
|
||||
|
||||
const VALIDATE_COMMAND_ALIASES = [
|
||||
{ canonical: 'validate.consistency', aliases: ['validate consistency'], subcommand: 'consistency', mutation: false },
|
||||
{ canonical: 'validate.health', aliases: ['validate health'], subcommand: 'health', mutation: false },
|
||||
{ canonical: 'validate.agents', aliases: ['validate agents'], subcommand: 'agents', mutation: false },
|
||||
{ canonical: 'validate.context', aliases: ['validate context'], subcommand: 'context', mutation: false },
|
||||
];
|
||||
|
||||
const ROADMAP_COMMAND_ALIASES = [
|
||||
{ canonical: 'roadmap.analyze', aliases: ['roadmap analyze'], subcommand: 'analyze', mutation: false },
|
||||
{ canonical: 'roadmap.get-phase', aliases: ['roadmap get-phase'], subcommand: 'get-phase', mutation: false },
|
||||
{ canonical: 'roadmap.update-plan-progress', aliases: ['roadmap update-plan-progress'], subcommand: 'update-plan-progress', mutation: true },
|
||||
{ canonical: 'roadmap.annotate-dependencies', aliases: ['roadmap annotate-dependencies'], subcommand: 'annotate-dependencies', mutation: true },
|
||||
];
|
||||
|
||||
const STATE_SUBCOMMANDS = STATE_COMMAND_ALIASES.map((entry) => entry.subcommand);
|
||||
const VERIFY_SUBCOMMANDS = VERIFY_COMMAND_ALIASES.map((entry) => entry.subcommand);
|
||||
const INIT_SUBCOMMANDS = INIT_COMMAND_ALIASES.map((entry) => entry.subcommand);
|
||||
const PHASE_SUBCOMMANDS = PHASE_COMMAND_ALIASES.map((entry) => entry.subcommand);
|
||||
const PHASES_SUBCOMMANDS = PHASES_COMMAND_ALIASES.map((entry) => entry.subcommand);
|
||||
const VALIDATE_SUBCOMMANDS = VALIDATE_COMMAND_ALIASES.map((entry) => entry.subcommand);
|
||||
const ROADMAP_SUBCOMMANDS = ROADMAP_COMMAND_ALIASES.map((entry) => entry.subcommand);
|
||||
|
||||
module.exports = {
|
||||
STATE_COMMAND_ALIASES,
|
||||
VERIFY_COMMAND_ALIASES,
|
||||
INIT_COMMAND_ALIASES,
|
||||
PHASE_COMMAND_ALIASES,
|
||||
PHASES_COMMAND_ALIASES,
|
||||
VALIDATE_COMMAND_ALIASES,
|
||||
ROADMAP_COMMAND_ALIASES,
|
||||
STATE_SUBCOMMANDS,
|
||||
VERIFY_SUBCOMMANDS,
|
||||
INIT_SUBCOMMANDS,
|
||||
PHASE_SUBCOMMANDS,
|
||||
PHASES_SUBCOMMANDS,
|
||||
VALIDATE_SUBCOMMANDS,
|
||||
ROADMAP_SUBCOMMANDS,
|
||||
};
|
||||
70
get-shit-done/bin/lib/init-command-router.cjs
Normal file
70
get-shit-done/bin/lib/init-command-router.cjs
Normal file
@@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
|
||||
const { INIT_SUBCOMMANDS } = require('./command-aliases.generated.cjs');
|
||||
|
||||
function routeInitCommand({ init, args, cwd, raw, parseNamedArgs, error }) {
|
||||
const workflow = args[1];
|
||||
switch (workflow) {
|
||||
case 'execute-phase': {
|
||||
const { validate: epValidate, tdd: epTdd } = parseNamedArgs(args, [], ['validate', 'tdd']);
|
||||
init.cmdInitExecutePhase(cwd, args[2], raw, { validate: epValidate, tdd: epTdd });
|
||||
break;
|
||||
}
|
||||
case 'plan-phase': {
|
||||
const { validate: ppValidate, tdd: ppTdd } = parseNamedArgs(args, [], ['validate', 'tdd']);
|
||||
init.cmdInitPlanPhase(cwd, args[2], raw, { validate: ppValidate, tdd: ppTdd });
|
||||
break;
|
||||
}
|
||||
case 'new-project':
|
||||
init.cmdInitNewProject(cwd, raw);
|
||||
break;
|
||||
case 'new-milestone':
|
||||
init.cmdInitNewMilestone(cwd, raw);
|
||||
break;
|
||||
case 'quick':
|
||||
init.cmdInitQuick(cwd, args.slice(2).join(' '), raw);
|
||||
break;
|
||||
case 'ingest-docs':
|
||||
init.cmdInitIngestDocs(cwd, raw);
|
||||
break;
|
||||
case 'resume':
|
||||
init.cmdInitResume(cwd, raw);
|
||||
break;
|
||||
case 'verify-work':
|
||||
init.cmdInitVerifyWork(cwd, args[2], raw);
|
||||
break;
|
||||
case 'phase-op':
|
||||
init.cmdInitPhaseOp(cwd, args[2], raw);
|
||||
break;
|
||||
case 'todos':
|
||||
init.cmdInitTodos(cwd, args[2], raw);
|
||||
break;
|
||||
case 'milestone-op':
|
||||
init.cmdInitMilestoneOp(cwd, raw);
|
||||
break;
|
||||
case 'map-codebase':
|
||||
init.cmdInitMapCodebase(cwd, raw);
|
||||
break;
|
||||
case 'progress':
|
||||
init.cmdInitProgress(cwd, raw);
|
||||
break;
|
||||
case 'manager':
|
||||
init.cmdInitManager(cwd, raw);
|
||||
break;
|
||||
case 'new-workspace':
|
||||
init.cmdInitNewWorkspace(cwd, raw);
|
||||
break;
|
||||
case 'list-workspaces':
|
||||
init.cmdInitListWorkspaces(cwd, raw);
|
||||
break;
|
||||
case 'remove-workspace':
|
||||
init.cmdInitRemoveWorkspace(cwd, args[2], raw);
|
||||
break;
|
||||
default:
|
||||
error(`Unknown init workflow: ${workflow}\nAvailable: ${INIT_SUBCOMMANDS.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
routeInitCommand,
|
||||
};
|
||||
49
get-shit-done/bin/lib/phase-command-router.cjs
Normal file
49
get-shit-done/bin/lib/phase-command-router.cjs
Normal file
@@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
const { PHASE_SUBCOMMANDS } = require('./command-aliases.generated.cjs');
|
||||
|
||||
function routePhaseCommand({ phase, args, cwd, raw, error }) {
|
||||
const subcommand = args[1];
|
||||
|
||||
if (subcommand === 'next-decimal') {
|
||||
phase.cmdPhaseNextDecimal(cwd, args[2], raw);
|
||||
} else if (subcommand === 'add') {
|
||||
let customId = null;
|
||||
const descArgs = [];
|
||||
for (let i = 2; i < args.length; i++) {
|
||||
if (args[i] === '--id' && i + 1 < args.length) {
|
||||
customId = args[i + 1];
|
||||
i++;
|
||||
} else {
|
||||
descArgs.push(args[i]);
|
||||
}
|
||||
}
|
||||
phase.cmdPhaseAdd(cwd, descArgs.join(' '), raw, customId);
|
||||
} else if (subcommand === 'add-batch') {
|
||||
const descFlagIdx = args.indexOf('--descriptions');
|
||||
let descriptions;
|
||||
if (descFlagIdx !== -1 && args[descFlagIdx + 1]) {
|
||||
try {
|
||||
descriptions = JSON.parse(args[descFlagIdx + 1]);
|
||||
} catch {
|
||||
error('--descriptions must be a JSON array');
|
||||
}
|
||||
} else {
|
||||
descriptions = args.slice(2).filter(a => a !== '--raw');
|
||||
}
|
||||
phase.cmdPhaseAddBatch(cwd, descriptions, raw);
|
||||
} else if (subcommand === 'insert') {
|
||||
phase.cmdPhaseInsert(cwd, args[2], args.slice(3).join(' '), raw);
|
||||
} else if (subcommand === 'remove') {
|
||||
const forceFlag = args.includes('--force');
|
||||
phase.cmdPhaseRemove(cwd, args[2], { force: forceFlag }, raw);
|
||||
} else if (subcommand === 'complete') {
|
||||
phase.cmdPhaseComplete(cwd, args[2], raw);
|
||||
} else {
|
||||
error(`Unknown phase subcommand. Available: ${PHASE_SUBCOMMANDS.filter((s) => s !== 'list-plans' && s !== 'list-artifacts' && s !== 'scaffold').join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
routePhaseCommand,
|
||||
};
|
||||
36
get-shit-done/bin/lib/phases-command-router.cjs
Normal file
36
get-shit-done/bin/lib/phases-command-router.cjs
Normal file
@@ -0,0 +1,36 @@
|
||||
'use strict';
|
||||
|
||||
const { PHASES_SUBCOMMANDS } = require('./command-aliases.generated.cjs');
|
||||
|
||||
/**
|
||||
* Manifest-backed phases subcommand router.
|
||||
* Keeps gsd-tools.cjs thin while preserving current CJS semantics:
|
||||
* - list
|
||||
* - clear
|
||||
*
|
||||
* Note: `archive` is currently SDK-only (`phases.archive` handler in SDK query
|
||||
* registry). CJS `gsd-tools phases` intentionally supports list/clear only.
|
||||
*/
|
||||
function routePhasesCommand({ phase, milestone, args, cwd, raw, error }) {
|
||||
const subcommand = args[1];
|
||||
|
||||
if (subcommand === 'list') {
|
||||
const typeIndex = args.indexOf('--type');
|
||||
const phaseIndex = args.indexOf('--phase');
|
||||
const options = {
|
||||
type: typeIndex !== -1 ? args[typeIndex + 1] : null,
|
||||
phase: phaseIndex !== -1 ? args[phaseIndex + 1] : null,
|
||||
includeArchived: args.includes('--include-archived'),
|
||||
};
|
||||
phase.cmdPhasesList(cwd, options, raw);
|
||||
} else if (subcommand === 'clear') {
|
||||
milestone.cmdPhasesClear(cwd, raw, args.slice(2));
|
||||
} else {
|
||||
const cjsSupported = PHASES_SUBCOMMANDS.filter((s) => s !== 'archive');
|
||||
error(`Unknown phases subcommand. Available: ${cjsSupported.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
routePhasesCommand,
|
||||
};
|
||||
23
get-shit-done/bin/lib/roadmap-command-router.cjs
Normal file
23
get-shit-done/bin/lib/roadmap-command-router.cjs
Normal file
@@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
const { ROADMAP_SUBCOMMANDS } = require('./command-aliases.generated.cjs');
|
||||
|
||||
function routeRoadmapCommand({ roadmap, args, cwd, raw, error }) {
|
||||
const subcommand = args[1];
|
||||
|
||||
if (subcommand === 'get-phase') {
|
||||
roadmap.cmdRoadmapGetPhase(cwd, args[2], raw);
|
||||
} else if (subcommand === 'analyze') {
|
||||
roadmap.cmdRoadmapAnalyze(cwd, raw);
|
||||
} else if (subcommand === 'update-plan-progress') {
|
||||
roadmap.cmdRoadmapUpdatePlanProgress(cwd, args[2], raw);
|
||||
} else if (subcommand === 'annotate-dependencies') {
|
||||
roadmap.cmdRoadmapAnnotateDependencies(cwd, args[2], raw);
|
||||
} else {
|
||||
error(`Unknown roadmap subcommand. Available: ${ROADMAP_SUBCOMMANDS.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
routeRoadmapCommand,
|
||||
};
|
||||
90
get-shit-done/bin/lib/state-command-router.cjs
Normal file
90
get-shit-done/bin/lib/state-command-router.cjs
Normal file
@@ -0,0 +1,90 @@
|
||||
'use strict';
|
||||
|
||||
const { STATE_SUBCOMMANDS } = require('./command-aliases.generated.cjs');
|
||||
|
||||
/**
|
||||
* Manifest-backed state subcommand router.
|
||||
* Keeps gsd-tools.cjs thin while preserving existing command semantics.
|
||||
*/
|
||||
function routeStateCommand({ state, args, cwd, raw, parseNamedArgs, error }) {
|
||||
const subcommand = args[1];
|
||||
|
||||
if (subcommand === 'json') {
|
||||
state.cmdStateJson(cwd, raw);
|
||||
} else if (subcommand === 'update') {
|
||||
state.cmdStateUpdate(cwd, args[2], args[3]);
|
||||
} else if (subcommand === 'get') {
|
||||
state.cmdStateGet(cwd, args[2], raw);
|
||||
} else if (subcommand === 'patch') {
|
||||
const patches = {};
|
||||
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);
|
||||
} else if (subcommand === 'advance-plan') {
|
||||
state.cmdStateAdvancePlan(cwd, raw);
|
||||
} else if (subcommand === 'record-metric') {
|
||||
const { phase: p, plan, duration, tasks, files } = parseNamedArgs(args, ['phase', 'plan', 'duration', 'tasks', 'files']);
|
||||
state.cmdStateRecordMetric(cwd, { phase: p, plan, duration, tasks, files }, raw);
|
||||
} else if (subcommand === 'update-progress') {
|
||||
state.cmdStateUpdateProgress(cwd, raw);
|
||||
} else if (subcommand === 'add-decision') {
|
||||
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);
|
||||
} else if (subcommand === 'add-blocker') {
|
||||
const { text, 'text-file': text_file } = parseNamedArgs(args, ['text', 'text-file']);
|
||||
state.cmdStateAddBlocker(cwd, { text, text_file }, raw);
|
||||
} else if (subcommand === 'resolve-blocker') {
|
||||
state.cmdStateResolveBlocker(cwd, parseNamedArgs(args, ['text']).text, raw);
|
||||
} else if (subcommand === 'record-session') {
|
||||
const { 'stopped-at': stopped_at, 'resume-file': resume_file } = parseNamedArgs(args, ['stopped-at', 'resume-file']);
|
||||
state.cmdStateRecordSession(cwd, { stopped_at, resume_file: resume_file || 'None' }, raw);
|
||||
} else if (subcommand === 'begin-phase') {
|
||||
const { phase: p, name, plans } = parseNamedArgs(args, ['phase', 'name', 'plans']);
|
||||
const parsedPlans = plans == null ? null : Number.parseInt(plans, 10);
|
||||
if (plans != null && Number.isNaN(parsedPlans)) {
|
||||
return error('Invalid --plans value. Expected an integer.');
|
||||
}
|
||||
state.cmdStateBeginPhase(cwd, p, name, parsedPlans, raw);
|
||||
} else if (subcommand === 'signal-waiting') {
|
||||
const { type, question, options, phase: p } = parseNamedArgs(args, ['type', 'question', 'options', 'phase']);
|
||||
state.cmdSignalWaiting(cwd, type, question, options, p, raw);
|
||||
} else if (subcommand === 'signal-resume') {
|
||||
state.cmdSignalResume(cwd, raw);
|
||||
} else if (subcommand === 'planned-phase') {
|
||||
const { phase: p, plans } = parseNamedArgs(args, ['phase', 'name', 'plans']);
|
||||
const parsedPlans = plans == null ? null : Number.parseInt(plans, 10);
|
||||
if (plans != null && Number.isNaN(parsedPlans)) {
|
||||
return error('Invalid --plans value. Expected an integer.');
|
||||
}
|
||||
state.cmdStatePlannedPhase(cwd, p, parsedPlans, raw);
|
||||
} else if (subcommand === 'validate') {
|
||||
state.cmdStateValidate(cwd, raw);
|
||||
} else if (subcommand === 'sync') {
|
||||
const { verify } = parseNamedArgs(args, [], ['verify']);
|
||||
state.cmdStateSync(cwd, { verify }, raw);
|
||||
} else if (subcommand === 'prune') {
|
||||
const { 'keep-recent': keepRecent, 'dry-run': dryRun } = parseNamedArgs(args, ['keep-recent'], ['dry-run']);
|
||||
state.cmdStatePrune(cwd, { keepRecent: keepRecent || '3', dryRun: !!dryRun }, raw);
|
||||
} else if (subcommand === 'complete-phase') {
|
||||
state.cmdStateCompletePhase(cwd, raw);
|
||||
} else if (subcommand === 'milestone-switch') {
|
||||
const { milestone, name } = parseNamedArgs(args, ['milestone', 'name']);
|
||||
state.cmdStateMilestoneSwitch(cwd, milestone, name, raw);
|
||||
} else if (subcommand === 'add-roadmap-evolution') {
|
||||
error('state add-roadmap-evolution is SDK-only. Use: gsd-sdk query state.add-roadmap-evolution ...');
|
||||
} else if (subcommand === undefined || subcommand === 'load') {
|
||||
state.cmdStateLoad(cwd, raw);
|
||||
} else {
|
||||
const available = ['load', 'complete-phase', ...STATE_SUBCOMMANDS.filter((s) => s !== 'load')];
|
||||
error(`Unknown state subcommand: "${subcommand}". Available: ${available.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
routeStateCommand,
|
||||
};
|
||||
55
get-shit-done/bin/lib/validate-command-router.cjs
Normal file
55
get-shit-done/bin/lib/validate-command-router.cjs
Normal file
@@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
const { VALIDATE_SUBCOMMANDS } = require('./command-aliases.generated.cjs');
|
||||
|
||||
function routeValidateCommand({ verify, args, cwd, raw, parseNamedArgs, output, error }) {
|
||||
const subcommand = args[1];
|
||||
|
||||
if (subcommand === 'consistency') {
|
||||
verify.cmdValidateConsistency(cwd, raw);
|
||||
} else if (subcommand === 'health') {
|
||||
const repairFlag = args.includes('--repair');
|
||||
const backfillFlag = args.includes('--backfill');
|
||||
verify.cmdValidateHealth(cwd, { repair: repairFlag, backfill: backfillFlag }, raw);
|
||||
} else if (subcommand === 'agents') {
|
||||
verify.cmdValidateAgents(cwd, raw);
|
||||
} else if (subcommand === 'context') {
|
||||
const opts = parseNamedArgs(args, ['tokens-used', 'context-window']);
|
||||
if (opts['tokens-used'] === null) {
|
||||
error('--tokens-used <integer> is required for `validate context`');
|
||||
return;
|
||||
}
|
||||
if (opts['context-window'] === null) {
|
||||
error('--context-window <integer> is required for `validate context`');
|
||||
return;
|
||||
}
|
||||
const { classifyContextUtilization, STATES } = require('./context-utilization.cjs');
|
||||
const RECOMMENDATIONS = {
|
||||
[STATES.HEALTHY]: null,
|
||||
[STATES.WARNING]: 'Context is approaching the fracture zone — consider /gsd-thread to continue in a fresh window.',
|
||||
[STATES.CRITICAL]: 'Reasoning quality may degrade past 70% utilization (fracture point). Run /gsd-thread now to preserve output quality.',
|
||||
};
|
||||
let classified;
|
||||
try {
|
||||
classified = classifyContextUtilization(Number(opts['tokens-used']), Number(opts['context-window']));
|
||||
} catch (e) {
|
||||
const flag = /tokensUsed/.test(e.message) ? '--tokens-used' : '--context-window';
|
||||
error(`${flag} must be a non-negative integer (window > 0), got the values supplied`);
|
||||
return;
|
||||
}
|
||||
const result = { ...classified, recommendation: RECOMMENDATIONS[classified.state] };
|
||||
if (args.includes('--json')) {
|
||||
output(result, raw);
|
||||
} else {
|
||||
const lines = [`Context utilization: ${result.percent}% (${result.state})`];
|
||||
if (result.recommendation) lines.push(result.recommendation);
|
||||
output(result, true, lines.join('\n'));
|
||||
}
|
||||
} else {
|
||||
error(`Unknown validate subcommand. Available: ${VALIDATE_SUBCOMMANDS.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
routeValidateCommand,
|
||||
};
|
||||
34
get-shit-done/bin/lib/verify-command-router.cjs
Normal file
34
get-shit-done/bin/lib/verify-command-router.cjs
Normal file
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
const { VERIFY_SUBCOMMANDS } = require('./command-aliases.generated.cjs');
|
||||
|
||||
function routeVerifyCommand({ verify, args, cwd, raw, error }) {
|
||||
const subcommand = args[1];
|
||||
|
||||
if (subcommand === 'plan-structure') {
|
||||
verify.cmdVerifyPlanStructure(cwd, args[2], raw);
|
||||
} else if (subcommand === 'phase-completeness') {
|
||||
verify.cmdVerifyPhaseCompleteness(cwd, args[2], raw);
|
||||
} else if (subcommand === 'references') {
|
||||
verify.cmdVerifyReferences(cwd, args[2], raw);
|
||||
} else if (subcommand === 'commits') {
|
||||
verify.cmdVerifyCommits(cwd, args.slice(2), raw);
|
||||
} else if (subcommand === 'artifacts') {
|
||||
verify.cmdVerifyArtifacts(cwd, args[2], raw);
|
||||
} else if (subcommand === 'key-links') {
|
||||
verify.cmdVerifyKeyLinks(cwd, args[2], raw);
|
||||
} else if (subcommand === 'schema-drift') {
|
||||
const rest = args.slice(2);
|
||||
const skipFlag = rest.includes('--skip');
|
||||
const phaseArg = rest.find((arg) => !arg.startsWith('-'));
|
||||
verify.cmdVerifySchemaDrift(cwd, phaseArg, skipFlag, raw);
|
||||
} else if (subcommand === 'codebase-drift') {
|
||||
verify.cmdVerifyCodebaseDrift(cwd, raw);
|
||||
} else {
|
||||
error(`Unknown verify subcommand. Available: ${VERIFY_SUBCOMMANDS.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
routeVerifyCommand,
|
||||
};
|
||||
87
sdk/scripts/check-command-aliases-fresh.mjs
Normal file
87
sdk/scripts/check-command-aliases-fresh.mjs
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env node
|
||||
import { createRequire } from 'node:module';
|
||||
import { resolve, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const {
|
||||
STATE_COMMAND_MANIFEST,
|
||||
} = await import('../dist/query/command-manifest.state.js');
|
||||
const {
|
||||
VERIFY_COMMAND_MANIFEST,
|
||||
} = await import('../dist/query/command-manifest.verify.js');
|
||||
const {
|
||||
INIT_COMMAND_MANIFEST,
|
||||
} = await import('../dist/query/command-manifest.init.js');
|
||||
const {
|
||||
PHASE_COMMAND_MANIFEST,
|
||||
} = await import('../dist/query/command-manifest.phase.js');
|
||||
const {
|
||||
PHASES_COMMAND_MANIFEST,
|
||||
} = await import('../dist/query/command-manifest.phases.js');
|
||||
const {
|
||||
VALIDATE_COMMAND_MANIFEST,
|
||||
} = await import('../dist/query/command-manifest.validate.js');
|
||||
const {
|
||||
ROADMAP_COMMAND_MANIFEST,
|
||||
} = await import('../dist/query/command-manifest.roadmap.js');
|
||||
|
||||
const {
|
||||
STATE_COMMAND_ALIASES,
|
||||
VERIFY_COMMAND_ALIASES,
|
||||
INIT_COMMAND_ALIASES,
|
||||
PHASE_COMMAND_ALIASES,
|
||||
PHASES_COMMAND_ALIASES,
|
||||
VALIDATE_COMMAND_ALIASES,
|
||||
ROADMAP_COMMAND_ALIASES,
|
||||
} = await import('../dist/query/command-aliases.generated.js');
|
||||
|
||||
const cjsAliases = require(resolve(here, '..', '..', 'get-shit-done', 'bin', 'lib', 'command-aliases.generated.cjs'));
|
||||
|
||||
function toAliasEntries(manifest, family) {
|
||||
const prefix = `${family}.`;
|
||||
return manifest.map((entry) => ({
|
||||
canonical: entry.canonical,
|
||||
aliases: [...entry.aliases],
|
||||
subcommand: entry.canonical.slice(prefix.length),
|
||||
mutation: entry.mutation,
|
||||
}));
|
||||
}
|
||||
|
||||
function assertEqual(label, actual, expected) {
|
||||
const a = JSON.stringify(actual);
|
||||
const e = JSON.stringify(expected);
|
||||
if (a !== e) {
|
||||
throw new Error(
|
||||
`${label} drift detected. Regenerate command alias artifacts and commit them.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const expectedState = toAliasEntries(STATE_COMMAND_MANIFEST, 'state');
|
||||
const expectedVerify = toAliasEntries(VERIFY_COMMAND_MANIFEST, 'verify');
|
||||
const expectedInit = toAliasEntries(INIT_COMMAND_MANIFEST, 'init');
|
||||
const expectedPhase = toAliasEntries(PHASE_COMMAND_MANIFEST, 'phase');
|
||||
const expectedPhases = toAliasEntries(PHASES_COMMAND_MANIFEST, 'phases');
|
||||
const expectedValidate = toAliasEntries(VALIDATE_COMMAND_MANIFEST, 'validate');
|
||||
const expectedRoadmap = toAliasEntries(ROADMAP_COMMAND_MANIFEST, 'roadmap');
|
||||
|
||||
assertEqual('TS STATE_COMMAND_ALIASES', STATE_COMMAND_ALIASES, expectedState);
|
||||
assertEqual('TS VERIFY_COMMAND_ALIASES', VERIFY_COMMAND_ALIASES, expectedVerify);
|
||||
assertEqual('TS INIT_COMMAND_ALIASES', INIT_COMMAND_ALIASES, expectedInit);
|
||||
assertEqual('TS PHASE_COMMAND_ALIASES', PHASE_COMMAND_ALIASES, expectedPhase);
|
||||
assertEqual('TS PHASES_COMMAND_ALIASES', PHASES_COMMAND_ALIASES, expectedPhases);
|
||||
assertEqual('TS VALIDATE_COMMAND_ALIASES', VALIDATE_COMMAND_ALIASES, expectedValidate);
|
||||
assertEqual('TS ROADMAP_COMMAND_ALIASES', ROADMAP_COMMAND_ALIASES, expectedRoadmap);
|
||||
|
||||
assertEqual('CJS STATE_COMMAND_ALIASES', cjsAliases.STATE_COMMAND_ALIASES, expectedState);
|
||||
assertEqual('CJS VERIFY_COMMAND_ALIASES', cjsAliases.VERIFY_COMMAND_ALIASES, expectedVerify);
|
||||
assertEqual('CJS INIT_COMMAND_ALIASES', cjsAliases.INIT_COMMAND_ALIASES, expectedInit);
|
||||
assertEqual('CJS PHASE_COMMAND_ALIASES', cjsAliases.PHASE_COMMAND_ALIASES, expectedPhase);
|
||||
assertEqual('CJS PHASES_COMMAND_ALIASES', cjsAliases.PHASES_COMMAND_ALIASES, expectedPhases);
|
||||
assertEqual('CJS VALIDATE_COMMAND_ALIASES', cjsAliases.VALIDATE_COMMAND_ALIASES, expectedValidate);
|
||||
assertEqual('CJS ROADMAP_COMMAND_ALIASES', cjsAliases.ROADMAP_COMMAND_ALIASES, expectedRoadmap);
|
||||
|
||||
console.log('command alias artifacts are fresh');
|
||||
124
sdk/scripts/gen-command-aliases.ts
Normal file
124
sdk/scripts/gen-command-aliases.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Build-time alias generator skeleton for command-manifest-driven routing.
|
||||
*
|
||||
* This pilot commits generated artifacts directly; this script documents and
|
||||
* preserves the generation seam so future command families can be migrated
|
||||
* without hand-maintained alias duplication.
|
||||
*/
|
||||
|
||||
import { writeFile } from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import { STATE_COMMAND_MANIFEST } from '../src/query/command-manifest.state.js';
|
||||
import { VERIFY_COMMAND_MANIFEST } from '../src/query/command-manifest.verify.js';
|
||||
import { INIT_COMMAND_MANIFEST } from '../src/query/command-manifest.init.js';
|
||||
import { PHASE_COMMAND_MANIFEST } from '../src/query/command-manifest.phase.js';
|
||||
import { PHASES_COMMAND_MANIFEST } from '../src/query/command-manifest.phases.js';
|
||||
import { VALIDATE_COMMAND_MANIFEST } from '../src/query/command-manifest.validate.js';
|
||||
import { ROADMAP_COMMAND_MANIFEST } from '../src/query/command-manifest.roadmap.js';
|
||||
|
||||
function toSubcommand(canonical: string, family: 'state' | 'verify' | 'init' | 'phase' | 'phases' | 'validate' | 'roadmap'): string {
|
||||
const prefix = `${family}.`;
|
||||
return canonical.startsWith(prefix) ? canonical.slice(prefix.length) : canonical;
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const stateEntries = STATE_COMMAND_MANIFEST.map((entry) => ({
|
||||
canonical: entry.canonical,
|
||||
aliases: entry.aliases,
|
||||
subcommand: toSubcommand(entry.canonical, 'state'),
|
||||
mutation: entry.mutation,
|
||||
}));
|
||||
|
||||
const verifyEntries = VERIFY_COMMAND_MANIFEST.map((entry) => ({
|
||||
canonical: entry.canonical,
|
||||
aliases: entry.aliases,
|
||||
subcommand: toSubcommand(entry.canonical, 'verify'),
|
||||
mutation: entry.mutation,
|
||||
}));
|
||||
|
||||
const initEntries = INIT_COMMAND_MANIFEST.map((entry) => ({
|
||||
canonical: entry.canonical,
|
||||
aliases: entry.aliases,
|
||||
subcommand: toSubcommand(entry.canonical, 'init'),
|
||||
mutation: entry.mutation,
|
||||
}));
|
||||
|
||||
const phaseEntries = PHASE_COMMAND_MANIFEST.map((entry) => ({
|
||||
canonical: entry.canonical,
|
||||
aliases: entry.aliases,
|
||||
subcommand: toSubcommand(entry.canonical, 'phase'),
|
||||
mutation: entry.mutation,
|
||||
}));
|
||||
|
||||
const phasesEntries = PHASES_COMMAND_MANIFEST.map((entry) => ({
|
||||
canonical: entry.canonical,
|
||||
aliases: entry.aliases,
|
||||
subcommand: toSubcommand(entry.canonical, 'phases'),
|
||||
mutation: entry.mutation,
|
||||
}));
|
||||
|
||||
const validateEntries = VALIDATE_COMMAND_MANIFEST.map((entry) => ({
|
||||
canonical: entry.canonical,
|
||||
aliases: entry.aliases,
|
||||
subcommand: toSubcommand(entry.canonical, 'validate'),
|
||||
mutation: entry.mutation,
|
||||
}));
|
||||
|
||||
const roadmapEntries = ROADMAP_COMMAND_MANIFEST.map((entry) => ({
|
||||
canonical: entry.canonical,
|
||||
aliases: entry.aliases,
|
||||
subcommand: toSubcommand(entry.canonical, 'roadmap'),
|
||||
mutation: entry.mutation,
|
||||
}));
|
||||
|
||||
const outPath = fileURLToPath(new URL('../src/query/command-aliases.generated.ts', import.meta.url));
|
||||
const header = `/**\n * GENERATED FILE — command alias expansion for state.*, verify.*, init.*, phase.*, phases.*, validate.*, and roadmap.* pilots.\n * Source: sdk/src/query/command-manifest.{state,verify,init,phase,phases,validate,roadmap}.ts\n */\n\n`;
|
||||
const body = [
|
||||
`export const STATE_COMMAND_ALIASES = ${JSON.stringify(stateEntries, null, 2)} as const;`,
|
||||
'',
|
||||
`export const VERIFY_COMMAND_ALIASES = ${JSON.stringify(verifyEntries, null, 2)} as const;`,
|
||||
'',
|
||||
`export const INIT_COMMAND_ALIASES = ${JSON.stringify(initEntries, null, 2)} as const;`,
|
||||
'',
|
||||
`export const PHASE_COMMAND_ALIASES = ${JSON.stringify(phaseEntries, null, 2)} as const;`,
|
||||
'',
|
||||
`export const PHASES_COMMAND_ALIASES = ${JSON.stringify(phasesEntries, null, 2)} as const;`,
|
||||
'',
|
||||
`export const VALIDATE_COMMAND_ALIASES = ${JSON.stringify(validateEntries, null, 2)} as const;`,
|
||||
'',
|
||||
`export const ROADMAP_COMMAND_ALIASES = ${JSON.stringify(roadmapEntries, null, 2)} as const;`,
|
||||
'',
|
||||
'export const STATE_SUBCOMMANDS = new Set<string>(STATE_COMMAND_ALIASES.map((entry) => entry.subcommand));',
|
||||
'export const VERIFY_SUBCOMMANDS = new Set<string>(VERIFY_COMMAND_ALIASES.map((entry) => entry.subcommand));',
|
||||
'export const INIT_SUBCOMMANDS = new Set<string>(INIT_COMMAND_ALIASES.map((entry) => entry.subcommand));',
|
||||
'export const PHASE_SUBCOMMANDS = new Set<string>(PHASE_COMMAND_ALIASES.map((entry) => entry.subcommand));',
|
||||
'export const PHASES_SUBCOMMANDS = new Set<string>(PHASES_COMMAND_ALIASES.map((entry) => entry.subcommand));',
|
||||
'export const VALIDATE_SUBCOMMANDS = new Set<string>(VALIDATE_COMMAND_ALIASES.map((entry) => entry.subcommand));',
|
||||
'export const ROADMAP_SUBCOMMANDS = new Set<string>(ROADMAP_COMMAND_ALIASES.map((entry) => entry.subcommand));',
|
||||
'',
|
||||
'export const STATE_MUTATION_COMMANDS: readonly string[] = STATE_COMMAND_ALIASES',
|
||||
' .filter((entry) => entry.mutation)',
|
||||
' .flatMap((entry) => [entry.canonical, ...entry.aliases]);',
|
||||
'',
|
||||
'export const PHASE_MUTATION_COMMANDS: readonly string[] = PHASE_COMMAND_ALIASES',
|
||||
' .filter((entry) => entry.mutation)',
|
||||
' .flatMap((entry) => [entry.canonical, ...entry.aliases]);',
|
||||
'',
|
||||
'export const PHASES_MUTATION_COMMANDS: readonly string[] = PHASES_COMMAND_ALIASES',
|
||||
' .filter((entry) => entry.mutation)',
|
||||
' .flatMap((entry) => [entry.canonical, ...entry.aliases]);',
|
||||
'',
|
||||
'export const ROADMAP_MUTATION_COMMANDS: readonly string[] = ROADMAP_COMMAND_ALIASES',
|
||||
' .filter((entry) => entry.mutation)',
|
||||
' .flatMap((entry) => [entry.canonical, ...entry.aliases]);',
|
||||
'',
|
||||
].join('\n');
|
||||
await writeFile(outPath, header + body, 'utf-8');
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -10,6 +10,20 @@ This document records contracts for the typed query layer consumed by `gsd-sdk q
|
||||
- CJS `**summary-extract**` → SDK `**summary.extract**` / `**summary extract**` / `**history-digest**` (see `index.ts`).
|
||||
- CJS top-level `**scaffold <type> ...**` → SDK `**phase.scaffold**` / `**phase scaffold**` with the scaffold type as the first argument (no separate `scaffold` alias on the registry).
|
||||
|
||||
### Manifest-backed family ownership
|
||||
|
||||
These families are sourced from `command-manifest.*.ts` files and expanded into generated alias artifacts (`command-aliases.generated.ts` + CJS mirror):
|
||||
|
||||
- `state.*` → `command-manifest.state.ts`
|
||||
- `verify.*` → `command-manifest.verify.ts`
|
||||
- `init.*` → `command-manifest.init.ts`
|
||||
- `phase.*` → `command-manifest.phase.ts`
|
||||
- `phases.*` → `command-manifest.phases.ts`
|
||||
- `validate.*` → `command-manifest.validate.ts`
|
||||
- `roadmap.*` → `command-manifest.roadmap.ts`
|
||||
|
||||
CJS routing seams mirror these families with thin adapters (`state/verify/init/phase/phases/validate/roadmap-command-router.cjs`) so `gsd-tools.cjs` stays orchestration-only.
|
||||
|
||||
## `gsd-sdk query` routing
|
||||
|
||||
1. **`normalizeQueryCommand()`** (`normalize-query-command.ts`) — maps the first argv tokens to the same **command + subcommand** patterns as `gsd-tools` `runCommand()` where needed (e.g. `state json` → `state.json`, `init execute-phase 9` → `init.execute-phase` with args `['9']`, `scaffold …` → `phase.scaffold`). Re-exported from **`@gsd-build/sdk`** and **`createRegistry`’s module** (`sdk/src/query/index.ts`) so programmatic callers can mirror CLI tokenization without importing a deep path.
|
||||
|
||||
122
sdk/src/query/command-aliases.generated.ts
Normal file
122
sdk/src/query/command-aliases.generated.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* GENERATED FILE — command alias expansion for state.*, verify.*, init.*, phase.*, phases.*, validate.*, and roadmap.* pilots.
|
||||
* Source: sdk/src/query/command-manifest.{state,verify,init,phase,phases,validate,roadmap}.ts
|
||||
*/
|
||||
|
||||
export interface FamilyCommandAlias {
|
||||
canonical: string;
|
||||
aliases: string[];
|
||||
subcommand: string;
|
||||
mutation: boolean;
|
||||
}
|
||||
|
||||
export const STATE_COMMAND_ALIASES: readonly FamilyCommandAlias[] = [
|
||||
{ canonical: 'state.load', aliases: [], subcommand: 'load', mutation: false },
|
||||
{ canonical: 'state.json', aliases: ['state json'], subcommand: 'json', mutation: false },
|
||||
{ canonical: 'state.get', aliases: ['state get'], subcommand: 'get', mutation: false },
|
||||
{ canonical: 'state.update', aliases: ['state update'], subcommand: 'update', mutation: true },
|
||||
{ canonical: 'state.patch', aliases: ['state patch'], subcommand: 'patch', mutation: true },
|
||||
{ canonical: 'state.begin-phase', aliases: ['state begin-phase'], subcommand: 'begin-phase', mutation: true },
|
||||
{ canonical: 'state.advance-plan', aliases: ['state advance-plan'], subcommand: 'advance-plan', mutation: true },
|
||||
{ canonical: 'state.record-metric', aliases: ['state record-metric'], subcommand: 'record-metric', mutation: true },
|
||||
{ canonical: 'state.update-progress', aliases: ['state update-progress'], subcommand: 'update-progress', mutation: true },
|
||||
{ canonical: 'state.add-decision', aliases: ['state add-decision'], subcommand: 'add-decision', mutation: true },
|
||||
{ canonical: 'state.add-blocker', aliases: ['state add-blocker'], subcommand: 'add-blocker', mutation: true },
|
||||
{ canonical: 'state.resolve-blocker', aliases: ['state resolve-blocker'], subcommand: 'resolve-blocker', mutation: true },
|
||||
{ canonical: 'state.record-session', aliases: ['state record-session'], subcommand: 'record-session', mutation: true },
|
||||
{ canonical: 'state.signal-waiting', aliases: ['state signal-waiting'], subcommand: 'signal-waiting', mutation: true },
|
||||
{ canonical: 'state.signal-resume', aliases: ['state signal-resume'], subcommand: 'signal-resume', mutation: true },
|
||||
{ canonical: 'state.planned-phase', aliases: ['state planned-phase'], subcommand: 'planned-phase', mutation: true },
|
||||
{ canonical: 'state.validate', aliases: ['state validate'], subcommand: 'validate', mutation: false },
|
||||
{ canonical: 'state.sync', aliases: ['state sync'], subcommand: 'sync', mutation: true },
|
||||
{ canonical: 'state.prune', aliases: ['state prune'], subcommand: 'prune', mutation: true },
|
||||
{ canonical: 'state.milestone-switch', aliases: ['state milestone-switch'], subcommand: 'milestone-switch', mutation: true },
|
||||
{ canonical: 'state.add-roadmap-evolution', aliases: ['state add-roadmap-evolution'], subcommand: 'add-roadmap-evolution', mutation: true },
|
||||
] as const;
|
||||
|
||||
export const VERIFY_COMMAND_ALIASES: readonly FamilyCommandAlias[] = [
|
||||
{ canonical: 'verify.plan-structure', aliases: ['verify plan-structure'], subcommand: 'plan-structure', mutation: false },
|
||||
{ canonical: 'verify.phase-completeness', aliases: ['verify phase-completeness'], subcommand: 'phase-completeness', mutation: false },
|
||||
{ canonical: 'verify.references', aliases: ['verify references'], subcommand: 'references', mutation: false },
|
||||
{ canonical: 'verify.commits', aliases: ['verify commits'], subcommand: 'commits', mutation: false },
|
||||
{ canonical: 'verify.artifacts', aliases: ['verify artifacts'], subcommand: 'artifacts', mutation: false },
|
||||
{ canonical: 'verify.key-links', aliases: ['verify key-links'], subcommand: 'key-links', mutation: false },
|
||||
{ canonical: 'verify.schema-drift', aliases: ['verify schema-drift'], subcommand: 'schema-drift', mutation: false },
|
||||
{ canonical: 'verify.codebase-drift', aliases: ['verify codebase-drift'], subcommand: 'codebase-drift', mutation: false },
|
||||
] as const;
|
||||
|
||||
export const INIT_COMMAND_ALIASES: readonly FamilyCommandAlias[] = [
|
||||
{ canonical: 'init.execute-phase', aliases: ['init execute-phase'], subcommand: 'execute-phase', mutation: false },
|
||||
{ canonical: 'init.plan-phase', aliases: ['init plan-phase'], subcommand: 'plan-phase', mutation: false },
|
||||
{ canonical: 'init.new-project', aliases: ['init new-project'], subcommand: 'new-project', mutation: false },
|
||||
{ canonical: 'init.new-milestone', aliases: ['init new-milestone'], subcommand: 'new-milestone', mutation: false },
|
||||
{ canonical: 'init.quick', aliases: ['init quick'], subcommand: 'quick', mutation: false },
|
||||
{ canonical: 'init.ingest-docs', aliases: ['init ingest-docs'], subcommand: 'ingest-docs', mutation: false },
|
||||
{ canonical: 'init.resume', aliases: ['init resume'], subcommand: 'resume', mutation: false },
|
||||
{ canonical: 'init.verify-work', aliases: ['init verify-work'], subcommand: 'verify-work', mutation: false },
|
||||
{ canonical: 'init.phase-op', aliases: ['init phase-op'], subcommand: 'phase-op', mutation: false },
|
||||
{ canonical: 'init.todos', aliases: ['init todos'], subcommand: 'todos', mutation: false },
|
||||
{ canonical: 'init.milestone-op', aliases: ['init milestone-op'], subcommand: 'milestone-op', mutation: false },
|
||||
{ canonical: 'init.map-codebase', aliases: ['init map-codebase'], subcommand: 'map-codebase', mutation: false },
|
||||
{ canonical: 'init.progress', aliases: ['init progress'], subcommand: 'progress', mutation: false },
|
||||
{ canonical: 'init.manager', aliases: ['init manager'], subcommand: 'manager', mutation: false },
|
||||
{ canonical: 'init.new-workspace', aliases: ['init new-workspace'], subcommand: 'new-workspace', mutation: false },
|
||||
{ canonical: 'init.list-workspaces', aliases: ['init list-workspaces'], subcommand: 'list-workspaces', mutation: false },
|
||||
{ canonical: 'init.remove-workspace', aliases: ['init remove-workspace'], subcommand: 'remove-workspace', mutation: false },
|
||||
] as const;
|
||||
|
||||
export const PHASE_COMMAND_ALIASES: readonly FamilyCommandAlias[] = [
|
||||
{ canonical: 'phase.list-plans', aliases: ['phase list-plans'], subcommand: 'list-plans', mutation: false },
|
||||
{ canonical: 'phase.list-artifacts', aliases: ['phase list-artifacts'], subcommand: 'list-artifacts', mutation: false },
|
||||
{ canonical: 'phase.next-decimal', aliases: ['phase next-decimal'], subcommand: 'next-decimal', mutation: false },
|
||||
{ canonical: 'phase.add', aliases: ['phase add'], subcommand: 'add', mutation: true },
|
||||
{ canonical: 'phase.add-batch', aliases: ['phase add-batch'], subcommand: 'add-batch', mutation: true },
|
||||
{ canonical: 'phase.insert', aliases: ['phase insert'], subcommand: 'insert', mutation: true },
|
||||
{ canonical: 'phase.remove', aliases: ['phase remove'], subcommand: 'remove', mutation: true },
|
||||
{ canonical: 'phase.complete', aliases: ['phase complete'], subcommand: 'complete', mutation: true },
|
||||
{ canonical: 'phase.scaffold', aliases: ['phase scaffold'], subcommand: 'scaffold', mutation: true },
|
||||
] as const;
|
||||
|
||||
export const PHASES_COMMAND_ALIASES: readonly FamilyCommandAlias[] = [
|
||||
{ canonical: 'phases.list', aliases: ['phases list'], subcommand: 'list', mutation: false },
|
||||
{ canonical: 'phases.clear', aliases: ['phases clear'], subcommand: 'clear', mutation: true },
|
||||
{ canonical: 'phases.archive', aliases: ['phases archive'], subcommand: 'archive', mutation: true },
|
||||
] as const;
|
||||
|
||||
export const VALIDATE_COMMAND_ALIASES: readonly FamilyCommandAlias[] = [
|
||||
{ canonical: 'validate.consistency', aliases: ['validate consistency'], subcommand: 'consistency', mutation: false },
|
||||
{ canonical: 'validate.health', aliases: ['validate health'], subcommand: 'health', mutation: false },
|
||||
{ canonical: 'validate.agents', aliases: ['validate agents'], subcommand: 'agents', mutation: false },
|
||||
{ canonical: 'validate.context', aliases: ['validate context'], subcommand: 'context', mutation: false },
|
||||
] as const;
|
||||
|
||||
export const ROADMAP_COMMAND_ALIASES: readonly FamilyCommandAlias[] = [
|
||||
{ canonical: 'roadmap.analyze', aliases: ['roadmap analyze'], subcommand: 'analyze', mutation: false },
|
||||
{ canonical: 'roadmap.get-phase', aliases: ['roadmap get-phase'], subcommand: 'get-phase', mutation: false },
|
||||
{ canonical: 'roadmap.update-plan-progress', aliases: ['roadmap update-plan-progress'], subcommand: 'update-plan-progress', mutation: true },
|
||||
{ canonical: 'roadmap.annotate-dependencies', aliases: ['roadmap annotate-dependencies'], subcommand: 'annotate-dependencies', mutation: true },
|
||||
] as const;
|
||||
|
||||
export const STATE_SUBCOMMANDS = new Set<string>(STATE_COMMAND_ALIASES.map((entry) => entry.subcommand));
|
||||
export const VERIFY_SUBCOMMANDS = new Set<string>(VERIFY_COMMAND_ALIASES.map((entry) => entry.subcommand));
|
||||
export const INIT_SUBCOMMANDS = new Set<string>(INIT_COMMAND_ALIASES.map((entry) => entry.subcommand));
|
||||
export const PHASE_SUBCOMMANDS = new Set<string>(PHASE_COMMAND_ALIASES.map((entry) => entry.subcommand));
|
||||
export const PHASES_SUBCOMMANDS = new Set<string>(PHASES_COMMAND_ALIASES.map((entry) => entry.subcommand));
|
||||
export const VALIDATE_SUBCOMMANDS = new Set<string>(VALIDATE_COMMAND_ALIASES.map((entry) => entry.subcommand));
|
||||
export const ROADMAP_SUBCOMMANDS = new Set<string>(ROADMAP_COMMAND_ALIASES.map((entry) => entry.subcommand));
|
||||
|
||||
export const STATE_MUTATION_COMMANDS: readonly string[] = STATE_COMMAND_ALIASES
|
||||
.filter((entry) => entry.mutation)
|
||||
.flatMap((entry) => [entry.canonical, ...entry.aliases]);
|
||||
|
||||
export const PHASE_MUTATION_COMMANDS: readonly string[] = PHASE_COMMAND_ALIASES
|
||||
.filter((entry) => entry.mutation)
|
||||
.flatMap((entry) => [entry.canonical, ...entry.aliases]);
|
||||
|
||||
export const PHASES_MUTATION_COMMANDS: readonly string[] = PHASES_COMMAND_ALIASES
|
||||
.filter((entry) => entry.mutation)
|
||||
.flatMap((entry) => [entry.canonical, ...entry.aliases]);
|
||||
|
||||
export const ROADMAP_MUTATION_COMMANDS: readonly string[] = ROADMAP_COMMAND_ALIASES
|
||||
.filter((entry) => entry.mutation)
|
||||
.flatMap((entry) => [entry.canonical, ...entry.aliases]);
|
||||
24
sdk/src/query/command-manifest.init.ts
Normal file
24
sdk/src/query/command-manifest.init.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { CommandManifestEntry } from './command-manifest.types.js';
|
||||
|
||||
/**
|
||||
* Canonical init.* command manifest.
|
||||
*/
|
||||
export const INIT_COMMAND_MANIFEST: readonly CommandManifestEntry[] = [
|
||||
{ family: 'init', canonical: 'init.execute-phase', aliases: ['init execute-phase'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.plan-phase', aliases: ['init plan-phase'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.new-project', aliases: ['init new-project'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.new-milestone', aliases: ['init new-milestone'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.quick', aliases: ['init quick'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.ingest-docs', aliases: ['init ingest-docs'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.resume', aliases: ['init resume'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.verify-work', aliases: ['init verify-work'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.phase-op', aliases: ['init phase-op'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.todos', aliases: ['init todos'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.milestone-op', aliases: ['init milestone-op'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.map-codebase', aliases: ['init map-codebase'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.progress', aliases: ['init progress'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.manager', aliases: ['init manager'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.new-workspace', aliases: ['init new-workspace'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.list-workspaces', aliases: ['init list-workspaces'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'init', canonical: 'init.remove-workspace', aliases: ['init remove-workspace'], mutation: false, outputMode: 'json' },
|
||||
] as const;
|
||||
16
sdk/src/query/command-manifest.phase.ts
Normal file
16
sdk/src/query/command-manifest.phase.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { CommandManifestEntry } from './command-manifest.types.js';
|
||||
|
||||
/**
|
||||
* Canonical phase.* command manifest.
|
||||
*/
|
||||
export const PHASE_COMMAND_MANIFEST: readonly CommandManifestEntry[] = [
|
||||
{ family: 'phase', canonical: 'phase.list-plans', aliases: ['phase list-plans'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'phase', canonical: 'phase.list-artifacts', aliases: ['phase list-artifacts'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'phase', canonical: 'phase.next-decimal', aliases: ['phase next-decimal'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'phase', canonical: 'phase.add', aliases: ['phase add'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'phase', canonical: 'phase.add-batch', aliases: ['phase add-batch'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'phase', canonical: 'phase.insert', aliases: ['phase insert'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'phase', canonical: 'phase.remove', aliases: ['phase remove'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'phase', canonical: 'phase.complete', aliases: ['phase complete'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'phase', canonical: 'phase.scaffold', aliases: ['phase scaffold'], mutation: true, outputMode: 'json' },
|
||||
] as const;
|
||||
11
sdk/src/query/command-manifest.phases.ts
Normal file
11
sdk/src/query/command-manifest.phases.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { CommandManifestEntry } from './command-manifest.types.js';
|
||||
|
||||
/**
|
||||
* Canonical phases.* command manifest.
|
||||
* Note: `phases.archive` is SDK-only; CJS `gsd-tools phases` currently supports list/clear.
|
||||
*/
|
||||
export const PHASES_COMMAND_MANIFEST: readonly CommandManifestEntry[] = [
|
||||
{ family: 'phases', canonical: 'phases.list', aliases: ['phases list'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'phases', canonical: 'phases.clear', aliases: ['phases clear'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'phases', canonical: 'phases.archive', aliases: ['phases archive'], mutation: true, outputMode: 'json' },
|
||||
] as const;
|
||||
11
sdk/src/query/command-manifest.roadmap.ts
Normal file
11
sdk/src/query/command-manifest.roadmap.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { CommandManifestEntry } from './command-manifest.types.js';
|
||||
|
||||
/**
|
||||
* Canonical roadmap.* command manifest.
|
||||
*/
|
||||
export const ROADMAP_COMMAND_MANIFEST: readonly CommandManifestEntry[] = [
|
||||
{ family: 'roadmap', canonical: 'roadmap.analyze', aliases: ['roadmap analyze'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'roadmap', canonical: 'roadmap.get-phase', aliases: ['roadmap get-phase'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'roadmap', canonical: 'roadmap.update-plan-progress', aliases: ['roadmap update-plan-progress'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'roadmap', canonical: 'roadmap.annotate-dependencies', aliases: ['roadmap annotate-dependencies'], mutation: true, outputMode: 'json' },
|
||||
] as const;
|
||||
31
sdk/src/query/command-manifest.state.ts
Normal file
31
sdk/src/query/command-manifest.state.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { CommandManifestEntry } from './command-manifest.types.js';
|
||||
|
||||
/**
|
||||
* Canonical state.* command manifest.
|
||||
*
|
||||
* Source of truth for the state family seam. Adapters derive registry aliases,
|
||||
* mutation classification, and CJS subcommand routing metadata from this list.
|
||||
*/
|
||||
export const STATE_COMMAND_MANIFEST: readonly CommandManifestEntry[] = [
|
||||
{ family: 'state', canonical: 'state.load', aliases: [], mutation: false, outputMode: 'raw' },
|
||||
{ family: 'state', canonical: 'state.json', aliases: ['state json'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.get', aliases: ['state get'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.update', aliases: ['state update'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.patch', aliases: ['state patch'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.begin-phase', aliases: ['state begin-phase'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.advance-plan', aliases: ['state advance-plan'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.record-metric', aliases: ['state record-metric'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.update-progress', aliases: ['state update-progress'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.add-decision', aliases: ['state add-decision'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.add-blocker', aliases: ['state add-blocker'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.resolve-blocker', aliases: ['state resolve-blocker'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.record-session', aliases: ['state record-session'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.signal-waiting', aliases: ['state signal-waiting'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.signal-resume', aliases: ['state signal-resume'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.planned-phase', aliases: ['state planned-phase'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.validate', aliases: ['state validate'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.sync', aliases: ['state sync'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.prune', aliases: ['state prune'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.milestone-switch', aliases: ['state milestone-switch'], mutation: true, outputMode: 'json' },
|
||||
{ family: 'state', canonical: 'state.add-roadmap-evolution', aliases: ['state add-roadmap-evolution'], mutation: true, outputMode: 'json' },
|
||||
] as const;
|
||||
17
sdk/src/query/command-manifest.ts
Normal file
17
sdk/src/query/command-manifest.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { STATE_COMMAND_MANIFEST } from './command-manifest.state.js';
|
||||
import { VERIFY_COMMAND_MANIFEST } from './command-manifest.verify.js';
|
||||
import { INIT_COMMAND_MANIFEST } from './command-manifest.init.js';
|
||||
import { PHASE_COMMAND_MANIFEST } from './command-manifest.phase.js';
|
||||
import { PHASES_COMMAND_MANIFEST } from './command-manifest.phases.js';
|
||||
import { VALIDATE_COMMAND_MANIFEST } from './command-manifest.validate.js';
|
||||
import { ROADMAP_COMMAND_MANIFEST } from './command-manifest.roadmap.js';
|
||||
|
||||
export const COMMAND_MANIFEST = [
|
||||
...STATE_COMMAND_MANIFEST,
|
||||
...VERIFY_COMMAND_MANIFEST,
|
||||
...INIT_COMMAND_MANIFEST,
|
||||
...PHASE_COMMAND_MANIFEST,
|
||||
...PHASES_COMMAND_MANIFEST,
|
||||
...VALIDATE_COMMAND_MANIFEST,
|
||||
...ROADMAP_COMMAND_MANIFEST,
|
||||
] as const;
|
||||
11
sdk/src/query/command-manifest.types.ts
Normal file
11
sdk/src/query/command-manifest.types.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export type CommandFamily = 'state' | 'verify' | 'init' | 'phase' | 'phases' | 'validate' | 'roadmap';
|
||||
|
||||
export type OutputMode = 'json' | 'raw';
|
||||
|
||||
export interface CommandManifestEntry {
|
||||
family: CommandFamily;
|
||||
canonical: string;
|
||||
aliases: string[];
|
||||
mutation: boolean;
|
||||
outputMode: OutputMode;
|
||||
}
|
||||
11
sdk/src/query/command-manifest.validate.ts
Normal file
11
sdk/src/query/command-manifest.validate.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { CommandManifestEntry } from './command-manifest.types.js';
|
||||
|
||||
/**
|
||||
* Canonical validate.* command manifest.
|
||||
*/
|
||||
export const VALIDATE_COMMAND_MANIFEST: readonly CommandManifestEntry[] = [
|
||||
{ family: 'validate', canonical: 'validate.consistency', aliases: ['validate consistency'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'validate', canonical: 'validate.health', aliases: ['validate health'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'validate', canonical: 'validate.agents', aliases: ['validate agents'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'validate', canonical: 'validate.context', aliases: ['validate context'], mutation: false, outputMode: 'json' },
|
||||
] as const;
|
||||
15
sdk/src/query/command-manifest.verify.ts
Normal file
15
sdk/src/query/command-manifest.verify.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { CommandManifestEntry } from './command-manifest.types.js';
|
||||
|
||||
/**
|
||||
* Canonical verify.* command manifest.
|
||||
*/
|
||||
export const VERIFY_COMMAND_MANIFEST: readonly CommandManifestEntry[] = [
|
||||
{ family: 'verify', canonical: 'verify.plan-structure', aliases: ['verify plan-structure'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'verify', canonical: 'verify.phase-completeness', aliases: ['verify phase-completeness'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'verify', canonical: 'verify.references', aliases: ['verify references'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'verify', canonical: 'verify.commits', aliases: ['verify commits'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'verify', canonical: 'verify.artifacts', aliases: ['verify artifacts'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'verify', canonical: 'verify.key-links', aliases: ['verify key-links'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'verify', canonical: 'verify.schema-drift', aliases: ['verify schema-drift'], mutation: false, outputMode: 'json' },
|
||||
{ family: 'verify', canonical: 'verify.codebase-drift', aliases: ['verify codebase-drift'], mutation: false, outputMode: 'json' },
|
||||
] as const;
|
||||
118
sdk/src/query/command-seam-coverage.test.ts
Normal file
118
sdk/src/query/command-seam-coverage.test.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
import { createRegistry } from './index.js';
|
||||
import { STATE_COMMAND_MANIFEST } from './command-manifest.state.js';
|
||||
import { VERIFY_COMMAND_MANIFEST } from './command-manifest.verify.js';
|
||||
import { INIT_COMMAND_MANIFEST } from './command-manifest.init.js';
|
||||
import { PHASE_COMMAND_MANIFEST } from './command-manifest.phase.js';
|
||||
import { PHASES_COMMAND_MANIFEST } from './command-manifest.phases.js';
|
||||
import { VALIDATE_COMMAND_MANIFEST } from './command-manifest.validate.js';
|
||||
import { ROADMAP_COMMAND_MANIFEST } from './command-manifest.roadmap.js';
|
||||
import {
|
||||
STATE_COMMAND_ALIASES,
|
||||
VERIFY_COMMAND_ALIASES,
|
||||
INIT_COMMAND_ALIASES,
|
||||
PHASE_COMMAND_ALIASES,
|
||||
PHASES_COMMAND_ALIASES,
|
||||
VALIDATE_COMMAND_ALIASES,
|
||||
ROADMAP_COMMAND_ALIASES,
|
||||
} from './command-aliases.generated.js';
|
||||
|
||||
function subcommandFor(canonical: string, family: 'state' | 'verify' | 'init' | 'phase' | 'phases' | 'validate' | 'roadmap'): string {
|
||||
return canonical.slice(`${family}.`.length);
|
||||
}
|
||||
|
||||
describe('command seam coverage (manifest -> generated -> adapters)', () => {
|
||||
it('state/verify/init/phase/phases/validate/roadmap manifest canonicals are present in generated alias artifacts', () => {
|
||||
const generated = new Map<string, { aliases: string[]; subcommand: string; mutation: boolean }>();
|
||||
for (const entry of [...STATE_COMMAND_ALIASES, ...VERIFY_COMMAND_ALIASES, ...INIT_COMMAND_ALIASES, ...PHASE_COMMAND_ALIASES, ...PHASES_COMMAND_ALIASES, ...VALIDATE_COMMAND_ALIASES, ...ROADMAP_COMMAND_ALIASES]) {
|
||||
generated.set(entry.canonical, { aliases: [...entry.aliases], subcommand: entry.subcommand, mutation: !!entry.mutation });
|
||||
}
|
||||
|
||||
for (const entry of STATE_COMMAND_MANIFEST) {
|
||||
const g = generated.get(entry.canonical);
|
||||
expect(g, `missing generated canonical ${entry.canonical}`).toBeTruthy();
|
||||
expect(g?.subcommand).toBe(subcommandFor(entry.canonical, 'state'));
|
||||
expect(g?.aliases ?? []).toEqual(entry.aliases);
|
||||
expect(g?.mutation).toBe(entry.mutation);
|
||||
}
|
||||
|
||||
for (const entry of VERIFY_COMMAND_MANIFEST) {
|
||||
const g = generated.get(entry.canonical);
|
||||
expect(g, `missing generated canonical ${entry.canonical}`).toBeTruthy();
|
||||
expect(g?.subcommand).toBe(subcommandFor(entry.canonical, 'verify'));
|
||||
expect(g?.aliases ?? []).toEqual(entry.aliases);
|
||||
expect(g?.mutation).toBe(entry.mutation);
|
||||
}
|
||||
|
||||
for (const entry of INIT_COMMAND_MANIFEST) {
|
||||
const g = generated.get(entry.canonical);
|
||||
expect(g, `missing generated canonical ${entry.canonical}`).toBeTruthy();
|
||||
expect(g?.subcommand).toBe(subcommandFor(entry.canonical, 'init'));
|
||||
expect(g?.aliases ?? []).toEqual(entry.aliases);
|
||||
expect(g?.mutation).toBe(entry.mutation);
|
||||
}
|
||||
|
||||
for (const entry of PHASE_COMMAND_MANIFEST) {
|
||||
const g = generated.get(entry.canonical);
|
||||
expect(g, `missing generated canonical ${entry.canonical}`).toBeTruthy();
|
||||
expect(g?.subcommand).toBe(subcommandFor(entry.canonical, 'phase'));
|
||||
expect(g?.aliases ?? []).toEqual(entry.aliases);
|
||||
expect(g?.mutation).toBe(entry.mutation);
|
||||
}
|
||||
|
||||
for (const entry of PHASES_COMMAND_MANIFEST) {
|
||||
const g = generated.get(entry.canonical);
|
||||
expect(g, `missing generated canonical ${entry.canonical}`).toBeTruthy();
|
||||
expect(g?.subcommand).toBe(subcommandFor(entry.canonical, 'phases'));
|
||||
expect(g?.aliases ?? []).toEqual(entry.aliases);
|
||||
expect(g?.mutation).toBe(entry.mutation);
|
||||
}
|
||||
|
||||
for (const entry of VALIDATE_COMMAND_MANIFEST) {
|
||||
const g = generated.get(entry.canonical);
|
||||
expect(g, `missing generated canonical ${entry.canonical}`).toBeTruthy();
|
||||
expect(g?.subcommand).toBe(subcommandFor(entry.canonical, 'validate'));
|
||||
expect(g?.aliases ?? []).toEqual(entry.aliases);
|
||||
expect(g?.mutation).toBe(entry.mutation);
|
||||
}
|
||||
|
||||
for (const entry of ROADMAP_COMMAND_MANIFEST) {
|
||||
const g = generated.get(entry.canonical);
|
||||
expect(g, `missing generated canonical ${entry.canonical}`).toBeTruthy();
|
||||
expect(g?.subcommand).toBe(subcommandFor(entry.canonical, 'roadmap'));
|
||||
expect(g?.aliases ?? []).toEqual(entry.aliases);
|
||||
expect(g?.mutation).toBe(entry.mutation);
|
||||
}
|
||||
});
|
||||
|
||||
it('registry has every canonical + alias for migrated families', () => {
|
||||
const registry = createRegistry();
|
||||
for (const entry of [...STATE_COMMAND_ALIASES, ...VERIFY_COMMAND_ALIASES, ...INIT_COMMAND_ALIASES, ...PHASE_COMMAND_ALIASES, ...PHASES_COMMAND_ALIASES, ...VALIDATE_COMMAND_ALIASES, ...ROADMAP_COMMAND_ALIASES]) {
|
||||
expect(registry.has(entry.canonical), `missing registry canonical ${entry.canonical}`).toBe(true);
|
||||
for (const alias of entry.aliases) {
|
||||
expect(registry.has(alias), `missing registry alias ${alias}`).toBe(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('CJS seam adapters export expected router functions', () => {
|
||||
const require = createRequire(import.meta.url);
|
||||
const stateRouter = require('../../../get-shit-done/bin/lib/state-command-router.cjs');
|
||||
const verifyRouter = require('../../../get-shit-done/bin/lib/verify-command-router.cjs');
|
||||
const initRouter = require('../../../get-shit-done/bin/lib/init-command-router.cjs');
|
||||
const phaseRouter = require('../../../get-shit-done/bin/lib/phase-command-router.cjs');
|
||||
const phasesRouter = require('../../../get-shit-done/bin/lib/phases-command-router.cjs');
|
||||
const validateRouter = require('../../../get-shit-done/bin/lib/validate-command-router.cjs');
|
||||
const roadmapRouter = require('../../../get-shit-done/bin/lib/roadmap-command-router.cjs');
|
||||
|
||||
expect(typeof stateRouter.routeStateCommand).toBe('function');
|
||||
expect(typeof verifyRouter.routeVerifyCommand).toBe('function');
|
||||
expect(typeof initRouter.routeInitCommand).toBe('function');
|
||||
expect(typeof phaseRouter.routePhaseCommand).toBe('function');
|
||||
expect(typeof phasesRouter.routePhasesCommand).toBe('function');
|
||||
expect(typeof validateRouter.routeValidateCommand).toBe('function');
|
||||
expect(typeof roadmapRouter.routeRoadmapCommand).toBe('function');
|
||||
});
|
||||
});
|
||||
@@ -20,6 +20,19 @@ import { frontmatterGet } from './frontmatter.js';
|
||||
import { configGet, configPath, resolveModel } from './config-query.js';
|
||||
import { stateJson, stateGet, stateSnapshot } from './state.js';
|
||||
import { stateProjectLoad } from './state-project-load.js';
|
||||
import {
|
||||
STATE_COMMAND_ALIASES,
|
||||
STATE_MUTATION_COMMANDS,
|
||||
VERIFY_COMMAND_ALIASES,
|
||||
INIT_COMMAND_ALIASES,
|
||||
PHASE_COMMAND_ALIASES,
|
||||
PHASE_MUTATION_COMMANDS,
|
||||
PHASES_COMMAND_ALIASES,
|
||||
PHASES_MUTATION_COMMANDS,
|
||||
VALIDATE_COMMAND_ALIASES,
|
||||
ROADMAP_COMMAND_ALIASES,
|
||||
ROADMAP_MUTATION_COMMANDS,
|
||||
} from './command-aliases.generated.js';
|
||||
import { findPhase, phasePlanIndex } from './phase.js';
|
||||
import { phaseListPlans, phaseListArtifacts } from './phase-list-queries.js';
|
||||
import { planTaskStructure } from './plan-task-structure.js';
|
||||
@@ -126,28 +139,14 @@ export { normalizeQueryCommand } from './normalize-query-command.js';
|
||||
* (they emit JSON for workflows; agents perform writes).
|
||||
*/
|
||||
export const QUERY_MUTATION_COMMANDS = new Set<string>([
|
||||
'state.update', 'state.patch', 'state.begin-phase', 'state.advance-plan',
|
||||
'state.record-metric', 'state.update-progress', 'state.add-decision',
|
||||
'state.add-blocker', 'state.resolve-blocker', 'state.record-session',
|
||||
'state.planned-phase', 'state planned-phase',
|
||||
'state.signal-waiting', 'state signal-waiting',
|
||||
'state.signal-resume', 'state signal-resume',
|
||||
'state.sync', 'state sync',
|
||||
'state.prune', 'state prune',
|
||||
'state.milestone-switch', 'state milestone-switch',
|
||||
'state.add-roadmap-evolution', 'state add-roadmap-evolution',
|
||||
...STATE_MUTATION_COMMANDS,
|
||||
'frontmatter.set', 'frontmatter.merge', 'frontmatter.validate', 'frontmatter validate',
|
||||
'config-set', 'config-set-model-profile', 'config-new-project', 'config-ensure-section',
|
||||
'commit', 'check-commit', 'commit-to-subrepo',
|
||||
'template.fill', 'template.select', 'template select',
|
||||
'validate.health', 'validate health',
|
||||
'validate.context', 'validate context',
|
||||
'phase.add', 'phase.add-batch', 'phase.insert', 'phase.remove', 'phase.complete',
|
||||
'phase.scaffold', 'phases.clear', 'phases.archive',
|
||||
'phase add', 'phase add-batch', 'phase insert', 'phase remove', 'phase complete',
|
||||
'phase scaffold', 'phases clear', 'phases archive',
|
||||
'roadmap.update-plan-progress', 'roadmap update-plan-progress',
|
||||
'roadmap.annotate-dependencies', 'roadmap annotate-dependencies',
|
||||
...PHASE_MUTATION_COMMANDS,
|
||||
...PHASES_MUTATION_COMMANDS,
|
||||
...ROADMAP_MUTATION_COMMANDS,
|
||||
'requirements.mark-complete', 'requirements mark-complete',
|
||||
'todo.complete', 'todo complete',
|
||||
'milestone.complete', 'milestone complete',
|
||||
@@ -284,22 +283,62 @@ export function createRegistry(
|
||||
registry.register('config-get', configGet);
|
||||
registry.register('config-path', configPath);
|
||||
registry.register('resolve-model', resolveModel);
|
||||
registry.register('state.load', stateProjectLoad);
|
||||
registry.register('state.json', stateJson);
|
||||
registry.register('state.get', stateGet);
|
||||
const stateHandlers: Record<string, QueryHandler> = {
|
||||
'state.load': stateProjectLoad,
|
||||
'state.json': stateJson,
|
||||
'state.get': stateGet,
|
||||
'state.update': stateUpdate,
|
||||
'state.patch': statePatch,
|
||||
'state.begin-phase': stateBeginPhase,
|
||||
'state.advance-plan': stateAdvancePlan,
|
||||
'state.record-metric': stateRecordMetric,
|
||||
'state.update-progress': stateUpdateProgress,
|
||||
'state.add-decision': stateAddDecision,
|
||||
'state.add-blocker': stateAddBlocker,
|
||||
'state.resolve-blocker': stateResolveBlocker,
|
||||
'state.record-session': stateRecordSession,
|
||||
'state.signal-waiting': stateSignalWaiting,
|
||||
'state.signal-resume': stateSignalResume,
|
||||
'state.planned-phase': statePlannedPhase,
|
||||
'state.validate': stateValidate,
|
||||
'state.sync': stateSync,
|
||||
'state.prune': statePrune,
|
||||
'state.milestone-switch': stateMilestoneSwitch,
|
||||
'state.add-roadmap-evolution': stateAddRoadmapEvolution,
|
||||
};
|
||||
|
||||
for (const entry of STATE_COMMAND_ALIASES) {
|
||||
const handler = stateHandlers[entry.canonical];
|
||||
if (!handler) continue;
|
||||
registry.register(entry.canonical, handler);
|
||||
for (const alias of entry.aliases) {
|
||||
registry.register(alias, handler);
|
||||
}
|
||||
}
|
||||
|
||||
registry.register('state-snapshot', stateSnapshot);
|
||||
registry.register('find-phase', findPhase);
|
||||
registry.register('phase-plan-index', phasePlanIndex);
|
||||
registry.register('phase.list-plans', phaseListPlans);
|
||||
registry.register('phase list-plans', phaseListPlans);
|
||||
registry.register('phase.list-artifacts', phaseListArtifacts);
|
||||
registry.register('phase list-artifacts', phaseListArtifacts);
|
||||
registry.register('plan.task-structure', planTaskStructure);
|
||||
registry.register('plan task-structure', planTaskStructure);
|
||||
registry.register('requirements.extract-from-plans', requirementsExtractFromPlans);
|
||||
registry.register('requirements extract-from-plans', requirementsExtractFromPlans);
|
||||
registry.register('roadmap.analyze', roadmapAnalyze);
|
||||
registry.register('roadmap.get-phase', roadmapGetPhase);
|
||||
const roadmapHandlers: Record<string, QueryHandler> = {
|
||||
'roadmap.analyze': roadmapAnalyze,
|
||||
'roadmap.get-phase': roadmapGetPhase,
|
||||
'roadmap.update-plan-progress': roadmapUpdatePlanProgress,
|
||||
'roadmap.annotate-dependencies': roadmapAnnotateDependencies,
|
||||
};
|
||||
|
||||
for (const entry of ROADMAP_COMMAND_ALIASES) {
|
||||
const handler = roadmapHandlers[entry.canonical];
|
||||
if (!handler) continue;
|
||||
registry.register(entry.canonical, handler);
|
||||
for (const alias of entry.aliases) {
|
||||
registry.register(alias, handler);
|
||||
}
|
||||
}
|
||||
|
||||
registry.register('progress', progressJson);
|
||||
registry.register('progress.json', progressJson);
|
||||
|
||||
@@ -309,32 +348,6 @@ export function createRegistry(
|
||||
registry.register('frontmatter.validate', frontmatterValidate);
|
||||
registry.register('frontmatter validate', frontmatterValidate);
|
||||
|
||||
// State mutation handlers
|
||||
registry.register('state.update', stateUpdate);
|
||||
registry.register('state.patch', statePatch);
|
||||
registry.register('state.begin-phase', stateBeginPhase);
|
||||
registry.register('state.advance-plan', stateAdvancePlan);
|
||||
registry.register('state.record-metric', stateRecordMetric);
|
||||
registry.register('state.update-progress', stateUpdateProgress);
|
||||
registry.register('state.add-decision', stateAddDecision);
|
||||
registry.register('state.add-blocker', stateAddBlocker);
|
||||
registry.register('state.resolve-blocker', stateResolveBlocker);
|
||||
registry.register('state.record-session', stateRecordSession);
|
||||
registry.register('state.signal-waiting', stateSignalWaiting);
|
||||
registry.register('state.signal-resume', stateSignalResume);
|
||||
registry.register('state.validate', stateValidate);
|
||||
registry.register('state.sync', stateSync);
|
||||
registry.register('state.prune', statePrune);
|
||||
registry.register('state.milestone-switch', stateMilestoneSwitch);
|
||||
registry.register('state.add-roadmap-evolution', stateAddRoadmapEvolution);
|
||||
registry.register('state milestone-switch', stateMilestoneSwitch);
|
||||
registry.register('state add-roadmap-evolution', stateAddRoadmapEvolution);
|
||||
registry.register('state signal-waiting', stateSignalWaiting);
|
||||
registry.register('state signal-resume', stateSignalResume);
|
||||
registry.register('state validate', stateValidate);
|
||||
registry.register('state sync', stateSync);
|
||||
registry.register('state prune', statePrune);
|
||||
|
||||
// Config mutation handlers
|
||||
registry.register('config-set', configSet);
|
||||
registry.register('config-set-model-profile', configSetModelProfile);
|
||||
@@ -350,19 +363,26 @@ export function createRegistry(
|
||||
registry.register('template.select', templateSelect);
|
||||
registry.register('template select', templateSelect);
|
||||
|
||||
// Verification handlers
|
||||
registry.register('verify.plan-structure', verifyPlanStructure);
|
||||
registry.register('verify plan-structure', verifyPlanStructure);
|
||||
registry.register('verify.phase-completeness', verifyPhaseCompleteness);
|
||||
registry.register('verify phase-completeness', verifyPhaseCompleteness);
|
||||
registry.register('verify.artifacts', verifyArtifacts);
|
||||
registry.register('verify artifacts', verifyArtifacts);
|
||||
registry.register('verify.key-links', verifyKeyLinks);
|
||||
registry.register('verify key-links', verifyKeyLinks);
|
||||
registry.register('verify.commits', verifyCommits);
|
||||
registry.register('verify commits', verifyCommits);
|
||||
registry.register('verify.references', verifyReferences);
|
||||
registry.register('verify references', verifyReferences);
|
||||
const verifyHandlers: Record<string, QueryHandler> = {
|
||||
'verify.plan-structure': verifyPlanStructure,
|
||||
'verify.phase-completeness': verifyPhaseCompleteness,
|
||||
'verify.references': verifyReferences,
|
||||
'verify.commits': verifyCommits,
|
||||
'verify.artifacts': verifyArtifacts,
|
||||
'verify.key-links': verifyKeyLinks,
|
||||
'verify.schema-drift': verifySchemaDrift,
|
||||
'verify.codebase-drift': verifyCodebaseDrift,
|
||||
};
|
||||
|
||||
for (const entry of VERIFY_COMMAND_ALIASES) {
|
||||
const handler = verifyHandlers[entry.canonical];
|
||||
if (!handler) continue;
|
||||
registry.register(entry.canonical, handler);
|
||||
for (const alias of entry.aliases) {
|
||||
registry.register(alias, handler);
|
||||
}
|
||||
}
|
||||
|
||||
registry.register('verify-summary', verifySummary);
|
||||
registry.register('verify.summary', verifySummary);
|
||||
registry.register('verify summary', verifySummary);
|
||||
@@ -377,14 +397,21 @@ export function createRegistry(
|
||||
registry.register('check decision-coverage-plan', checkDecisionCoveragePlan);
|
||||
registry.register('check.decision-coverage-verify', checkDecisionCoverageVerify);
|
||||
registry.register('check decision-coverage-verify', checkDecisionCoverageVerify);
|
||||
registry.register('validate.consistency', validateConsistency);
|
||||
registry.register('validate consistency', validateConsistency);
|
||||
registry.register('validate.health', validateHealth);
|
||||
registry.register('validate health', validateHealth);
|
||||
registry.register('validate.agents', validateAgents);
|
||||
registry.register('validate agents', validateAgents);
|
||||
registry.register('validate.context', validateContext);
|
||||
registry.register('validate context', validateContext);
|
||||
const validateHandlers: Record<string, QueryHandler> = {
|
||||
'validate.consistency': validateConsistency,
|
||||
'validate.health': validateHealth,
|
||||
'validate.agents': validateAgents,
|
||||
'validate.context': validateContext,
|
||||
};
|
||||
|
||||
for (const entry of VALIDATE_COMMAND_ALIASES) {
|
||||
const handler = validateHandlers[entry.canonical];
|
||||
if (!handler) continue;
|
||||
registry.register(entry.canonical, handler);
|
||||
for (const alias of entry.aliases) {
|
||||
registry.register(alias, handler);
|
||||
}
|
||||
}
|
||||
|
||||
// Decision routing (SDK-only — no `gsd-tools.cjs` mirror yet; see QUERY-HANDLERS.md)
|
||||
registry.register('check.config-gates', checkConfigGates);
|
||||
@@ -406,82 +433,75 @@ export function createRegistry(
|
||||
registry.register('check.ship-ready', checkShipReady);
|
||||
registry.register('check ship-ready', checkShipReady);
|
||||
|
||||
// Phase lifecycle handlers
|
||||
registry.register('phase.add', phaseAdd);
|
||||
registry.register('phase.add-batch', phaseAddBatch);
|
||||
registry.register('phase.insert', phaseInsert);
|
||||
registry.register('phase.remove', phaseRemove);
|
||||
registry.register('phase.complete', phaseComplete);
|
||||
registry.register('phase.scaffold', phaseScaffold);
|
||||
registry.register('phases.clear', phasesClear);
|
||||
registry.register('phases.archive', phasesArchive);
|
||||
registry.register('phases.list', phasesList);
|
||||
registry.register('phase.next-decimal', phaseNextDecimal);
|
||||
// Space-delimited aliases for CJS compatibility
|
||||
registry.register('phase add', phaseAdd);
|
||||
registry.register('phase add-batch', phaseAddBatch);
|
||||
registry.register('phase insert', phaseInsert);
|
||||
registry.register('phase remove', phaseRemove);
|
||||
registry.register('phase complete', phaseComplete);
|
||||
registry.register('phase scaffold', phaseScaffold);
|
||||
registry.register('phases clear', phasesClear);
|
||||
registry.register('phases archive', phasesArchive);
|
||||
registry.register('phases list', phasesList);
|
||||
registry.register('phase next-decimal', phaseNextDecimal);
|
||||
const phaseHandlers: Record<string, QueryHandler> = {
|
||||
'phase.list-plans': phaseListPlans,
|
||||
'phase.list-artifacts': phaseListArtifacts,
|
||||
'phase.add': phaseAdd,
|
||||
'phase.add-batch': phaseAddBatch,
|
||||
'phase.insert': phaseInsert,
|
||||
'phase.remove': phaseRemove,
|
||||
'phase.complete': phaseComplete,
|
||||
'phase.scaffold': phaseScaffold,
|
||||
'phase.next-decimal': phaseNextDecimal,
|
||||
};
|
||||
|
||||
// Init composition handlers
|
||||
registry.register('init.execute-phase', initExecutePhase);
|
||||
registry.register('init.plan-phase', initPlanPhase);
|
||||
registry.register('init.new-milestone', initNewMilestone);
|
||||
registry.register('init.quick', initQuick);
|
||||
registry.register('init.resume', initResume);
|
||||
registry.register('init.verify-work', initVerifyWork);
|
||||
registry.register('init.phase-op', initPhaseOp);
|
||||
registry.register('init.todos', initTodos);
|
||||
registry.register('init.milestone-op', initMilestoneOp);
|
||||
registry.register('init.map-codebase', initMapCodebase);
|
||||
registry.register('init.new-workspace', initNewWorkspace);
|
||||
registry.register('init.list-workspaces', initListWorkspaces);
|
||||
registry.register('init.remove-workspace', initRemoveWorkspace);
|
||||
registry.register('init.ingest-docs', initIngestDocs);
|
||||
// Space-delimited aliases for CJS compatibility
|
||||
registry.register('init execute-phase', initExecutePhase);
|
||||
registry.register('init plan-phase', initPlanPhase);
|
||||
registry.register('init new-milestone', initNewMilestone);
|
||||
registry.register('init quick', initQuick);
|
||||
registry.register('init resume', initResume);
|
||||
registry.register('init verify-work', initVerifyWork);
|
||||
registry.register('init phase-op', initPhaseOp);
|
||||
registry.register('init todos', initTodos);
|
||||
registry.register('init milestone-op', initMilestoneOp);
|
||||
registry.register('init map-codebase', initMapCodebase);
|
||||
registry.register('init new-workspace', initNewWorkspace);
|
||||
registry.register('init list-workspaces', initListWorkspaces);
|
||||
registry.register('init remove-workspace', initRemoveWorkspace);
|
||||
registry.register('init ingest-docs', initIngestDocs);
|
||||
for (const entry of PHASE_COMMAND_ALIASES) {
|
||||
const handler = phaseHandlers[entry.canonical];
|
||||
if (!handler) continue;
|
||||
registry.register(entry.canonical, handler);
|
||||
for (const alias of entry.aliases) {
|
||||
registry.register(alias, handler);
|
||||
}
|
||||
}
|
||||
|
||||
// Complex init handlers
|
||||
registry.register('init.new-project', initNewProject);
|
||||
registry.register('init.progress', initProgress);
|
||||
registry.register('init.manager', initManager);
|
||||
registry.register('init new-project', initNewProject);
|
||||
registry.register('init progress', initProgress);
|
||||
registry.register('init manager', initManager);
|
||||
const phasesHandlers: Record<string, QueryHandler> = {
|
||||
'phases.list': phasesList,
|
||||
'phases.clear': phasesClear,
|
||||
'phases.archive': phasesArchive,
|
||||
};
|
||||
|
||||
for (const entry of PHASES_COMMAND_ALIASES) {
|
||||
const handler = phasesHandlers[entry.canonical];
|
||||
if (!handler) continue;
|
||||
registry.register(entry.canonical, handler);
|
||||
for (const alias of entry.aliases) {
|
||||
registry.register(alias, handler);
|
||||
}
|
||||
}
|
||||
|
||||
const initHandlers: Record<string, QueryHandler> = {
|
||||
'init.execute-phase': initExecutePhase,
|
||||
'init.plan-phase': initPlanPhase,
|
||||
'init.new-project': initNewProject,
|
||||
'init.new-milestone': initNewMilestone,
|
||||
'init.quick': initQuick,
|
||||
'init.ingest-docs': initIngestDocs,
|
||||
'init.resume': initResume,
|
||||
'init.verify-work': initVerifyWork,
|
||||
'init.phase-op': initPhaseOp,
|
||||
'init.todos': initTodos,
|
||||
'init.milestone-op': initMilestoneOp,
|
||||
'init.map-codebase': initMapCodebase,
|
||||
'init.progress': initProgress,
|
||||
'init.manager': initManager,
|
||||
'init.new-workspace': initNewWorkspace,
|
||||
'init.list-workspaces': initListWorkspaces,
|
||||
'init.remove-workspace': initRemoveWorkspace,
|
||||
};
|
||||
|
||||
for (const entry of INIT_COMMAND_ALIASES) {
|
||||
const handler = initHandlers[entry.canonical];
|
||||
if (!handler) continue;
|
||||
registry.register(entry.canonical, handler);
|
||||
for (const alias of entry.aliases) {
|
||||
registry.register(alias, handler);
|
||||
}
|
||||
}
|
||||
|
||||
// Domain-specific handlers (fully implemented)
|
||||
registry.register('agent-skills', agentSkills);
|
||||
registry.register('roadmap.update-plan-progress', roadmapUpdatePlanProgress);
|
||||
registry.register('roadmap update-plan-progress', roadmapUpdatePlanProgress);
|
||||
registry.register('roadmap.annotate-dependencies', roadmapAnnotateDependencies);
|
||||
registry.register('roadmap annotate-dependencies', roadmapAnnotateDependencies);
|
||||
registry.register('requirements.mark-complete', requirementsMarkComplete);
|
||||
registry.register('requirements mark-complete', requirementsMarkComplete);
|
||||
registry.register('state.planned-phase', statePlannedPhase);
|
||||
registry.register('state planned-phase', statePlannedPhase);
|
||||
registry.register('verify.schema-drift', verifySchemaDrift);
|
||||
registry.register('verify schema-drift', verifySchemaDrift);
|
||||
registry.register('verify.codebase-drift', verifyCodebaseDrift);
|
||||
registry.register('verify codebase-drift', verifyCodebaseDrift);
|
||||
registry.register('todo.match-phase', todoMatchPhase);
|
||||
registry.register('todo match-phase', todoMatchPhase);
|
||||
registry.register('list-todos', listTodos);
|
||||
|
||||
@@ -7,15 +7,28 @@ describe('normalizeQueryCommand', () => {
|
||||
expect(normalizeQueryCommand('state', ['validate'])).toEqual(['state.validate', []]);
|
||||
});
|
||||
|
||||
it('merges verify known subcommands only', () => {
|
||||
expect(normalizeQueryCommand('verify', ['plan-structure', 'x.md'])).toEqual(['verify.plan-structure', ['x.md']]);
|
||||
expect(normalizeQueryCommand('verify', ['unknown-op'])).toEqual(['verify', ['unknown-op']]);
|
||||
});
|
||||
|
||||
it('maps bare state to state.load', () => {
|
||||
expect(normalizeQueryCommand('state', [])).toEqual(['state.load', []]);
|
||||
});
|
||||
|
||||
it('does not merge unknown state subcommands', () => {
|
||||
expect(normalizeQueryCommand('state', ['not-a-subcommand'])).toEqual(['state', ['not-a-subcommand']]);
|
||||
});
|
||||
|
||||
it('merges init workflows', () => {
|
||||
expect(normalizeQueryCommand('init', ['execute-phase', '9'])).toEqual(['init.execute-phase', ['9']]);
|
||||
expect(normalizeQueryCommand('init', ['new-project'])).toEqual(['init.new-project', []]);
|
||||
});
|
||||
|
||||
it('does not merge unknown init subcommands', () => {
|
||||
expect(normalizeQueryCommand('init', ['made-up-init-op', 'x'])).toEqual(['init', ['made-up-init-op', 'x']]);
|
||||
});
|
||||
|
||||
it('maps scaffold to phase.scaffold', () => {
|
||||
expect(normalizeQueryCommand('scaffold', ['phase-dir', '--phase', '1'])).toEqual([
|
||||
'phase.scaffold',
|
||||
@@ -33,7 +46,7 @@ describe('normalizeQueryCommand', () => {
|
||||
expect(normalizeQueryCommand('generate-slug', ['Hello'])).toEqual(['generate-slug', ['Hello']]);
|
||||
});
|
||||
|
||||
it('merges phase add-batch for future handler', () => {
|
||||
it('merges check/route helper commands', () => {
|
||||
expect(normalizeQueryCommand('check', ['config-gates', 'plan-phase'])).toEqual([
|
||||
'check.config-gates',
|
||||
['plan-phase'],
|
||||
@@ -41,10 +54,49 @@ describe('normalizeQueryCommand', () => {
|
||||
expect(normalizeQueryCommand('check', ['phase-ready', '3'])).toEqual(['check.phase-ready', ['3']]);
|
||||
expect(normalizeQueryCommand('check', ['auto-mode'])).toEqual(['check.auto-mode', []]);
|
||||
expect(normalizeQueryCommand('route', ['next-action'])).toEqual(['route.next-action', []]);
|
||||
});
|
||||
|
||||
it('merges known phase subcommands and preserves unknown ones', () => {
|
||||
expect(normalizeQueryCommand('phase', ['add-batch', '--descriptions', '[]'])).toEqual([
|
||||
'phase.add-batch',
|
||||
['--descriptions', '[]'],
|
||||
]);
|
||||
expect(normalizeQueryCommand('phase', ['made-up-phase-op', 'x'])).toEqual([
|
||||
'phase',
|
||||
['made-up-phase-op', 'x'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('merges known phases subcommands and preserves unknown ones', () => {
|
||||
expect(normalizeQueryCommand('phases', ['clear', 'v1.0'])).toEqual([
|
||||
'phases.clear',
|
||||
['v1.0'],
|
||||
]);
|
||||
expect(normalizeQueryCommand('phases', ['made-up-phases-op', 'x'])).toEqual([
|
||||
'phases',
|
||||
['made-up-phases-op', 'x'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('merges known validate subcommands and preserves unknown ones', () => {
|
||||
expect(normalizeQueryCommand('validate', ['consistency'])).toEqual([
|
||||
'validate.consistency',
|
||||
[],
|
||||
]);
|
||||
expect(normalizeQueryCommand('validate', ['made-up-validate-op', 'x'])).toEqual([
|
||||
'validate',
|
||||
['made-up-validate-op', 'x'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('merges known roadmap subcommands and preserves unknown ones', () => {
|
||||
expect(normalizeQueryCommand('roadmap', ['analyze'])).toEqual([
|
||||
'roadmap.analyze',
|
||||
[],
|
||||
]);
|
||||
expect(normalizeQueryCommand('roadmap', ['made-up-roadmap-op', 'x'])).toEqual([
|
||||
'roadmap',
|
||||
['made-up-roadmap-op', 'x'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,16 +7,23 @@
|
||||
* under `runCommand()` so two-token (and longer) invocations resolve to dotted registry names.
|
||||
*/
|
||||
|
||||
import {
|
||||
STATE_SUBCOMMANDS,
|
||||
VERIFY_SUBCOMMANDS,
|
||||
INIT_SUBCOMMANDS,
|
||||
PHASE_SUBCOMMANDS,
|
||||
PHASES_SUBCOMMANDS,
|
||||
VALIDATE_SUBCOMMANDS,
|
||||
ROADMAP_SUBCOMMANDS,
|
||||
} from './command-aliases.generated.js';
|
||||
|
||||
const MERGE_FIRST_WITH_SUBCOMMAND = new Set<string>([
|
||||
'state',
|
||||
'template',
|
||||
'frontmatter',
|
||||
'verify',
|
||||
'phase',
|
||||
'phases',
|
||||
'roadmap',
|
||||
'requirements',
|
||||
'validate',
|
||||
'init',
|
||||
'workstream',
|
||||
'intel',
|
||||
@@ -43,6 +50,62 @@ export function normalizeQueryCommand(command: string, args: string[]): [string,
|
||||
return ['state.load', []];
|
||||
}
|
||||
|
||||
if (command === 'state' && args.length > 0) {
|
||||
const sub = args[0];
|
||||
if (STATE_SUBCOMMANDS.has(sub)) {
|
||||
return [`state.${sub}`, args.slice(1)];
|
||||
}
|
||||
return [command, args];
|
||||
}
|
||||
|
||||
if (command === 'verify' && args.length > 0) {
|
||||
const sub = args[0];
|
||||
if (VERIFY_SUBCOMMANDS.has(sub)) {
|
||||
return [`verify.${sub}`, args.slice(1)];
|
||||
}
|
||||
return [command, args];
|
||||
}
|
||||
|
||||
if (command === 'init' && args.length > 0) {
|
||||
const sub = args[0];
|
||||
if (INIT_SUBCOMMANDS.has(sub)) {
|
||||
return [`init.${sub}`, args.slice(1)];
|
||||
}
|
||||
return [command, args];
|
||||
}
|
||||
|
||||
if (command === 'phase' && args.length > 0) {
|
||||
const sub = args[0];
|
||||
if (PHASE_SUBCOMMANDS.has(sub)) {
|
||||
return [`phase.${sub}`, args.slice(1)];
|
||||
}
|
||||
return [command, args];
|
||||
}
|
||||
|
||||
if (command === 'phases' && args.length > 0) {
|
||||
const sub = args[0];
|
||||
if (PHASES_SUBCOMMANDS.has(sub)) {
|
||||
return [`phases.${sub}`, args.slice(1)];
|
||||
}
|
||||
return [command, args];
|
||||
}
|
||||
|
||||
if (command === 'validate' && args.length > 0) {
|
||||
const sub = args[0];
|
||||
if (VALIDATE_SUBCOMMANDS.has(sub)) {
|
||||
return [`validate.${sub}`, args.slice(1)];
|
||||
}
|
||||
return [command, args];
|
||||
}
|
||||
|
||||
if (command === 'roadmap' && args.length > 0) {
|
||||
const sub = args[0];
|
||||
if (ROADMAP_SUBCOMMANDS.has(sub)) {
|
||||
return [`roadmap.${sub}`, args.slice(1)];
|
||||
}
|
||||
return [command, args];
|
||||
}
|
||||
|
||||
if (MERGE_FIRST_WITH_SUBCOMMAND.has(command) && args.length > 0) {
|
||||
const sub = args[0];
|
||||
return [`${command}.${sub}`, args.slice(1)];
|
||||
|
||||
@@ -14,6 +14,7 @@ const path = require('path');
|
||||
|
||||
const REPO_ROOT = path.join(__dirname, '..');
|
||||
const REGISTRY_FILE = path.join(REPO_ROOT, 'sdk', 'src', 'query', 'index.ts');
|
||||
const COMMAND_ALIASES_FILE = path.join(REPO_ROOT, 'get-shit-done', 'bin', 'lib', 'command-aliases.generated.cjs');
|
||||
|
||||
// Prose tokens that repeatedly appear after `gsd-sdk query` in English
|
||||
// documentation but aren't real command names.
|
||||
@@ -41,9 +42,39 @@ const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build']);
|
||||
function collectRegisteredNames() {
|
||||
const src = fs.readFileSync(REGISTRY_FILE, 'utf8');
|
||||
const names = new Set();
|
||||
|
||||
// Static registrations in index.ts
|
||||
const re = /registry\.register\(\s*['"]([^'"]+)['"]/g;
|
||||
let m;
|
||||
while ((m = re.exec(src)) !== null) names.add(m[1]);
|
||||
|
||||
// Manifest-generated family aliases registered via loop in index.ts.
|
||||
// Keep this in sync with command-manifest-driven routing seams.
|
||||
try {
|
||||
// eslint-disable-next-line global-require, import/no-dynamic-require
|
||||
const aliases = require(COMMAND_ALIASES_FILE);
|
||||
const familyArrays = [
|
||||
aliases.STATE_COMMAND_ALIASES,
|
||||
aliases.VERIFY_COMMAND_ALIASES,
|
||||
aliases.INIT_COMMAND_ALIASES,
|
||||
aliases.PHASE_COMMAND_ALIASES,
|
||||
aliases.PHASES_COMMAND_ALIASES,
|
||||
aliases.VALIDATE_COMMAND_ALIASES,
|
||||
aliases.ROADMAP_COMMAND_ALIASES,
|
||||
];
|
||||
for (const arr of familyArrays) {
|
||||
if (!Array.isArray(arr)) continue;
|
||||
for (const entry of arr) {
|
||||
if (entry?.canonical) names.add(entry.canonical);
|
||||
if (Array.isArray(entry?.aliases)) {
|
||||
for (const alias of entry.aliases) names.add(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// If generated aliases are unavailable, fall back to static extraction only.
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user