paqad-ai
Version:
Spec-driven development framework — AI agents that think before they type
122 lines (108 loc) • 3.57 kB
JavaScript
import process from 'node:process';
import {
appendHooksLog,
hookStandsDown,
isPaqadDisabled,
parseHookPayload,
resolveProjectRoot,
} from './lib/paqad-disabled.mjs';
import {
resolveCompletionTranscriptText,
resolveSubagentTranscriptText,
} from './lib/transcript.mjs';
import { refreshHudFromHook } from './lib/hud-snapshot.mjs';
import { refreshCostFromHook } from './lib/cost-refresh.mjs';
const ADAPTER = process.argv[2] || 'claude-code';
async function main(input) {
try {
const projectRoot = resolveProjectRoot(process.env, parseHookPayload(input));
if (isPaqadDisabled(projectRoot) || hookStandsDown(parseHookPayload(input))) return 0;
let payload;
try {
payload = JSON.parse(input);
} catch {
/* v8 ignore next */
return 0;
}
const transcriptText = resolveCompletionTranscriptText(input, ADAPTER) || '';
if (await sendBack(projectRoot, payload, transcriptText)) return 0;
const recorderUrl = new URL(
'../../dist/stage-isolation/subagent-completion.js',
import.meta.url,
);
const { recordStageAgentCompletion, recordHighTableUsage } = await import(recorderUrl.href);
recordStageAgentCompletion({
projectRoot,
payload,
transcriptText,
adapter: ADAPTER,
log: (line) => appendHooksLog(projectRoot, line),
});
try {
recordHighTableUsage({ projectRoot, payload });
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
appendHooksLog(
projectRoot,
`stage-agent-completion: high-table usage not recorded: ${message}`,
);
}
const agentText = resolveSubagentTranscriptText(input, ADAPTER) || '';
if (agentText) {
try {
const markerUrl = new URL('../../dist/stage-evidence/marker-parse.js', import.meta.url);
const { parseAndRecordMarkers } = await import(markerUrl.href);
parseAndRecordMarkers({
projectRoot,
transcriptText: agentText,
sessionId: markerSessionId(payload),
keepSessionCache: true,
adapter: ADAPTER,
});
} catch {
/* v8 ignore next */
}
}
return 0;
} catch {
/* v8 ignore next */
return 0;
}
}
async function sendBack(projectRoot, payload, transcriptText) {
try {
const url = new URL('../../dist/stage-isolation/stage-handback.js', import.meta.url);
const { decideStageStop } = await import(url.href);
const decision = decideStageStop({ projectRoot, payload, transcriptText, adapter: ADAPTER });
if (!decision.block) return false;
process.stdout.write(JSON.stringify({ decision: 'block', reason: decision.reason }));
return true;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
appendHooksLog(projectRoot, `stage-agent-completion: send-back check failed: ${message}`);
return false;
}
}
function markerSessionId(payload) {
for (const candidate of [
payload?.session_id,
process.env.SE_SESSION,
process.env.CLAUDE_SESSION_ID,
]) {
if (typeof candidate === 'string' && candidate.trim() !== '') return candidate.trim();
}
return null;
}
let input = '';
process.stdin.on('data', (chunk) => {
input += chunk;
});
process.stdin.on('end', () => {
main(input).then(async (code) => {
await refreshCostFromHook(input, 'stage-agent-completion');
await refreshHudFromHook(input, 'stage-agent-completion');
process.exit(code);
});
});
process.stdin.resume();