refactor(query): deepen command/dispatch seams and resolve coderabbit findings

* refactor(query): deepen command definition seam and fold fallback mapping cleanup

* refactor(query): add shared dispatch formatting module seam

* fix(query): restore QueryResult type import in dispatch deps

* test/query: align raw-output policy and definition normalization contracts

* refactor(query): deepen diagnosis, invariant report, and error taxonomy seams

* refactor(query): deepen dispatch plan, fallback bridge, policy snapshot, and hints seams

* refactor(query): deepen validation, fallback policy, capability, and result builder seams

* refactor(query): deepen resolution strategy, output classifier, observability, and policy-capability seams

* refactor(query): finalize deep strategy/classifier/observability/capability seams

* test/query: address coderabbit inline and out-of-diff dispatch nits

* fix(query): address remaining coderabbit input-validation and bridge stderr threads

* fix(query): address remaining coderabbit dispatch and strategy/output nits
This commit is contained in:
Tom Boucher
2026-05-03 15:29:34 -04:00
committed by GitHub
parent c3f896f311
commit b6c401dc90
49 changed files with 1159 additions and 484 deletions

View File

@@ -1,14 +1,8 @@
import { execFile } from 'node:child_process';
import { readFile } from 'node:fs/promises';
import { extractField } from './registry.js';
import { formatSuccess } from './query-dispatch-formatting.js';
import type { QueryDispatchResult } from './query-dispatch-contract.js';
import { mapFallbackDispatchError, toDispatchFailure } from './query-dispatch-error-mapper.js';
interface CjsFallbackQueryResult {
mode: 'json' | 'text';
output: unknown;
stderr: string;
}
import { runFallbackBridge } from './query-fallback-bridge-adapter.js';
import { fallbackBridgeNotices } from './query-dispatch-observability.js';
export interface RunCjsFallbackDispatchInput {
projectDir: string;
@@ -19,85 +13,21 @@ export interface RunCjsFallbackDispatchInput {
pickField?: string;
}
function dottedCommandToCjsArgv(normCmd: string, normArgs: string[]): string[] {
if (normCmd.includes('.')) return [...normCmd.split('.'), ...normArgs];
return [normCmd, ...normArgs];
}
function execGsdToolsCjsQuery(
projectDir: string,
gsdToolsPath: string,
normCmd: string,
normArgs: string[],
ws: string | undefined,
): Promise<{ stdout: string; stderr: string }> {
const cjsArgv = dottedCommandToCjsArgv(normCmd, normArgs);
const wsSuffix = ws ? ['--ws', ws] : [];
const fullArgv = [gsdToolsPath, ...cjsArgv, ...wsSuffix];
return new Promise((resolve, reject) => {
execFile(
process.execPath,
fullArgv,
{ cwd: projectDir, maxBuffer: 10 * 1024 * 1024, timeout: 30_000, killSignal: 'SIGKILL', env: { ...process.env } },
(err, stdout, stderr) => {
if (err) reject(err);
else resolve({ stdout: stdout?.toString() ?? '', stderr: stderr?.toString() ?? '' });
},
);
});
}
async function parseCliQueryJsonOutput(raw: string, projectDir: string): Promise<unknown> {
const trimmed = raw.trim();
if (trimmed === '') return null;
let jsonStr = trimmed;
if (jsonStr.startsWith('@file:')) {
const rel = jsonStr.slice(6).trim();
const { resolvePathUnderProject } = await import('./helpers.js');
const filePath = await resolvePathUnderProject(projectDir, rel);
jsonStr = await readFile(filePath, 'utf-8');
}
return JSON.parse(jsonStr);
}
async function runCjsFallbackQuery(
projectDir: string,
gsdToolsPath: string,
normCmd: string,
normArgs: string[],
ws: string | undefined,
): Promise<CjsFallbackQueryResult> {
const { stdout, stderr } = await execGsdToolsCjsQuery(projectDir, gsdToolsPath, normCmd, normArgs, ws);
try {
const output = await parseCliQueryJsonOutput(stdout, projectDir);
return { mode: 'json', output, stderr };
} catch {
return { mode: 'text', output: stdout, stderr };
}
}
function formatFallbackOutput(data: unknown, mode: 'json' | 'text', pickField?: string): string | undefined {
if (mode === 'text') {
const text = String(data ?? '');
if (!text.trim()) return undefined;
return text.endsWith('\n') ? text : `${text}\n`;
}
let output: unknown = data;
if (pickField) output = extractField(output, pickField);
return `${JSON.stringify(output, null, 2)}\n`;
return formatSuccess(data, mode, pickField);
}
export async function runCjsFallbackDispatch(input: RunCjsFallbackDispatchInput): Promise<QueryDispatchResult> {
const { projectDir, gsdToolsPath, normCmd, normArgs, ws, pickField } = input;
const stderr = [
`[gsd-sdk] '${normCmd}' not in native registry; falling back to gsd-tools.cjs.`,
'[gsd-sdk] Transparent bridge — prefer adding a native handler when parity matters.',
];
const stderr = fallbackBridgeNotices(normCmd);
try {
const fallback = await runCjsFallbackQuery(projectDir, gsdToolsPath, normCmd, normArgs, ws);
const fallback = await runFallbackBridge({ projectDir, gsdToolsPath, normCmd, normArgs, ws });
if (fallback.stderr.trim()) stderr.push(fallback.stderr.trimEnd());
return {
ok: true,
@@ -106,9 +36,8 @@ export async function runCjsFallbackDispatch(input: RunCjsFallbackDispatchInput)
exit_code: 0,
};
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
return toDispatchFailure(
mapFallbackDispatchError(msg, normCmd, normArgs),
mapFallbackDispatchError(err, normCmd, normArgs),
stderr,
);
}