refactor(cli): extract Query CLI Adapter Module seam (#3074)

* refactor(cli): extract query adapter seam from cli entrypoint

* test: update ws forwarding guard for query-cli-adapter seam

* fix(query): close remaining CodeRabbit findings on cli adapter

* test: address remaining CodeRabbit nitpicks on ws forwarding coverage
This commit is contained in:
Tom Boucher
2026-05-03 15:57:01 -04:00
committed by GitHub
parent b6c401dc90
commit 5c9f34bd31
5 changed files with 152 additions and 66 deletions

View File

@@ -18,6 +18,7 @@ import { InitRunner } from './init-runner.js';
import { validateWorkstreamName } from './workstream-utils.js';
import { loadConfig } from './config.js';
import { assertRuntimeSupportsAutoMode } from './runtime-gate.js';
import { runQueryCliCommand } from './query/query-cli-adapter.js';
// ─── Parsed CLI args ─────────────────────────────────────────────────────────
@@ -276,13 +277,6 @@ async function readStdin(): Promise<string> {
});
}
/** When false, unknown `gsd-sdk query` commands error instead of shelling out to gsd-tools.cjs. */
function queryFallbackToCjsEnabled(): boolean {
const v = process.env.GSD_QUERY_FALLBACK?.toLowerCase();
if (v === 'off' || v === 'never' || v === 'false' || v === '0') return false;
return true;
}
// ─── Main ────────────────────────────────────────────────────────────────────
@@ -316,72 +310,35 @@ export async function main(argv: string[] = process.argv.slice(2)): Promise<void
return;
}
// ─── Query command ──────────────────────────────────────────────────────
if (args.command === 'query') {
const result = await runQueryCliCommand({
projectDir: args.projectDir,
ws: args.ws,
queryArgv: args.queryArgv,
});
for (const line of result.stderrLines) console.error(line);
for (const chunk of result.stdoutChunks) process.stdout.write(chunk);
process.exitCode = result.exitCode;
return;
}
// Fall back to GSD_WORKSTREAM env var when --ws is not supplied (#2791).
// gsd-tools.cjs resolves the active workstream via this env var; parity
// means gsd-sdk query commands see the same .planning/ path as gsd-tools.
// means gsd-sdk command paths see the same .planning/ path as gsd-tools.
if (args.ws === undefined && process.env.GSD_WORKSTREAM) {
const envWs = process.env.GSD_WORKSTREAM;
if (validateWorkstreamName(envWs)) {
args = { ...args, ws: envWs };
}
// If the env var contains an invalid name, silently ignore it (same as CJS).
}
// Multi-repo project-root resolution (issue #2623).
//
// When the user launches `gsd-sdk` from inside a `sub_repos`-listed child repo,
// `projectDir` defaults to `process.cwd()` which points at the child, not the
// parent workspace that owns `.planning/`. Mirror the legacy `gsd-tools.cjs`
// walk-up semantics so handlers see the correct project root.
//
// Idempotent: if `projectDir` already has its own `.planning/` (including an
// explicit `--project-dir` pointing at the workspace root), findProjectRoot
// returns it unchanged.
{
const { findProjectRoot } = await import('./query/helpers.js');
args = { ...args, projectDir: findProjectRoot(args.projectDir) };
}
// ─── Query command ──────────────────────────────────────────────────────
if (args.command === 'query') {
const { createRegistry } = await import('./query/index.js');
const { runQueryDispatch } = await import('./query/query-dispatch.js');
const { resolveGsdToolsPath, GSDToolsError } = await import('./gsd-tools.js');
const { GSDError, exitCodeFor } = await import('./errors.js');
try {
const registry = createRegistry();
const out = await runQueryDispatch({
registry,
projectDir: args.projectDir,
ws: args.ws,
cjsFallbackEnabled: queryFallbackToCjsEnabled(),
resolveGsdToolsPath,
dispatchNative: (cmd, argv) => registry.dispatch(cmd, argv, args.projectDir, args.ws),
}, args.queryArgv ?? []);
for (const line of out.stderr) console.error(line);
if (!out.ok) {
console.error(out.error.message);
process.exitCode = out.exit_code;
return;
}
if (out.stdout) process.stdout.write(out.stdout);
} catch (err) {
if (err instanceof GSDError) {
console.error(`Error: ${err.message}`);
process.exitCode = exitCodeFor(err.classification);
} else if (err instanceof GSDToolsError) {
console.error(`Error: ${err.message}`);
process.exitCode = err.exitCode ?? 1;
} else {
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
process.exitCode = 1;
}
}
return;
}
if (args.command !== 'run' && args.command !== 'init' && args.command !== 'auto') {
console.error('Error: Expected "gsd-sdk run <prompt>", "gsd-sdk auto", "gsd-sdk init [input]", or "gsd-sdk query <command>"');
console.error(USAGE);