UNPKG

paqad-ai

Version:

Spec-driven development framework — AI agents that think before they type

161 lines (141 loc) • 5.96 kB
#!/usr/bin/env node 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();