feat(#972): graphify command cutover — first capability owning a command family (ADR-857 phase 4d-impl-2) (#975)
Migrate graphify into a Capability that owns its `graphify` command family, dispatched via the registry (#961 mechanism) instead of a hardcoded case. graphify is now an enable/disable plug-in. - src/graphify-command-router.cts: routeGraphifyCommand (standard route*Command), reproduces the removed case EXACTLY (query +--budget, status, diff, build, hidden build snapshot, usage/unknown errors); injectable _graphify test seam. - capabilities/graphify/capability.json: role feature, tier:full, skills:[graphify], config:{graphify.enabled default false}, commands:[{family:graphify, module, router:routeGraphifyCommand}]. - removed case 'graphify' from gsd-tools.cjs; graphify now flows default -> dispatchCapabilityCommand -> commandFamilies.graphify -> router. - regenerated registry (commandFamilies/bySkill/configSchema/profileMembership/ capabilityClusters for graphify); tier:full keeps 4c install/surface a no-op. Equivalence-proven: 8 recording-mock unit tests assert the exact fn+args per subcommand (budget, snapshot-vs-build); 9 subprocess tests assert distinguishing output shapes; existing graphify tests pass unchanged through the new path. Surfaced (not silently accepted): the pre-existing --budget-no-value NaN no-op quirk, preserved for equivalence, filed separately. Closes #972 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1507,33 +1507,6 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
||||
break;
|
||||
}
|
||||
|
||||
// ─── Graphify ──────────────────────────────────────────────────────────
|
||||
|
||||
case 'graphify': {
|
||||
const graphify = require('./lib/graphify.cjs');
|
||||
const subcommand = args[1];
|
||||
if (subcommand === 'query') {
|
||||
const term = args[2];
|
||||
if (!term) error('Usage: gsd-tools graphify query <term>', ERROR_REASON.USAGE);
|
||||
const budgetIdx = args.indexOf('--budget');
|
||||
const budget = budgetIdx !== -1 ? parseInt(args[budgetIdx + 1], 10) : null;
|
||||
core.output(graphify.graphifyQuery(cwd, term, { budget }), raw);
|
||||
} else if (subcommand === 'status') {
|
||||
core.output(graphify.graphifyStatus(cwd), raw);
|
||||
} else if (subcommand === 'diff') {
|
||||
core.output(graphify.graphifyDiff(cwd), raw);
|
||||
} else if (subcommand === 'build') {
|
||||
if (args[2] === 'snapshot') {
|
||||
core.output(graphify.writeSnapshot(cwd), raw);
|
||||
} else {
|
||||
core.output(graphify.graphifyBuild(cwd), raw);
|
||||
}
|
||||
} else {
|
||||
error('Unknown graphify subcommand. Available: build, query, status, diff', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// ─── Documentation ────────────────────────────────────────────────────
|
||||
|
||||
case 'docs-init': {
|
||||
@@ -2086,7 +2059,8 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
||||
// An unmigrated command still hits its hardcoded `case` above — untouched.
|
||||
// A migrated command's `case` is removed at cutover, so it reaches here and
|
||||
// dispatchCapabilityCommand routes it to the capability's registered router.
|
||||
// With commandFamilies={} today, this always returns false and is a no-op.
|
||||
// commandFamilies now includes migrated capabilities (e.g. graphify → graphify-command-router.cjs);
|
||||
// this returns true when a registered capability owns the command, false otherwise.
|
||||
if (dispatchCapabilityCommand({ command, args, cwd, raw, error })) break;
|
||||
|
||||
// #3243: if the caller passed a dotted form (e.g. "foo.bar"), the shim
|
||||
|
||||
@@ -7,6 +7,36 @@
|
||||
*/
|
||||
|
||||
const capabilities = {
|
||||
"graphify": {
|
||||
"id": "graphify",
|
||||
"role": "feature",
|
||||
"title": "Knowledge graph",
|
||||
"description": "Build, query, and inspect the project knowledge graph in `.planning/graphs/`; exposes graphify CLI subcommands (build, query, status, diff) and the /gsd-graphify skill.",
|
||||
"tier": "full",
|
||||
"requires": [],
|
||||
"skills": [
|
||||
"graphify"
|
||||
],
|
||||
"agents": [],
|
||||
"config": {
|
||||
"graphify.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enable the graphify knowledge-graph command + skill."
|
||||
}
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"family": "graphify",
|
||||
"module": "graphify-command-router.cjs",
|
||||
"router": "routeGraphifyCommand"
|
||||
}
|
||||
],
|
||||
"hooks": [],
|
||||
"steps": [],
|
||||
"contributions": [],
|
||||
"gates": []
|
||||
},
|
||||
"ui": {
|
||||
"id": "ui",
|
||||
"role": "feature",
|
||||
@@ -86,6 +116,7 @@ const capabilities = {
|
||||
};
|
||||
|
||||
const bySkill = {
|
||||
"graphify": "graphify",
|
||||
"ui-phase": "ui",
|
||||
"ui-review": "ui"
|
||||
};
|
||||
@@ -202,12 +233,19 @@ const byLoopPoint = {
|
||||
};
|
||||
|
||||
const configKeys = {
|
||||
"graphify.enabled": "graphify",
|
||||
"workflow.ui_phase": "ui",
|
||||
"workflow.ui_review": "ui",
|
||||
"workflow.ui_safety_gate": "ui"
|
||||
};
|
||||
|
||||
const configSchema = {
|
||||
"graphify.enabled": {
|
||||
"owner": "graphify",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enable the graphify knowledge-graph command + skill."
|
||||
},
|
||||
"workflow.ui_phase": {
|
||||
"owner": "ui",
|
||||
"type": "boolean",
|
||||
@@ -230,9 +268,18 @@ const configSchema = {
|
||||
|
||||
const runtimes = {};
|
||||
|
||||
const commandFamilies = {};
|
||||
const commandFamilies = {
|
||||
"graphify": {
|
||||
"capId": "graphify",
|
||||
"module": "graphify-command-router.cjs",
|
||||
"router": "routeGraphifyCommand"
|
||||
}
|
||||
};
|
||||
|
||||
const capabilityClusters = {
|
||||
"graphify": [
|
||||
"graphify"
|
||||
],
|
||||
"ui": [
|
||||
"ui-phase",
|
||||
"ui-review"
|
||||
@@ -240,6 +287,12 @@ const capabilityClusters = {
|
||||
};
|
||||
|
||||
const profileMembership = {
|
||||
"graphify": {
|
||||
"tier": "full",
|
||||
"profiles": [
|
||||
"full"
|
||||
]
|
||||
},
|
||||
"ui": {
|
||||
"tier": "full",
|
||||
"profiles": [
|
||||
@@ -249,6 +302,7 @@ const profileMembership = {
|
||||
};
|
||||
|
||||
const _requiresGraph = {
|
||||
"graphify": [],
|
||||
"ui": []
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user