fix(cli): register the documented context command (#1611) (#1613)

Fixes #1611.

## What

`codegraph context <task>` has been advertised in the CLI usage header since the first commit, and the ContextBuilder behind the public `buildContext` API has always shipped in the package — but the command was never registered with commander (verified via `git log -S`: this is drift present from day one, not a removal). Invoking it errored with `unknown command 'context'`, which broke external integrations built against the documented contract — Memorix 1.8.1 invokes `codegraph context --path <project-root> --format json --max-nodes 8 --no-code <task>` and silently falls back to its own heuristic index when the command is missing.

## How

Registers `context <task...>` next to the other read commands (`query`/`explore` pattern), mapping flags 1:1 onto `BuildContextOptions`:

- `-p, --path <path>` — resolved exactly like every sibling command (nearest initialized project)
- `-f, --format <format>` — `markdown` (default) or `json`, unknown values rejected with exit 1
- `-n, --max-nodes <number>` — positive integer, validated
- `--no-code` — structure only (`includeCode: false`)

JSON output is clean, machine-parseable stdout — `error()` and warnings go to stderr — and the uninitialized-project path matches the sibling commands' error text and exit code. The usage-header line needed no change; the registered syntax matches what it has always advertised.

## Tested

New `__tests__/cli-context-command.test.ts` (modeled on `cli-query-command.test.ts`, spawning the built binary against a temp fixture): JSON parseability + shape, `--max-nodes` bounding, the exact Memorix invocation shape (`--format json --max-nodes 8 --no-code`), markdown default, uninitialized-project failure, unknown-format rejection. `npx vitest run __tests__/cli-context-command.test.ts __tests__/context.test.ts __tests__/context-ranking.test.ts __tests__/cli-query-command.test.ts` → 4 files, 39 tests, all green.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01LxZj6W6Y1SHXwvpT3uwJpK
This commit is contained in:
Colby Mchenry
2026-08-26 10:39:06 -05:00
committed by GitHub
parent a5c2709e6d
commit c382225461
3 changed files with 175 additions and 0 deletions
+59
View File
@@ -1250,6 +1250,65 @@ program
}
});
/**
* codegraph context <task...>
*
* The CLI face of the public `buildContext` API (ContextBuilder): FTS entry
* points + graph expansion + code blocks, formatted as markdown or JSON.
* Advertised in the usage header since the first release but never actually
* registered (#1611); external integrations (e.g. Memorix) invoke it as
* `codegraph context --path <root> --format json --max-nodes 8 --no-code <task>`.
*/
program
.command('context <task...>')
.description('Build context for a task: relevant symbols, relationships, and code blocks')
.option('-p, --path <path>', 'Project path')
.option('-f, --format <format>', 'Output format: markdown or json', 'markdown')
.option('-n, --max-nodes <number>', 'Maximum number of symbols to include')
.option('--no-code', 'Omit code blocks (structure only)')
.action(async (taskParts: string[], options: { path?: string; format?: string; maxNodes?: string; code?: boolean }) => {
const projectPath = resolveProjectPath(options.path);
const format = options.format ?? 'markdown';
if (format !== 'markdown' && format !== 'json') {
error(`Unknown format "${options.format}" — use "markdown" or "json".`);
process.exit(1);
}
let maxNodes: number | undefined;
if (options.maxNodes !== undefined) {
maxNodes = parseInt(options.maxNodes, 10);
if (Number.isNaN(maxNodes) || maxNodes < 1) {
error(`--max-nodes expects a positive integer, got "${options.maxNodes}".`);
process.exit(1);
}
}
try {
if (!isInitialized(projectPath)) {
error(`CodeGraph not initialized in ${projectPath}`);
process.exit(1);
}
const { default: CodeGraph } = await loadCodeGraph();
const cg = await CodeGraph.open(projectPath);
const result = await cg.buildContext(taskParts.join(' '), {
format,
includeCode: options.code !== false,
...(maxNodes !== undefined ? { maxNodes } : {}),
});
// Both supported formats return a formatted string; print it verbatim so
// `--format json` stays machine-parseable on stdout (error()/warnings go
// to stderr only).
console.log(typeof result === 'string' ? result : JSON.stringify(result, null, 2));
cg.destroy();
} catch (err) {
error(`Context build failed: ${err instanceof Error ? err.message : String(err)}`);
process.exit(1);
}
});
/**
* codegraph prompt-hook (hidden)
*