paqad-ai
Version:
Spec-driven development framework — AI agents that think before they type
161 lines (141 loc) • 5.96 kB
JavaScript
import { rmSync } from 'node:fs';
import process from 'node:process';
import { clearAgentEntryMarkers, sentinelPath } from './lib/agent-entry-sentinel.mjs';
import {
appendHooksLog,
hookStandsDown,
isProjectOnboarded,
parseHookPayload,
resolveProjectRoot,
} from './lib/paqad-disabled.mjs';
import { sessionIdFromStdin } from './lib/context-seam-emit.mjs';
import { mergeSessionStartOutput } from './lib/session-start-output.mjs';
function sourceFromStdin(input) {
const parsed = parseHookPayload(input);
return parsed && typeof parsed.source === 'string' ? parsed.source : undefined;
}
async function main(input) {
const projectRoot = resolveProjectRoot(process.env, parseHookPayload(input));
if (hookStandsDown(parseHookPayload(input))) return 0;
try {
rmSync(sentinelPath(projectRoot, sessionIdFromStdin(input)), { force: true });
clearAgentEntryMarkers(projectRoot);
} catch {}
try {
const abandonUrl = new URL('../../dist/feature-evidence/abandon.js', import.meta.url);
const { closeAbandonedBundles } = await import(abandonUrl.href);
closeAbandonedBundles(projectRoot);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
appendHooksLog(projectRoot, `session-start: abandoned close skipped: ${message}`);
}
try {
const hostSessionId = sessionIdFromStdin(input);
if (hostSessionId) {
const distUrl = new URL('../../dist/rag-ledger/session.js', import.meta.url);
const { persistLedgerSessionId } = await import(distUrl.href);
persistLedgerSessionId(projectRoot, hostSessionId);
const adoptUrl = new URL('../../dist/feature-evidence/adoption.js', import.meta.url);
const { reconcileSessionControl } = await import(adoptUrl.href);
reconcileSessionControl(projectRoot, hostSessionId);
try {
const releaseUrl = new URL(
'../../dist/stage-evidence/completion-deferred.js',
import.meta.url,
);
const { releaseStageAgentsAtSessionStart } = await import(releaseUrl.href);
releaseStageAgentsAtSessionStart(projectRoot, hostSessionId, sourceFromStdin(input));
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
appendHooksLog(projectRoot, `session-start: stage agent release skipped: ${message}`);
}
const baselineUrl = new URL('../../dist/pipeline/dirty-baseline.js', import.meta.url);
const { captureSessionDirtyBaseline } = await import(baselineUrl.href);
await captureSessionDirtyBaseline(projectRoot, hostSessionId);
}
} catch {}
const output = mergeSessionStartOutput(
mergeSessionStartOutput(
mergeSessionStartOutput(
mergeSessionStartOutput(
await missingStageAgentsOutput(projectRoot),
await teamConfigNotice(projectRoot),
),
await lastUpdateNotice(projectRoot, sourceFromStdin(input)),
),
await designSignOffNotice(projectRoot),
),
await rulesDriftNotice(projectRoot),
);
if (output) process.stdout.write(output);
return 0;
}
async function missingStageAgentsOutput(projectRoot) {
if (!process.env.CLAUDE_PROJECT_DIR || !isProjectOnboarded(projectRoot)) return null;
try {
const url = new URL('../../dist/stage-isolation/agent-availability.js', import.meta.url);
const { stageDispatchStopHookOutput } = await import(url.href);
return stageDispatchStopHookOutput(projectRoot);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
appendHooksLog(projectRoot, `session-start: stage agent check skipped: ${message}`);
return null;
}
}
async function teamConfigNotice(projectRoot) {
if (!isProjectOnboarded(projectRoot)) return null;
try {
const url = new URL('../../dist/core/team-config-report.js', import.meta.url);
const { teamConfigSessionStartText } = await import(url.href);
return teamConfigSessionStartText(projectRoot);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
appendHooksLog(projectRoot, `session-start: team settings check skipped: ${message}`);
return null;
}
}
async function lastUpdateNotice(projectRoot, source) {
if (!isProjectOnboarded(projectRoot)) return null;
try {
const url = new URL('../../dist/update/last-update.js', import.meta.url);
const { takeLastUpdateSessionLine } = await import(url.href);
return takeLastUpdateSessionLine(projectRoot, source);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
appendHooksLog(projectRoot, `session-start: last update notice skipped: ${message}`);
return null;
}
}
async function designSignOffNotice(projectRoot) {
if (!isProjectOnboarded(projectRoot)) return null;
try {
const url = new URL('../../dist/hld/session.js', import.meta.url);
const { waitingSignOffText } = await import(url.href);
return waitingSignOffText(projectRoot);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
appendHooksLog(projectRoot, `session-start: design sign-off check skipped: ${message}`);
return null;
}
}
async function rulesDriftNotice(projectRoot) {
if (!isProjectOnboarded(projectRoot)) return null;
try {
const url = new URL('../../dist/rules-discovery/nudge.js', import.meta.url);
const { rulesDriftSessionStartText } = await import(url.href);
return rulesDriftSessionStartText(projectRoot);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
appendHooksLog(projectRoot, `session-start: rules drift check skipped: ${message}`);
return null;
}
}
let input = '';
process.stdin.on('data', (chunk) => {
input += chunk;
});
process.stdin.on('end', () => {
main(input).then((code) => process.exit(code));
});
process.stdin.resume();