paqad-ai
Version:
Spec-driven development framework — AI agents that think before they type
125 lines (114 loc) • 3.43 kB
JavaScript
import process from 'node:process';
import {
appendHooksLog,
isPaqadDisabled,
isProjectOnboarded,
parseHookPayload,
resolveProjectRoot,
standDownReason,
} from './lib/paqad-disabled.mjs';
import { stampAgentEntryMarker } from './lib/agent-entry-sentinel.mjs';
const LOG_PREFIX = 'stage-agent-start';
function logKey(value) {
return typeof value === 'string' && value.trim() !== ''
? value
.trim()
.replace(/[^A-Za-z0-9_-]/g, '_')
.slice(0, 128)
: 'none';
}
function errorMessage(error) {
return (error instanceof Error ? error.message : String(error))
.replace(/\s+/g, ' ')
.slice(0, 300);
}
async function importDist(relative) {
return import(new URL(`../../dist/${relative}`, import.meta.url).href);
}
async function recordStart(projectRoot, payload) {
try {
const { recordStageAgentStart } = await importDist('stage-isolation/subagent-completion.js');
recordStageAgentStart({ projectRoot, payload });
} catch {}
}
async function seed(projectRoot, payload) {
const agent = logKey(payload.agent_id);
let stage = 'unknown';
try {
const { recordRecipeInjected, seedStageAgentDetailed, stageHelperStage } = await importDist(
'stage-isolation/stage-brief.js',
);
const resolved = stageHelperStage(payload.agent_type);
if (!resolved) {
appendHooksLog(projectRoot, `${LOG_PREFIX}: skipped agent=${agent} stage=none`);
return;
}
stage = resolved;
const seeded = seedStageAgentDetailed(stage, projectRoot, payload);
process.stdout.write(seeded.output);
stampAgentEntryMarker(projectRoot, payload.agent_id);
await recordStart(projectRoot, payload);
for (const note of seeded.notes) {
appendHooksLog(projectRoot, `${LOG_PREFIX}: agent=${agent} stage=${stage}: ${note}`);
}
if (seeded.injected) {
try {
recordRecipeInjected(projectRoot, stage, payload, seeded.injected);
} catch (error) {
appendHooksLog(
projectRoot,
`${LOG_PREFIX}: recipe row not written agent=${agent}: ${errorMessage(error)}`,
);
}
}
appendHooksLog(projectRoot, `${LOG_PREFIX}: seeded agent=${agent} stage=${stage}`);
} catch (error) {
appendHooksLog(
projectRoot,
`${LOG_PREFIX}: failed agent=${agent} stage=${stage}: ${errorMessage(error)}`,
);
}
}
async function main(input) {
const hookPayload = parseHookPayload(input);
const projectRoot = resolveProjectRoot(process.env, hookPayload);
try {
if (isPaqadDisabled(projectRoot) || !isProjectOnboarded(projectRoot)) {
return;
}
const reason = standDownReason(hookPayload);
if (reason !== null) {
appendHooksLog(
projectRoot,
`${LOG_PREFIX}: stood-down agent=${logKey(hookPayload?.agent_id)} reason=${reason}`,
);
return;
}
let payload;
try {
payload = JSON.parse(input);
} catch {
return;
}
if (!payload || typeof payload !== 'object') {
return;
}
await seed(projectRoot, payload);
} catch (error) {
appendHooksLog(
projectRoot,
`${LOG_PREFIX}: failed agent=none stage=none: ${errorMessage(error)}`,
);
}
}
let input = '';
process.stdin.on('data', (chunk) => {
input += chunk;
});
process.stdin.on('end', () => {
main(input).finally(() => {
process.exitCode = 0;
});
});
process.stdin.resume();