feat(sdk): add NON_FAMILY_COMMAND_ALIASES to manifest — 14 missing commands (#3305)

* test(3251): red — assert 14 missing commands in command-aliases.generated.cjs

Parametrized test covering all 14 commands from issue #3251. Requires the
CJS manifest and asserts structurally (never greps source). Currently fails
because NON_FAMILY_COMMAND_ALIASES is not exported and all 14 are missing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(3251): add 14 missing commands to command-aliases.generated.cjs

Adds NON_FAMILY_COMMAND_ALIASES export to command-aliases.generated.cjs and
extends sdk/src/query/command-manifest.non-family.ts with the 10 commands that
were registered in static catalogs but absent from the manifest source-of-truth:
- check.decision-coverage-plan / check.decision-coverage-verify
- frontmatter.get
- phase.mvp-mode
- progress.bar
- stats.json
- task.is-behavior-adding
- todo.match-phase
- uat.render-checkpoint
- workstream.list

The other 4 (frontmatter.set, learnings.copy, milestone.complete,
requirements.mark-complete) were already in non-family.ts but unexported.

Generator (sdk/scripts/gen-command-aliases.ts) now produces both the TS and CJS
artifacts including the non-family section, sorted by canonical for determinism.
Freshness check (sdk/scripts/check-command-aliases-fresh.mjs) verifies TS and CJS
non-family parity against the manifest source-of-truth.

Fixes #3251

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(3251): add changeset and CHANGELOG entry for PR #3305

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(gen-command-aliases): preserve TS type interfaces and annotations on regen (#3251)

- Add FamilyCommandAlias and NonFamilyCommandAlias interface declarations to tsBody so regen never strips them
- Replace JSON.stringify with single-line compact serialisers for TS output (matches committed file format)
- Apply typed annotations (readonly FamilyCommandAlias[] / readonly NonFamilyCommandAlias[]) to all exported TS constants
- CJS path unchanged: remains untyped pure-JS with JSON.stringify multi-line format
- Regenerate sdk/src/query/command-aliases.generated.ts to sync with updated generator

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: drop redundant CHANGELOG.md edit (use .changeset/ fragment per CONTRIBUTING.md)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-05-09 11:46:40 -04:00
committed by GitHub
parent 31e2c22309
commit 04031244b8
7 changed files with 1106 additions and 84 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 3305
---
**`command-aliases.generated.cjs` now exports `NON_FAMILY_COMMAND_ALIASES` with all 14 previously-missing commands** — the CJS manifest used by the SDK query registry only exposed the 7 "family" command arrays (state, verify, init, phase, phases, validate, roadmap). Commands registered in static catalogs (foundation + domain) had no manifest entry, so tooling that queries the manifest could not discover them. `command-manifest.non-family.ts` is extended with 10 new entries (`check.decision-coverage-plan`, `check.decision-coverage-verify`, `frontmatter.get`, `phase.mvp-mode`, `progress.bar`, `stats.json`, `task.is-behavior-adding`, `todo.match-phase`, `uat.render-checkpoint`, `workstream.list`); the other 4 were already in the source but not exported. Both the TS generated file and CJS manifest now include a `NON_FAMILY_COMMAND_ALIASES` array (40 entries, sorted by canonical). The generator and freshness check are extended to cover the non-family section. (#3305)

View File

@@ -1,95 +1,807 @@
'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
* GENERATED FILE — state.*, verify.*, init.*, phase.*, phases.*, validate.*, roadmap.*, and non-family alias/subcommand metadata for CJS routing.
* Source: sdk/src/query/command-manifest.{state,verify,init,phase,phases,validate,roadmap,non-family}.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 },
{
"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 },
{
"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 },
{
"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 },
{
"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 },
{
"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 },
{
"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 },
{
"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 NON_FAMILY_COMMAND_ALIASES = [
{
"canonical": "check-commit",
"aliases": [],
"mutation": true
},
{
"canonical": "check.decision-coverage-plan",
"aliases": [
"check decision-coverage-plan"
],
"mutation": false
},
{
"canonical": "check.decision-coverage-verify",
"aliases": [
"check decision-coverage-verify"
],
"mutation": false
},
{
"canonical": "commit",
"aliases": [],
"mutation": true
},
{
"canonical": "commit-to-subrepo",
"aliases": [],
"mutation": true
},
{
"canonical": "config-ensure-section",
"aliases": [],
"mutation": true
},
{
"canonical": "config-new-project",
"aliases": [],
"mutation": true
},
{
"canonical": "config-set",
"aliases": [],
"mutation": true
},
{
"canonical": "config-set-model-profile",
"aliases": [],
"mutation": true
},
{
"canonical": "docs-init",
"aliases": [],
"mutation": true
},
{
"canonical": "frontmatter.get",
"aliases": [],
"mutation": false
},
{
"canonical": "frontmatter.merge",
"aliases": [],
"mutation": true
},
{
"canonical": "frontmatter.set",
"aliases": [],
"mutation": true
},
{
"canonical": "frontmatter.validate",
"aliases": [
"frontmatter validate"
],
"mutation": true
},
{
"canonical": "generate-claude-md",
"aliases": [],
"mutation": true
},
{
"canonical": "generate-claude-profile",
"aliases": [],
"mutation": true
},
{
"canonical": "generate-dev-preferences",
"aliases": [],
"mutation": true
},
{
"canonical": "intel.patch-meta",
"aliases": [
"intel patch-meta"
],
"mutation": true
},
{
"canonical": "intel.snapshot",
"aliases": [
"intel snapshot"
],
"mutation": true
},
{
"canonical": "learnings.copy",
"aliases": [
"learnings copy"
],
"mutation": true
},
{
"canonical": "learnings.delete",
"aliases": [
"learnings delete"
],
"mutation": true
},
{
"canonical": "learnings.prune",
"aliases": [
"learnings prune"
],
"mutation": true
},
{
"canonical": "milestone.complete",
"aliases": [
"milestone complete"
],
"mutation": true
},
{
"canonical": "phase.mvp-mode",
"aliases": [
"phase mvp-mode"
],
"mutation": false
},
{
"canonical": "progress.bar",
"aliases": [
"progress bar"
],
"mutation": false
},
{
"canonical": "requirements.mark-complete",
"aliases": [
"requirements mark-complete"
],
"mutation": true
},
{
"canonical": "stats.json",
"aliases": [
"stats json"
],
"mutation": false
},
{
"canonical": "task.is-behavior-adding",
"aliases": [
"task is-behavior-adding"
],
"mutation": false
},
{
"canonical": "template.fill",
"aliases": [],
"mutation": true
},
{
"canonical": "template.select",
"aliases": [
"template select"
],
"mutation": true
},
{
"canonical": "todo.complete",
"aliases": [
"todo complete"
],
"mutation": true
},
{
"canonical": "todo.match-phase",
"aliases": [
"todo match-phase"
],
"mutation": false
},
{
"canonical": "uat.render-checkpoint",
"aliases": [
"uat render-checkpoint"
],
"mutation": false
},
{
"canonical": "verify-summary",
"aliases": [
"verify.summary",
"verify summary"
],
"mutation": false
},
{
"canonical": "workstream.complete",
"aliases": [
"workstream complete"
],
"mutation": true
},
{
"canonical": "workstream.create",
"aliases": [
"workstream create"
],
"mutation": true
},
{
"canonical": "workstream.list",
"aliases": [
"workstream list"
],
"mutation": false
},
{
"canonical": "workstream.progress",
"aliases": [
"workstream progress"
],
"mutation": true
},
{
"canonical": "workstream.set",
"aliases": [
"workstream set"
],
"mutation": true
},
{
"canonical": "write-profile",
"aliases": [],
"mutation": true
}
];
const STATE_SUBCOMMANDS = STATE_COMMAND_ALIASES.map((entry) => entry.subcommand);
@@ -108,6 +820,7 @@ module.exports = {
PHASES_COMMAND_ALIASES,
VALIDATE_COMMAND_ALIASES,
ROADMAP_COMMAND_ALIASES,
NON_FAMILY_COMMAND_ALIASES,
STATE_SUBCOMMANDS,
VERIFY_SUBCOMMANDS,
INIT_SUBCOMMANDS,
@@ -115,4 +828,4 @@ module.exports = {
PHASES_SUBCOMMANDS,
VALIDATE_SUBCOMMANDS,
ROADMAP_SUBCOMMANDS,
};
};

View File

@@ -36,8 +36,13 @@ const {
PHASES_COMMAND_ALIASES,
VALIDATE_COMMAND_ALIASES,
ROADMAP_COMMAND_ALIASES,
NON_FAMILY_COMMAND_ALIASES,
} = await import('../dist/query/command-aliases.generated.js');
const {
NON_FAMILY_COMMAND_MANIFEST,
} = await import('../dist/query/command-manifest.non-family.js');
const cjsAliases = require(resolve(here, '..', '..', 'get-shit-done', 'bin', 'lib', 'command-aliases.generated.cjs'));
function toAliasEntries(manifest, family) {
@@ -50,6 +55,16 @@ function toAliasEntries(manifest, family) {
}));
}
function toNonFamilyAliasEntries(manifest) {
return [...manifest]
.sort((a, b) => a.canonical.localeCompare(b.canonical))
.map((entry) => ({
canonical: entry.canonical,
aliases: [...entry.aliases],
mutation: entry.mutation,
}));
}
function assertEqual(label, actual, expected) {
const a = JSON.stringify(actual);
const e = JSON.stringify(expected);
@@ -67,6 +82,7 @@ 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');
const expectedNonFamily = toNonFamilyAliasEntries(NON_FAMILY_COMMAND_MANIFEST);
assertEqual('TS STATE_COMMAND_ALIASES', STATE_COMMAND_ALIASES, expectedState);
assertEqual('TS VERIFY_COMMAND_ALIASES', VERIFY_COMMAND_ALIASES, expectedVerify);
@@ -75,6 +91,7 @@ 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('TS NON_FAMILY_COMMAND_ALIASES', NON_FAMILY_COMMAND_ALIASES, expectedNonFamily);
assertEqual('CJS STATE_COMMAND_ALIASES', cjsAliases.STATE_COMMAND_ALIASES, expectedState);
assertEqual('CJS VERIFY_COMMAND_ALIASES', cjsAliases.VERIFY_COMMAND_ALIASES, expectedVerify);
@@ -83,5 +100,6 @@ assertEqual('CJS PHASE_COMMAND_ALIASES', cjsAliases.PHASE_COMMAND_ALIASES, expec
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);
assertEqual('CJS NON_FAMILY_COMMAND_ALIASES', cjsAliases.NON_FAMILY_COMMAND_ALIASES, expectedNonFamily);
console.log('command alias artifacts are fresh');

View File

@@ -11,6 +11,7 @@ import { writeFile } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
import { COMMAND_DEFINITIONS_BY_FAMILY } from '../src/query/command-definition.js';
import { NON_FAMILY_COMMAND_MANIFEST } from '../src/query/command-manifest.non-family.js';
function toSubcommand(canonical: string, family: 'state' | 'verify' | 'init' | 'phase' | 'phases' | 'validate' | 'roadmap'): string {
const prefix = `${family}.`;
@@ -67,22 +68,66 @@ async function main(): Promise<void> {
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;`,
// Non-family entries — sorted by canonical for deterministic output.
const nonFamilyEntries = [...NON_FAMILY_COMMAND_MANIFEST]
.sort((a, b) => a.canonical.localeCompare(b.canonical))
.map((entry) => ({
canonical: entry.canonical,
aliases: [...entry.aliases],
mutation: entry.mutation,
}));
// Serialise a FamilyCommandAlias entry as a single-line TS literal.
function serializeFamily(e: { canonical: string; aliases: string[]; subcommand: string; mutation: boolean }): string {
const aliases = `[${e.aliases.map((a) => `'${a}'`).join(', ')}]`;
return `{ canonical: '${e.canonical}', aliases: ${aliases}, subcommand: '${e.subcommand}', mutation: ${e.mutation} }`;
}
// Serialise a NonFamilyCommandAlias entry as a single-line TS literal.
function serializeNonFamily(e: { canonical: string; aliases: string[]; mutation: boolean }): string {
const aliases = `[${e.aliases.map((a) => `'${a}'`).join(', ')}]`;
return `{ canonical: '${e.canonical}', aliases: ${aliases}, mutation: ${e.mutation} }`;
}
function renderFamilyArray(entries: { canonical: string; aliases: string[]; subcommand: string; mutation: boolean }[]): string {
return `[\n${entries.map((e) => ` ${serializeFamily(e)},`).join('\n')}\n]`;
}
function renderNonFamilyArray(entries: { canonical: string; aliases: string[]; mutation: boolean }[]): string {
return `[\n${entries.map((e) => ` ${serializeNonFamily(e)},`).join('\n')}\n]`;
}
const tsOutPath = fileURLToPath(new URL('../src/query/command-aliases.generated.ts', import.meta.url));
const tsHeader = `/**\n * GENERATED FILE — command alias expansion for state.*, verify.*, init.*, phase.*, phases.*, validate.*, roadmap.*, and non-family commands.\n * Source: sdk/src/query/command-manifest.{state,verify,init,phase,phases,validate,roadmap,non-family}.ts\n */\n\n`;
const tsBody = [
'export interface FamilyCommandAlias {',
' canonical: string;',
' aliases: string[];',
' subcommand: string;',
' mutation: boolean;',
'}',
'',
`export const VERIFY_COMMAND_ALIASES = ${JSON.stringify(verifyEntries, null, 2)} as const;`,
`export const STATE_COMMAND_ALIASES: readonly FamilyCommandAlias[] = ${renderFamilyArray(stateEntries)} as const;`,
'',
`export const INIT_COMMAND_ALIASES = ${JSON.stringify(initEntries, null, 2)} as const;`,
`export const VERIFY_COMMAND_ALIASES: readonly FamilyCommandAlias[] = ${renderFamilyArray(verifyEntries)} as const;`,
'',
`export const PHASE_COMMAND_ALIASES = ${JSON.stringify(phaseEntries, null, 2)} as const;`,
`export const INIT_COMMAND_ALIASES: readonly FamilyCommandAlias[] = ${renderFamilyArray(initEntries)} as const;`,
'',
`export const PHASES_COMMAND_ALIASES = ${JSON.stringify(phasesEntries, null, 2)} as const;`,
`export const PHASE_COMMAND_ALIASES: readonly FamilyCommandAlias[] = ${renderFamilyArray(phaseEntries)} as const;`,
'',
`export const VALIDATE_COMMAND_ALIASES = ${JSON.stringify(validateEntries, null, 2)} as const;`,
`export const PHASES_COMMAND_ALIASES: readonly FamilyCommandAlias[] = ${renderFamilyArray(phasesEntries)} as const;`,
'',
`export const ROADMAP_COMMAND_ALIASES = ${JSON.stringify(roadmapEntries, null, 2)} as const;`,
`export const VALIDATE_COMMAND_ALIASES: readonly FamilyCommandAlias[] = ${renderFamilyArray(validateEntries)} as const;`,
'',
`export const ROADMAP_COMMAND_ALIASES: readonly FamilyCommandAlias[] = ${renderFamilyArray(roadmapEntries)} as const;`,
'',
'export interface NonFamilyCommandAlias {',
' canonical: string;',
' aliases: string[];',
' mutation: boolean;',
'}',
'',
`export const NON_FAMILY_COMMAND_ALIASES: readonly NonFamilyCommandAlias[] = ${renderNonFamilyArray(nonFamilyEntries)} 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));',
@@ -92,9 +137,57 @@ async function main(): Promise<void> {
'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));',
'',
].join('\n');
await writeFile(outPath, header + body, 'utf-8');
await writeFile(tsOutPath, tsHeader + tsBody, 'utf-8');
// Also generate the CJS mirror used by get-shit-done/bin/lib/ seams.
// CJS is plain JavaScript — no type annotations.
const cjsOutPath = fileURLToPath(new URL('../../get-shit-done/bin/lib/command-aliases.generated.cjs', import.meta.url));
const cjsHeader = `'use strict';\n\n/**\n * GENERATED FILE — state.*, verify.*, init.*, phase.*, phases.*, validate.*, roadmap.*, and non-family alias/subcommand metadata for CJS routing.\n * Source: sdk/src/query/command-manifest.{state,verify,init,phase,phases,validate,roadmap,non-family}.ts\n */\n\n`;
const cjsBody = [
`const STATE_COMMAND_ALIASES = ${JSON.stringify(stateEntries, null, 2)};`,
'',
`const VERIFY_COMMAND_ALIASES = ${JSON.stringify(verifyEntries, null, 2)};`,
'',
`const INIT_COMMAND_ALIASES = ${JSON.stringify(initEntries, null, 2)};`,
'',
`const PHASE_COMMAND_ALIASES = ${JSON.stringify(phaseEntries, null, 2)};`,
'',
`const PHASES_COMMAND_ALIASES = ${JSON.stringify(phasesEntries, null, 2)};`,
'',
`const VALIDATE_COMMAND_ALIASES = ${JSON.stringify(validateEntries, null, 2)};`,
'',
`const ROADMAP_COMMAND_ALIASES = ${JSON.stringify(roadmapEntries, null, 2)};`,
'',
`const NON_FAMILY_COMMAND_ALIASES = ${JSON.stringify(nonFamilyEntries, null, 2)};`,
'',
`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,`,
` NON_FAMILY_COMMAND_ALIASES,`,
` STATE_SUBCOMMANDS,`,
` VERIFY_SUBCOMMANDS,`,
` INIT_SUBCOMMANDS,`,
` PHASE_SUBCOMMANDS,`,
` PHASES_SUBCOMMANDS,`,
` VALIDATE_SUBCOMMANDS,`,
` ROADMAP_SUBCOMMANDS,`,
`};`,
].join('\n');
await writeFile(cjsOutPath, cjsHeader + cjsBody, 'utf-8');
}
main().catch((err) => {

View File

@@ -1,6 +1,6 @@
/**
* 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
* GENERATED FILE — command alias expansion for state.*, verify.*, init.*, phase.*, phases.*, validate.*, roadmap.*, and non-family commands.
* Source: sdk/src/query/command-manifest.{state,verify,init,phase,phases,validate,roadmap,non-family}.ts
*/
export interface FamilyCommandAlias {
@@ -97,6 +97,55 @@ export const ROADMAP_COMMAND_ALIASES: readonly FamilyCommandAlias[] = [
{ canonical: 'roadmap.annotate-dependencies', aliases: ['roadmap annotate-dependencies'], subcommand: 'annotate-dependencies', mutation: true },
] as const;
export interface NonFamilyCommandAlias {
canonical: string;
aliases: string[];
mutation: boolean;
}
export const NON_FAMILY_COMMAND_ALIASES: readonly NonFamilyCommandAlias[] = [
{ canonical: 'check-commit', aliases: [], mutation: true },
{ canonical: 'check.decision-coverage-plan', aliases: ['check decision-coverage-plan'], mutation: false },
{ canonical: 'check.decision-coverage-verify', aliases: ['check decision-coverage-verify'], mutation: false },
{ canonical: 'commit', aliases: [], mutation: true },
{ canonical: 'commit-to-subrepo', aliases: [], mutation: true },
{ canonical: 'config-ensure-section', aliases: [], mutation: true },
{ canonical: 'config-new-project', aliases: [], mutation: true },
{ canonical: 'config-set', aliases: [], mutation: true },
{ canonical: 'config-set-model-profile', aliases: [], mutation: true },
{ canonical: 'docs-init', aliases: [], mutation: true },
{ canonical: 'frontmatter.get', aliases: [], mutation: false },
{ canonical: 'frontmatter.merge', aliases: [], mutation: true },
{ canonical: 'frontmatter.set', aliases: [], mutation: true },
{ canonical: 'frontmatter.validate', aliases: ['frontmatter validate'], mutation: true },
{ canonical: 'generate-claude-md', aliases: [], mutation: true },
{ canonical: 'generate-claude-profile', aliases: [], mutation: true },
{ canonical: 'generate-dev-preferences', aliases: [], mutation: true },
{ canonical: 'intel.patch-meta', aliases: ['intel patch-meta'], mutation: true },
{ canonical: 'intel.snapshot', aliases: ['intel snapshot'], mutation: true },
{ canonical: 'learnings.copy', aliases: ['learnings copy'], mutation: true },
{ canonical: 'learnings.delete', aliases: ['learnings delete'], mutation: true },
{ canonical: 'learnings.prune', aliases: ['learnings prune'], mutation: true },
{ canonical: 'milestone.complete', aliases: ['milestone complete'], mutation: true },
{ canonical: 'phase.mvp-mode', aliases: ['phase mvp-mode'], mutation: false },
{ canonical: 'progress.bar', aliases: ['progress bar'], mutation: false },
{ canonical: 'requirements.mark-complete', aliases: ['requirements mark-complete'], mutation: true },
{ canonical: 'stats.json', aliases: ['stats json'], mutation: false },
{ canonical: 'task.is-behavior-adding', aliases: ['task is-behavior-adding'], mutation: false },
{ canonical: 'template.fill', aliases: [], mutation: true },
{ canonical: 'template.select', aliases: ['template select'], mutation: true },
{ canonical: 'todo.complete', aliases: ['todo complete'], mutation: true },
{ canonical: 'todo.match-phase', aliases: ['todo match-phase'], mutation: false },
{ canonical: 'uat.render-checkpoint', aliases: ['uat render-checkpoint'], mutation: false },
{ canonical: 'verify-summary', aliases: ['verify.summary', 'verify summary'], mutation: false },
{ canonical: 'workstream.complete', aliases: ['workstream complete'], mutation: true },
{ canonical: 'workstream.create', aliases: ['workstream create'], mutation: true },
{ canonical: 'workstream.list', aliases: ['workstream list'], mutation: false },
{ canonical: 'workstream.progress', aliases: ['workstream progress'], mutation: true },
{ canonical: 'workstream.set', aliases: ['workstream set'], mutation: true },
{ canonical: 'write-profile', aliases: [], 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));
@@ -104,4 +153,3 @@ export const PHASE_SUBCOMMANDS = new Set<string>(PHASE_COMMAND_ALIASES.map((entr
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));

View File

@@ -8,6 +8,10 @@ export interface NonFamilyCommandManifestEntry {
}
export const NON_FAMILY_COMMAND_MANIFEST: readonly NonFamilyCommandManifestEntry[] = [
{ canonical: 'check.decision-coverage-plan', aliases: ['check decision-coverage-plan'], mutation: false, outputMode: 'json' },
{ canonical: 'check.decision-coverage-verify', aliases: ['check decision-coverage-verify'], mutation: false, outputMode: 'json' },
{ canonical: 'frontmatter.get', aliases: [], mutation: false, outputMode: 'json' },
{ canonical: 'frontmatter.set', aliases: [], mutation: true, outputMode: 'json' },
{ canonical: 'frontmatter.merge', aliases: [], mutation: true, outputMode: 'json' },
{ canonical: 'frontmatter.validate', aliases: ['frontmatter validate'], mutation: true, outputMode: 'json' },
@@ -28,12 +32,25 @@ export const NON_FAMILY_COMMAND_MANIFEST: readonly NonFamilyCommandManifestEntry
{ canonical: 'todo.complete', aliases: ['todo complete'], mutation: true, outputMode: 'json' },
{ canonical: 'milestone.complete', aliases: ['milestone complete'], mutation: true, outputMode: 'json' },
{ canonical: 'phase.mvp-mode', aliases: ['phase mvp-mode'], mutation: false, outputMode: 'json' },
{ canonical: 'progress.bar', aliases: ['progress bar'], mutation: false, outputMode: 'json' },
{ canonical: 'stats.json', aliases: ['stats json'], mutation: false, outputMode: 'json' },
{ canonical: 'task.is-behavior-adding', aliases: ['task is-behavior-adding'], mutation: false, outputMode: 'json' },
{ canonical: 'todo.match-phase', aliases: ['todo match-phase'], mutation: false, outputMode: 'json' },
{ canonical: 'uat.render-checkpoint', aliases: ['uat render-checkpoint'], mutation: false, outputMode: 'json' },
{
canonical: 'workstream.create',
aliases: ['workstream create'],
mutation: true,
outputMode: 'json',
},
{ canonical: 'workstream.list', aliases: ['workstream list'], mutation: false, outputMode: 'json' },
{ canonical: 'workstream.set', aliases: ['workstream set'], mutation: true, outputMode: 'json' },
{
canonical: 'workstream.complete',

View File

@@ -0,0 +1,128 @@
'use strict';
/**
* Regression guard for issue #3251:
* 14 commands used in workflows must be present in command-aliases.generated.cjs.
*
* Asserts structurally by requiring the manifest and checking each canonical
* command appears in either the family arrays or the non-family array.
* Never greps the source file — see feedback_no_source_grep_tests.md.
*/
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const path = require('path');
const REPO_ROOT = path.join(__dirname, '..');
const COMMAND_ALIASES_FILE = path.join(
REPO_ROOT,
'get-shit-done',
'bin',
'lib',
'command-aliases.generated.cjs',
);
const MISSING_14 = [
'check.decision-coverage-plan',
'check.decision-coverage-verify',
'frontmatter.get',
'frontmatter.set',
'learnings.copy',
'milestone.complete',
'phase.mvp-mode',
'progress.bar',
'requirements.mark-complete',
'stats.json',
'task.is-behavior-adding',
'todo.match-phase',
'uat.render-checkpoint',
'workstream.list',
];
describe('feat-3251: command-aliases.generated.cjs manifest coverage', () => {
let manifest;
test('manifest file can be required without error', () => {
try {
manifest = require(COMMAND_ALIASES_FILE);
} catch (err) {
assert.fail(`Failed to require manifest: ${err.message}`);
}
assert.ok(manifest, 'manifest should be truthy');
});
test('manifest exports NON_FAMILY_COMMAND_ALIASES array', () => {
manifest = manifest ?? require(COMMAND_ALIASES_FILE);
assert.ok(
Array.isArray(manifest.NON_FAMILY_COMMAND_ALIASES),
'NON_FAMILY_COMMAND_ALIASES must be an exported array in command-aliases.generated.cjs',
);
});
test('all 14 missing commands are present in the manifest (family or non-family)', () => {
manifest = manifest ?? require(COMMAND_ALIASES_FILE);
const allCanonicalsInManifest = new Set();
// Collect from all family arrays
const familyArrayKeys = [
'STATE_COMMAND_ALIASES',
'VERIFY_COMMAND_ALIASES',
'INIT_COMMAND_ALIASES',
'PHASE_COMMAND_ALIASES',
'PHASES_COMMAND_ALIASES',
'VALIDATE_COMMAND_ALIASES',
'ROADMAP_COMMAND_ALIASES',
];
for (const key of familyArrayKeys) {
const arr = manifest[key];
if (!Array.isArray(arr)) continue;
for (const entry of arr) {
if (entry && entry.canonical) allCanonicalsInManifest.add(entry.canonical);
}
}
// Collect from non-family array
const nonFamily = manifest.NON_FAMILY_COMMAND_ALIASES;
if (Array.isArray(nonFamily)) {
for (const entry of nonFamily) {
if (entry && entry.canonical) allCanonicalsInManifest.add(entry.canonical);
}
}
const missing = MISSING_14.filter((cmd) => !allCanonicalsInManifest.has(cmd));
assert.deepStrictEqual(
missing,
[],
`${missing.length} command(s) still missing from manifest: ${missing.join(', ')}`,
);
});
test('each non-family entry has required fields: canonical, aliases, mutation', () => {
manifest = manifest ?? require(COMMAND_ALIASES_FILE);
const nonFamily = manifest.NON_FAMILY_COMMAND_ALIASES;
if (!Array.isArray(nonFamily)) return; // caught by earlier test
for (const entry of nonFamily) {
assert.ok(typeof entry.canonical === 'string' && entry.canonical.length > 0,
`entry missing canonical: ${JSON.stringify(entry)}`);
assert.ok(Array.isArray(entry.aliases),
`entry missing aliases array for canonical=${entry.canonical}`);
assert.ok(typeof entry.mutation === 'boolean',
`entry missing mutation boolean for canonical=${entry.canonical}`);
}
});
test('NON_FAMILY_COMMAND_ALIASES is sorted by canonical (deterministic output)', () => {
manifest = manifest ?? require(COMMAND_ALIASES_FILE);
const nonFamily = manifest.NON_FAMILY_COMMAND_ALIASES;
if (!Array.isArray(nonFamily)) return; // caught by earlier test
const canonicals = nonFamily.map((e) => e.canonical);
const sorted = [...canonicals].sort((a, b) => a.localeCompare(b));
assert.deepStrictEqual(
canonicals,
sorted,
'NON_FAMILY_COMMAND_ALIASES must be sorted by canonical for deterministic regeneration',
);
});
});