124 lines
3.9 KiB
JavaScript
124 lines
3.9 KiB
JavaScript
#!/usr/bin/env node
|
|
import fs from 'fs';
|
|
|
|
function parseArgs(argv) {
|
|
const args = { input: '', pretty: true };
|
|
for (let i = 2; i < argv.length; i += 1) {
|
|
const arg = argv[i];
|
|
if (arg === '--input') args.input = argv[++i] || '';
|
|
else if (arg === '--compact') args.pretty = false;
|
|
}
|
|
return args;
|
|
}
|
|
|
|
function readInput(path) {
|
|
if (!path || path === '-') {
|
|
return fs.readFileSync(0, 'utf8');
|
|
}
|
|
return fs.readFileSync(path, 'utf8');
|
|
}
|
|
|
|
function normalizeRequest(raw) {
|
|
const data = JSON.parse(raw);
|
|
return {
|
|
requestText: data.requestText || '',
|
|
hasFilesOrSystems: Boolean(data.hasFilesOrSystems),
|
|
needsWaiting: Boolean(data.needsWaiting),
|
|
needsSubagent: Boolean(data.needsSubagent),
|
|
needsOwnerDecision: Boolean(data.needsOwnerDecision),
|
|
canReplyNow: Boolean(data.canReplyNow),
|
|
taskName: data.taskName || 'Untitled long-task',
|
|
currentStep: data.currentStep || 'Classifying request',
|
|
nextStep: data.nextStep || 'Define next actionable step',
|
|
nextReportCondition: data.nextReportCondition || 'After next meaningful milestone',
|
|
waitingOn: data.waitingOn || 'none',
|
|
blocker: data.blocker || 'none',
|
|
checkpointTrigger: data.checkpointTrigger || '',
|
|
externalizedTrigger: data.externalizedTrigger || '',
|
|
triggerKind: data.triggerKind || '',
|
|
};
|
|
}
|
|
|
|
function classify(input) {
|
|
const classification = input.canReplyNow && !input.hasFilesOrSystems && !input.needsWaiting && !input.needsSubagent && !input.needsOwnerDecision
|
|
? 'general_chat'
|
|
: 'long_task';
|
|
|
|
const silentCandidate = classification === 'long_task' && (input.needsWaiting || input.needsSubagent || Boolean(input.checkpointTrigger));
|
|
const needsCheckpoint = classification === 'long_task';
|
|
|
|
return {
|
|
classification,
|
|
silentCandidate,
|
|
needsOwnerDecision: input.needsOwnerDecision,
|
|
needsCheckpoint,
|
|
needsSubagent: input.needsSubagent,
|
|
};
|
|
}
|
|
|
|
function bootstrapTaskState(input, classificationResult) {
|
|
if (classificationResult.classification !== 'long_task') return null;
|
|
return {
|
|
task_name: input.taskName,
|
|
status: input.blocker !== 'none' ? 'blocked' : (input.waitingOn !== 'none' ? 'waiting_user' : 'active'),
|
|
current_step: input.currentStep,
|
|
next_step: input.nextStep,
|
|
next_report_condition: input.nextReportCondition,
|
|
waiting_on: input.waitingOn,
|
|
blocker: input.blocker,
|
|
silent: classificationResult.silentCandidate,
|
|
};
|
|
}
|
|
|
|
function validateSilentLaunch(input, classificationResult) {
|
|
if (!classificationResult.silentCandidate) {
|
|
return { ok: true, reason: 'not a silent long-task' };
|
|
}
|
|
|
|
if (!input.checkpointTrigger) {
|
|
return { ok: false, reason: 'missing first forced checkpoint trigger' };
|
|
}
|
|
|
|
if (!input.externalizedTrigger) {
|
|
return { ok: false, reason: 'missing externalized checkpoint path' };
|
|
}
|
|
|
|
return {
|
|
ok: true,
|
|
reason: `${input.triggerKind || 'externalized'} trigger is defined`,
|
|
};
|
|
}
|
|
|
|
function planHandoff(classificationResult) {
|
|
if (classificationResult.needsOwnerDecision) {
|
|
return { mode: 'button_path' };
|
|
}
|
|
return { mode: 'direct_reply' };
|
|
}
|
|
|
|
function main() {
|
|
const args = parseArgs(process.argv);
|
|
const raw = readInput(args.input);
|
|
const input = normalizeRequest(raw);
|
|
const classificationResult = classify(input);
|
|
const taskRecord = bootstrapTaskState(input, classificationResult);
|
|
const silentLaunch = validateSilentLaunch(input, classificationResult);
|
|
const handoff = planHandoff(classificationResult);
|
|
|
|
const output = {
|
|
classification: classificationResult.classification,
|
|
silentCandidate: classificationResult.silentCandidate,
|
|
needsOwnerDecision: classificationResult.needsOwnerDecision,
|
|
needsCheckpoint: classificationResult.needsCheckpoint,
|
|
needsSubagent: classificationResult.needsSubagent,
|
|
taskRecord,
|
|
silentLaunchOk: silentLaunch.ok,
|
|
silentLaunchReason: silentLaunch.reason,
|
|
handoff,
|
|
};
|
|
|
|
process.stdout.write(JSON.stringify(output, null, args.pretty ? 2 : 0) + '\n');
|
|
}
|
|
|
|
main();
|