atris 3.15.56 → 3.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/GETTING_STARTED.md +1 -1
- package/PERSONA.md +4 -4
- package/README.md +11 -11
- package/atris/skills/copy-editor/SKILL.md +30 -4
- package/atris/skills/improve/SKILL.md +18 -20
- package/atris/wiki/concepts/agent-activation-contract.md +5 -3
- package/atris/wiki/concepts/workspace-initialization-contract.md +4 -4
- package/atris/wiki/index.md +1 -0
- package/ax +522 -73
- package/bin/atris.js +32 -31
- package/commands/align.js +0 -14
- package/commands/apps.js +102 -1
- package/commands/autopilot.js +197 -22
- package/commands/brain.js +219 -34
- package/commands/brainstorm.js +0 -829
- package/commands/computer.js +45 -83
- package/commands/improve.js +501 -0
- package/commands/integrations.js +228 -0
- package/commands/lesson.js +44 -0
- package/commands/member.js +4498 -226
- package/commands/mission.js +302 -27
- package/commands/now.js +89 -1
- package/commands/radar.js +181 -56
- package/commands/skill.js +37 -6
- package/commands/soul.js +0 -4
- package/commands/task.js +5582 -517
- package/commands/terminal.js +14 -10
- package/commands/wiki.js +87 -1
- package/commands/workflow.js +288 -73
- package/commands/worktree.js +52 -15
- package/commands/xp.js +41 -65
- package/lib/auto-accept-certified.js +294 -0
- package/lib/file-ops.js +0 -184
- package/lib/member-alive.js +232 -0
- package/lib/policy-lessons.js +280 -0
- package/lib/receipt-evidence.js +64 -0
- package/lib/state-detection.js +34 -0
- package/lib/task-db.js +568 -16
- package/lib/task-proof.js +43 -0
- package/package.json +1 -1
- package/utils/auth.js +13 -4
- package/commands/research.js +0 -52
- package/lib/section-merge.js +0 -196
package/commands/brainstorm.js
CHANGED
|
@@ -351,744 +351,6 @@ function brainstormAbortError() {
|
|
|
351
351
|
return error;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
function generateWorkflowFile(workflowFile, metadata) {
|
|
355
|
-
const targetDir = path.join(process.cwd(), 'atris');
|
|
356
|
-
|
|
357
|
-
// Load all context needed for agents
|
|
358
|
-
const navigatorFile = path.join(targetDir, 'team', 'navigator.md');
|
|
359
|
-
const executorFile = path.join(targetDir, 'team', 'executor.md');
|
|
360
|
-
const validatorFile = path.join(targetDir, 'team', 'validator.md');
|
|
361
|
-
const launcherFile = path.join(targetDir, 'team', 'launcher.md');
|
|
362
|
-
const personaFile = path.join(targetDir, 'PERSONA.md');
|
|
363
|
-
const mapFile = path.join(targetDir, 'MAP.md');
|
|
364
|
-
const todoFile = path.join(targetDir, 'TODO.md');
|
|
365
|
-
const legacyTaskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
|
|
366
|
-
const { logFile } = getLogPath();
|
|
367
|
-
|
|
368
|
-
const workflow = {
|
|
369
|
-
version: '1.0',
|
|
370
|
-
createdAt: new Date().toISOString(),
|
|
371
|
-
metadata: {
|
|
372
|
-
feature: metadata.feature,
|
|
373
|
-
userStory: metadata.userStory,
|
|
374
|
-
constraints: metadata.constraints || '',
|
|
375
|
-
successCriteria: metadata.successCriteria || [],
|
|
376
|
-
riskNotes: metadata.riskNotes || '',
|
|
377
|
-
journalPath: metadata.logFile
|
|
378
|
-
},
|
|
379
|
-
states: {
|
|
380
|
-
NAVIGATOR: {
|
|
381
|
-
agentSpec: fs.existsSync(navigatorFile) ? fs.readFileSync(navigatorFile, 'utf8') : '',
|
|
382
|
-
context: {
|
|
383
|
-
inboxPath: metadata.logFile,
|
|
384
|
-
taskContextsPath: 'atris/TODO.md',
|
|
385
|
-
mapPath: 'atris/MAP.md'
|
|
386
|
-
},
|
|
387
|
-
instructions: 'Take ideas from Inbox → break them down into perfect, manageable tasks. Create visualizations (ASCII diagrams) for logic flows, DB tables, architecture, UI/UX. Write tasks to TODO.md (formerly TASK_CONTEXTS.md).'
|
|
388
|
-
},
|
|
389
|
-
EXECUTOR: {
|
|
390
|
-
agentSpec: fs.existsSync(executorFile) ? fs.readFileSync(executorFile, 'utf8') : '',
|
|
391
|
-
context: {
|
|
392
|
-
personaPath: 'atris/PERSONA.md',
|
|
393
|
-
mapPath: 'atris/MAP.md',
|
|
394
|
-
taskContextsPath: 'atris/TODO.md'
|
|
395
|
-
},
|
|
396
|
-
instructions: 'Get it done, precisely, following instructions perfectly. Show ASCII visualization for complex changes. Execute tasks following executor spec. Move completed tasks to <completed> section.'
|
|
397
|
-
},
|
|
398
|
-
VALIDATOR: {
|
|
399
|
-
agentSpec: fs.existsSync(validatorFile) ? fs.readFileSync(validatorFile, 'utf8') : '',
|
|
400
|
-
context: {
|
|
401
|
-
taskContextsPath: 'atris/TODO.md',
|
|
402
|
-
mapPath: 'atris/MAP.md',
|
|
403
|
-
journalPath: metadata.logFile
|
|
404
|
-
},
|
|
405
|
-
instructions: 'Auto-activated after "atris do" completes. Ultrathink, check requirements → build → edge cases → errors → integration. Run tests. Repeat until: "✅ All good. Ready for human testing."'
|
|
406
|
-
},
|
|
407
|
-
LAUNCHER: {
|
|
408
|
-
agentSpec: fs.existsSync(launcherFile) ? fs.readFileSync(launcherFile, 'utf8') : '',
|
|
409
|
-
context: {
|
|
410
|
-
taskContextsPath: 'atris/TODO.md',
|
|
411
|
-
mapPath: 'atris/MAP.md',
|
|
412
|
-
journalPath: metadata.logFile
|
|
413
|
-
},
|
|
414
|
-
instructions: 'Ship it clean. Document what was shipped, extract learnings, update MAP.md and docs, clean up, Git commit + push, celebrate!'
|
|
415
|
-
}
|
|
416
|
-
},
|
|
417
|
-
currentState: null,
|
|
418
|
-
currentIteration: 0,
|
|
419
|
-
history: []
|
|
420
|
-
};
|
|
421
|
-
|
|
422
|
-
fs.writeFileSync(workflowFile, JSON.stringify(workflow, null, 2));
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
function updateWorkflowState(workflowFile, stateName, iteration) {
|
|
426
|
-
if (!fs.existsSync(workflowFile)) return;
|
|
427
|
-
|
|
428
|
-
const workflow = JSON.parse(fs.readFileSync(workflowFile, 'utf8'));
|
|
429
|
-
workflow.currentState = stateName;
|
|
430
|
-
workflow.currentIteration = iteration;
|
|
431
|
-
workflow.history.push({
|
|
432
|
-
state: stateName,
|
|
433
|
-
iteration: iteration,
|
|
434
|
-
timestamp: new Date().toISOString()
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
// Update context with latest file contents
|
|
438
|
-
const targetDir = path.join(process.cwd(), 'atris');
|
|
439
|
-
const todoFile = path.join(targetDir, 'TODO.md');
|
|
440
|
-
const legacyTaskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
|
|
441
|
-
const mapFile = path.join(targetDir, 'MAP.md');
|
|
442
|
-
const { logFile } = getLogPath();
|
|
443
|
-
|
|
444
|
-
// Refresh task contexts if exists (prefer TODO.md, fallback to legacy TASK_CONTEXTS.md)
|
|
445
|
-
const taskFilePath = fs.existsSync(todoFile)
|
|
446
|
-
? todoFile
|
|
447
|
-
: (fs.existsSync(legacyTaskContextsFile) ? legacyTaskContextsFile : null);
|
|
448
|
-
if (taskFilePath) {
|
|
449
|
-
workflow.states[stateName].context.taskContexts = fs.readFileSync(taskFilePath, 'utf8').substring(0, 5000); // Limit size
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
// Reference map path (agents read on-demand)
|
|
453
|
-
if (fs.existsSync(mapFile)) {
|
|
454
|
-
workflow.states[stateName].context.mapPath = path.relative(process.cwd(), mapFile);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// Refresh journal inbox if exists
|
|
458
|
-
if (fs.existsSync(logFile)) {
|
|
459
|
-
const logContent = fs.readFileSync(logFile, 'utf8');
|
|
460
|
-
const inboxMatch = logContent.match(/## Inbox\n([\s\S]*?)(?=\n##|$)/);
|
|
461
|
-
if (inboxMatch) {
|
|
462
|
-
workflow.states[stateName].context.inbox = inboxMatch[1].trim().substring(0, 2000); // Limit size
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
fs.writeFileSync(workflowFile, JSON.stringify(workflow, null, 2));
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
async function autopilotAtris(initialIdea = null) {
|
|
470
|
-
const targetDir = path.join(process.cwd(), 'atris');
|
|
471
|
-
if (!fs.existsSync(targetDir)) {
|
|
472
|
-
throw new Error('atris/ folder not found. Run "atris init" first.');
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const navigatorFile = path.join(targetDir, 'team', 'navigator.md');
|
|
476
|
-
const executorFile = path.join(targetDir, 'team', 'executor.md');
|
|
477
|
-
const validatorFile = path.join(targetDir, 'team', 'validator.md');
|
|
478
|
-
const launcherFile = path.join(targetDir, 'team', 'launcher.md');
|
|
479
|
-
|
|
480
|
-
const missingSpecs = [];
|
|
481
|
-
if (!fs.existsSync(navigatorFile)) missingSpecs.push('navigator.md');
|
|
482
|
-
if (!fs.existsSync(executorFile)) missingSpecs.push('executor.md');
|
|
483
|
-
if (!fs.existsSync(validatorFile)) missingSpecs.push('validator.md');
|
|
484
|
-
if (!fs.existsSync(launcherFile)) missingSpecs.push('launcher.md');
|
|
485
|
-
|
|
486
|
-
if (missingSpecs.length > 0) {
|
|
487
|
-
throw new Error(`Missing agent spec(s): ${missingSpecs.join(', ')}. Run "atris init" to restore them.`);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
ensureLogDirectory();
|
|
491
|
-
const { logFile, dateFormatted } = getLogPath();
|
|
492
|
-
if (!fs.existsSync(logFile)) {
|
|
493
|
-
createLogFile(logFile, dateFormatted);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
console.log('');
|
|
497
|
-
console.log('┌─────────────────────────────────────────────────────────────┐');
|
|
498
|
-
console.log(`│ Atris Autopilot v${pkg.version} — Full Cycle Automation │`);
|
|
499
|
-
console.log('│ brainstorm → plan → do → review → launch │');
|
|
500
|
-
console.log('└─────────────────────────────────────────────────────────────┘');
|
|
501
|
-
console.log('');
|
|
502
|
-
console.log(`Date: ${dateFormatted}`);
|
|
503
|
-
console.log('Type "exit" at any prompt to cancel.');
|
|
504
|
-
console.log('');
|
|
505
|
-
|
|
506
|
-
// Detect if running in chat/non-interactive mode
|
|
507
|
-
const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
|
|
508
|
-
const isAutoMode = !isInteractive || !!initialIdea; // Auto-approve if non-interactive or idea provided
|
|
509
|
-
|
|
510
|
-
if (isAutoMode && !initialIdea) {
|
|
511
|
-
console.log('💬 AUTO MODE: Running fully automated workflow.\n');
|
|
512
|
-
} else if (!isInteractive) {
|
|
513
|
-
console.log('💬 CHAT MODE: Autopilot will present prompts here for interactive conversation.');
|
|
514
|
-
console.log(' Respond to prompts in chat to continue the workflow.\n');
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
const rl = isInteractive ? readline.createInterface({
|
|
518
|
-
input: process.stdin,
|
|
519
|
-
output: process.stdout,
|
|
520
|
-
}) : null;
|
|
521
|
-
|
|
522
|
-
const ask = async (promptText, options = {}) => {
|
|
523
|
-
const { allowEmpty = false, defaultValue = null } = options;
|
|
524
|
-
|
|
525
|
-
// In non-interactive mode, never try to use readline - just output and return default
|
|
526
|
-
if (!isInteractive) {
|
|
527
|
-
console.log(`\n📝 PROMPT FOR CHAT: ${promptText}`);
|
|
528
|
-
if (defaultValue !== null) {
|
|
529
|
-
console.log(` ✓ Using default: ${defaultValue}`);
|
|
530
|
-
return defaultValue;
|
|
531
|
-
}
|
|
532
|
-
if (allowEmpty) {
|
|
533
|
-
console.log(' (Empty allowed - continuing)');
|
|
534
|
-
return '';
|
|
535
|
-
}
|
|
536
|
-
// No default and not allowEmpty - use a safe default
|
|
537
|
-
const safeDefault = 'Continue workflow';
|
|
538
|
-
console.log(` ✓ Using safe default: ${safeDefault}`);
|
|
539
|
-
return safeDefault;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
// Interactive mode - auto-approve if in auto mode
|
|
543
|
-
if (isAutoMode && defaultValue !== null) {
|
|
544
|
-
console.log(`\n✓ ${promptText}${defaultValue ? ` → ${defaultValue}` : ' (auto-approved)'}`);
|
|
545
|
-
return defaultValue;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// Only reach here if interactive mode AND rl exists
|
|
549
|
-
if (!rl || rl.closed) {
|
|
550
|
-
// Fallback: if readline closed, use safe default
|
|
551
|
-
if (allowEmpty) return '';
|
|
552
|
-
const safeDefault = 'Continue workflow';
|
|
553
|
-
console.log(` ⚠️ Readline unavailable, using safe default: ${safeDefault}`);
|
|
554
|
-
return safeDefault;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
while (true) {
|
|
558
|
-
try {
|
|
559
|
-
const answer = await new Promise((resolve, reject) => {
|
|
560
|
-
if (rl.closed) {
|
|
561
|
-
reject(new Error('readline was closed'));
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
rl.question(promptText, resolve);
|
|
565
|
-
});
|
|
566
|
-
const trimmed = answer.trim();
|
|
567
|
-
if (trimmed.toLowerCase() === 'exit') {
|
|
568
|
-
throw autopilotAbortError();
|
|
569
|
-
}
|
|
570
|
-
if (!allowEmpty && trimmed === '') {
|
|
571
|
-
console.log('Please enter a value (or type "exit" to abort).');
|
|
572
|
-
continue;
|
|
573
|
-
}
|
|
574
|
-
return trimmed;
|
|
575
|
-
} catch (error) {
|
|
576
|
-
if (error.message === 'readline was closed' || rl.closed) {
|
|
577
|
-
// Readline closed mid-prompt - use safe default
|
|
578
|
-
if (allowEmpty) return '';
|
|
579
|
-
const safeDefault = 'Continue workflow';
|
|
580
|
-
console.log(` ⚠️ Readline closed, using safe default: ${safeDefault}`);
|
|
581
|
-
return safeDefault;
|
|
582
|
-
}
|
|
583
|
-
throw error;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
const askYesNo = async (promptText, defaultYes = true) => {
|
|
589
|
-
// In non-interactive mode, never try readline - just return default
|
|
590
|
-
if (!isInteractive) {
|
|
591
|
-
console.log(`\n📝 PROMPT FOR CHAT: ${promptText}`);
|
|
592
|
-
console.log(` ✓ Auto-approving: ${defaultYes ? 'yes' : 'no'}`);
|
|
593
|
-
return defaultYes;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// Auto mode in interactive terminal - auto-approve
|
|
597
|
-
if (isAutoMode) {
|
|
598
|
-
console.log(`\n✓ ${promptText} → ${defaultYes ? 'yes (auto-approved)' : 'no (auto-approved)'}`);
|
|
599
|
-
return defaultYes;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
while (true) {
|
|
603
|
-
const response = (await ask(promptText)).toLowerCase();
|
|
604
|
-
if (response === 'y' || response === 'yes') return true;
|
|
605
|
-
if (response === 'n' || response === 'no') return false;
|
|
606
|
-
console.log('Please answer with "y" or "n" (or type "exit" to abort).');
|
|
607
|
-
}
|
|
608
|
-
};
|
|
609
|
-
|
|
610
|
-
// ========================================
|
|
611
|
-
// STEP 1: Brainstorm with user
|
|
612
|
-
// ========================================
|
|
613
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
614
|
-
console.log('🧠 STEP 1: Brainstorm — Define the vision');
|
|
615
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
616
|
-
console.log('');
|
|
617
|
-
|
|
618
|
-
let selectedInboxItem = null;
|
|
619
|
-
let topicSummary = '';
|
|
620
|
-
let userStory = '';
|
|
621
|
-
let feelingsVibe = '';
|
|
622
|
-
let constraints = '';
|
|
623
|
-
|
|
624
|
-
// Try to fetch latest journal entry from backend
|
|
625
|
-
let journalContext = '';
|
|
626
|
-
const config = loadConfig();
|
|
627
|
-
const ensured2 = await ensureValidCredentials(apiRequestJson);
|
|
628
|
-
const credentials = ensured2.error ? null : ensured2.credentials;
|
|
629
|
-
|
|
630
|
-
if (config.agent_id && credentials && credentials.token) {
|
|
631
|
-
try {
|
|
632
|
-
const journalResult = await apiRequestJson(`/agents/${config.agent_id}/journal/today`, {
|
|
633
|
-
method: 'GET',
|
|
634
|
-
token: credentials.token,
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
if (journalResult.ok && journalResult.data?.content) {
|
|
638
|
-
journalContext = journalResult.data.content;
|
|
639
|
-
} else {
|
|
640
|
-
const listResult = await apiRequestJson(`/agents/${config.agent_id}/journal/?limit=1`, {
|
|
641
|
-
method: 'GET',
|
|
642
|
-
token: credentials.token,
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
if (listResult.ok && listResult.data?.entries?.length > 0) {
|
|
646
|
-
journalContext = listResult.data.entries[0].content || '';
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
} catch (error) {
|
|
650
|
-
// Fallback to local
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// Fallback to local log file
|
|
655
|
-
if (!journalContext && fs.existsSync(logFile)) {
|
|
656
|
-
journalContext = fs.readFileSync(logFile, 'utf8');
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
try {
|
|
660
|
-
// If initial idea provided, use it directly (skip all prompts)
|
|
661
|
-
if (initialIdea) {
|
|
662
|
-
console.log(`🚀 Initial idea: "${initialIdea}"\n`);
|
|
663
|
-
topicSummary = initialIdea;
|
|
664
|
-
const newId = addInboxIdea(logFile, topicSummary);
|
|
665
|
-
console.log(`✓ Added I${newId} to today's Inbox.\n`);
|
|
666
|
-
selectedInboxItem = { id: newId, text: topicSummary };
|
|
667
|
-
} else {
|
|
668
|
-
// Normal flow: check inbox and prompt
|
|
669
|
-
const initialContent = journalContext || (fs.existsSync(logFile) ? fs.readFileSync(logFile, 'utf8') : '');
|
|
670
|
-
let inboxItems = parseInboxItems(initialContent);
|
|
671
|
-
|
|
672
|
-
if (inboxItems.length > 0) {
|
|
673
|
-
// In non-interactive mode, auto-select first inbox item
|
|
674
|
-
if (!isInteractive) {
|
|
675
|
-
selectedInboxItem = inboxItems[0];
|
|
676
|
-
topicSummary = selectedInboxItem.text;
|
|
677
|
-
console.log(`✓ Auto-selected inbox item I${selectedInboxItem.id}: ${topicSummary}\n`);
|
|
678
|
-
} else {
|
|
679
|
-
console.log('Choose a brainstorm source:');
|
|
680
|
-
console.log(' 1. Select an item from today\'s Inbox');
|
|
681
|
-
console.log(' 2. Enter a new idea');
|
|
682
|
-
console.log('');
|
|
683
|
-
|
|
684
|
-
let choice;
|
|
685
|
-
while (true) {
|
|
686
|
-
choice = await ask('Choice (1-2): ');
|
|
687
|
-
if (choice === '1' || choice === '2') {
|
|
688
|
-
break;
|
|
689
|
-
}
|
|
690
|
-
console.log('Please enter 1 or 2.');
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
if (choice === '1') {
|
|
694
|
-
console.log('');
|
|
695
|
-
console.log('Today\'s Inbox:');
|
|
696
|
-
inboxItems.forEach((item, index) => {
|
|
697
|
-
console.log(` ${index + 1}. I${item.id} — ${item.text}`);
|
|
698
|
-
});
|
|
699
|
-
console.log('');
|
|
700
|
-
|
|
701
|
-
while (true) {
|
|
702
|
-
const selection = await ask(`Pick an item (1-${inboxItems.length}): `);
|
|
703
|
-
const index = parseInt(selection, 10);
|
|
704
|
-
if (!Number.isNaN(index) && index >= 1 && index <= inboxItems.length) {
|
|
705
|
-
selectedInboxItem = inboxItems[index - 1];
|
|
706
|
-
break;
|
|
707
|
-
}
|
|
708
|
-
console.log(`Enter a number between 1 and ${inboxItems.length}.`);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
const editedSummary = await ask('Brainstorm topic (press Enter to keep original): ', { allowEmpty: true });
|
|
712
|
-
topicSummary = editedSummary ? editedSummary : selectedInboxItem.text;
|
|
713
|
-
} else {
|
|
714
|
-
if (!isInteractive) {
|
|
715
|
-
// Non-interactive: use default
|
|
716
|
-
topicSummary = 'New feature idea';
|
|
717
|
-
console.log(`✓ Using default idea: ${topicSummary}\n`);
|
|
718
|
-
} else {
|
|
719
|
-
console.log('');
|
|
720
|
-
topicSummary = await ask('Describe the brainstorm topic: ');
|
|
721
|
-
}
|
|
722
|
-
const newId = addInboxIdea(logFile, topicSummary);
|
|
723
|
-
console.log(`✓ Added I${newId} to today\'s Inbox.`);
|
|
724
|
-
selectedInboxItem = { id: newId, text: topicSummary };
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
} else {
|
|
728
|
-
// No inbox items AND no initial idea → trigger brainstorm mode
|
|
729
|
-
console.log('');
|
|
730
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
731
|
-
console.log('💡 No idea provided. Starting brainstorm mode...');
|
|
732
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
733
|
-
console.log('');
|
|
734
|
-
console.log('🧠 Let\'s shape an idea together. Answer a few questions to get started:');
|
|
735
|
-
console.log('');
|
|
736
|
-
|
|
737
|
-
// Interactive brainstorm session (in chat/non-interactive, use defaults)
|
|
738
|
-
if (!isInteractive || isAutoMode) {
|
|
739
|
-
// Non-interactive or auto mode: use defaults
|
|
740
|
-
topicSummary = 'New feature idea';
|
|
741
|
-
userStory = 'Improved user experience';
|
|
742
|
-
feelingsVibe = '';
|
|
743
|
-
constraints = '';
|
|
744
|
-
console.log('✓ Using default brainstorm values');
|
|
745
|
-
console.log(` Idea: ${topicSummary}`);
|
|
746
|
-
console.log(` Outcome: ${userStory}\n`);
|
|
747
|
-
} else {
|
|
748
|
-
topicSummary = await ask('What problem or feature are you thinking about? (describe it briefly): ');
|
|
749
|
-
if (!topicSummary) {
|
|
750
|
-
throw new Error('Brainstorm cancelled. Provide an idea to continue.');
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
userStory = await ask('What should users experience when this is done? (the outcome): ', {
|
|
754
|
-
allowEmpty: true
|
|
755
|
-
});
|
|
756
|
-
|
|
757
|
-
feelingsVibe = await ask('What vibe/feelings are we aiming for? (optional): ', {
|
|
758
|
-
allowEmpty: true
|
|
759
|
-
});
|
|
760
|
-
|
|
761
|
-
constraints = await ask('Any constraints or guardrails? (optional): ', {
|
|
762
|
-
allowEmpty: true
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
const newId = addInboxIdea(logFile, topicSummary);
|
|
767
|
-
console.log(`✓ Added I${newId} to today's Inbox.`);
|
|
768
|
-
selectedInboxItem = { id: newId, text: topicSummary };
|
|
769
|
-
|
|
770
|
-
console.log('');
|
|
771
|
-
console.log('✓ Idea shaped! Continuing with autopilot...');
|
|
772
|
-
console.log('');
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
const sourceLabel = selectedInboxItem ? `I${selectedInboxItem.id}` : 'Ad-hoc';
|
|
777
|
-
|
|
778
|
-
console.log('');
|
|
779
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
780
|
-
console.log('📖 Craft the Story — What should the output be?');
|
|
781
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
782
|
-
console.log('');
|
|
783
|
-
|
|
784
|
-
// In auto mode or if already set from brainstorm above, skip story prompts
|
|
785
|
-
if (isAutoMode && !userStory) {
|
|
786
|
-
// Extract story from initial idea or use default
|
|
787
|
-
userStory = initialIdea || 'Build the feature completely and correctly';
|
|
788
|
-
feelingsVibe = feelingsVibe || '';
|
|
789
|
-
constraints = constraints || '';
|
|
790
|
-
console.log(`✓ Outcome: ${userStory}`);
|
|
791
|
-
console.log('');
|
|
792
|
-
} else if (!userStory) {
|
|
793
|
-
// Only prompt if we don't already have these from brainstorm mode
|
|
794
|
-
if (!isInteractive) {
|
|
795
|
-
// Non-interactive: use defaults
|
|
796
|
-
userStory = 'Build the feature completely and correctly';
|
|
797
|
-
feelingsVibe = '';
|
|
798
|
-
constraints = '';
|
|
799
|
-
console.log(`✓ Outcome: ${userStory} (using defaults for non-interactive mode)`);
|
|
800
|
-
console.log('');
|
|
801
|
-
} else {
|
|
802
|
-
userStory = await ask('Describe the desired outcome (what should users experience?): ');
|
|
803
|
-
feelingsVibe = await ask('Feelings/vibes we\'re aiming for? (optional): ', { allowEmpty: true });
|
|
804
|
-
constraints = await ask('Constraints or guardrails? (optional): ', { allowEmpty: true });
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
// Generate and show brainstorm prompt (shorter in auto mode)
|
|
809
|
-
if (!isAutoMode) {
|
|
810
|
-
const promptLines = [];
|
|
811
|
-
promptLines.push('You:');
|
|
812
|
-
promptLines.push('');
|
|
813
|
-
promptLines.push(`I want to brainstorm: ${topicSummary}`);
|
|
814
|
-
promptLines.push('');
|
|
815
|
-
|
|
816
|
-
if (userStory) {
|
|
817
|
-
promptLines.push(`The outcome should be: ${userStory}`);
|
|
818
|
-
promptLines.push('');
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
if (feelingsVibe) {
|
|
822
|
-
promptLines.push(`Vibe we\'re going for: ${feelingsVibe}`);
|
|
823
|
-
promptLines.push('');
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
if (constraints) {
|
|
827
|
-
promptLines.push(`Constraints: ${constraints}`);
|
|
828
|
-
promptLines.push('');
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
promptLines.push('Help me uncover what we need to build. Keep responses short (4-5 sentences), pause for alignment, sketch ASCII when structure helps.');
|
|
832
|
-
promptLines.push('');
|
|
833
|
-
promptLines.push('Claude:');
|
|
834
|
-
|
|
835
|
-
const promptText = promptLines.join('\n');
|
|
836
|
-
|
|
837
|
-
console.log('');
|
|
838
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
839
|
-
console.log('📋 PROMPT FOR YOUR CODING EDITOR:');
|
|
840
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
841
|
-
console.log('');
|
|
842
|
-
console.log('```');
|
|
843
|
-
console.log(promptText);
|
|
844
|
-
console.log('```');
|
|
845
|
-
console.log('');
|
|
846
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
847
|
-
console.log('');
|
|
848
|
-
|
|
849
|
-
// Get approval to proceed with autopilot
|
|
850
|
-
const proceed = await askYesNo('✓ Brainstorm complete. Ready to start autopilot (plan → do → review → launch)? (y/n): ');
|
|
851
|
-
if (!proceed) {
|
|
852
|
-
console.log('\nAutopilot cancelled. Brainstorm prompt is ready for your agent.');
|
|
853
|
-
return;
|
|
854
|
-
}
|
|
855
|
-
} else {
|
|
856
|
-
// Auto mode: just show brief summary
|
|
857
|
-
console.log('');
|
|
858
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
859
|
-
console.log('✓ Vision defined — proceeding automatically');
|
|
860
|
-
console.log(` Feature: ${topicSummary}`);
|
|
861
|
-
console.log(` Goal: ${userStory}`);
|
|
862
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
863
|
-
console.log('');
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
// Log brainstorm session
|
|
867
|
-
const sessionSummary = isAutoMode || !isInteractive ? 'Auto brainstorm' : await ask('Brainstorm session summary (1-2 sentences, optional): ', { allowEmpty: true, defaultValue: 'Autopilot brainstorm session' });
|
|
868
|
-
recordBrainstormSession(
|
|
869
|
-
logFile,
|
|
870
|
-
sourceLabel,
|
|
871
|
-
topicSummary,
|
|
872
|
-
userStory,
|
|
873
|
-
[],
|
|
874
|
-
[],
|
|
875
|
-
constraints,
|
|
876
|
-
'',
|
|
877
|
-
feelingsVibe || '',
|
|
878
|
-
[],
|
|
879
|
-
sessionSummary || 'Autopilot brainstorm session'
|
|
880
|
-
);
|
|
881
|
-
|
|
882
|
-
// Define success criteria
|
|
883
|
-
console.log('');
|
|
884
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
885
|
-
console.log('🎯 Define Success Criteria');
|
|
886
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
887
|
-
console.log('');
|
|
888
|
-
|
|
889
|
-
let successCriteria = [];
|
|
890
|
-
let riskNotes = '';
|
|
891
|
-
|
|
892
|
-
if (isAutoMode) {
|
|
893
|
-
// Auto mode: generate basic success criteria
|
|
894
|
-
successCriteria = [
|
|
895
|
-
'Feature implemented and working',
|
|
896
|
-
'Tests pass (if applicable)',
|
|
897
|
-
'Code follows project standards',
|
|
898
|
-
'Documentation updated (MAP.md, journal)'
|
|
899
|
-
];
|
|
900
|
-
console.log('✓ Auto-generated success criteria:');
|
|
901
|
-
successCriteria.forEach((item, index) => {
|
|
902
|
-
console.log(` ${index + 1}. ${item}`);
|
|
903
|
-
});
|
|
904
|
-
console.log('');
|
|
905
|
-
} else {
|
|
906
|
-
while (true) {
|
|
907
|
-
const criteria = await ask(`Success criteria ${successCriteria.length + 1}: `, {
|
|
908
|
-
allowEmpty: successCriteria.length > 0,
|
|
909
|
-
});
|
|
910
|
-
if (!criteria) {
|
|
911
|
-
if (successCriteria.length === 0) {
|
|
912
|
-
console.log('Please provide at least one success criteria.');
|
|
913
|
-
continue;
|
|
914
|
-
}
|
|
915
|
-
break;
|
|
916
|
-
}
|
|
917
|
-
successCriteria.push(criteria);
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
riskNotes = await ask('Any risks or notes? (optional): ', { allowEmpty: true });
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
recordAutopilotVision(
|
|
924
|
-
logFile,
|
|
925
|
-
sourceLabel,
|
|
926
|
-
topicSummary,
|
|
927
|
-
successCriteria,
|
|
928
|
-
riskNotes ? riskNotes : ''
|
|
929
|
-
);
|
|
930
|
-
|
|
931
|
-
console.log('');
|
|
932
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
933
|
-
console.log('✓ Vision locked in');
|
|
934
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
935
|
-
console.log(`• Source: ${sourceLabel}`);
|
|
936
|
-
console.log(`• Summary: ${topicSummary}`);
|
|
937
|
-
console.log('• Success Criteria:');
|
|
938
|
-
successCriteria.forEach((item, index) => {
|
|
939
|
-
console.log(` ${index + 1}. ${item}`);
|
|
940
|
-
});
|
|
941
|
-
if (riskNotes) {
|
|
942
|
-
console.log(`• Notes: ${riskNotes}`);
|
|
943
|
-
}
|
|
944
|
-
console.log('');
|
|
945
|
-
if (isAutoMode) {
|
|
946
|
-
console.log('🚀 AUTO MODE: Running fully automated cycle (plan → do → review → launch)');
|
|
947
|
-
} else {
|
|
948
|
-
console.log('🚀 Starting automated cycle: plan → do → review → launch');
|
|
949
|
-
console.log(' (No manual pauses - fully automated after this approval)');
|
|
950
|
-
}
|
|
951
|
-
console.log('');
|
|
952
|
-
|
|
953
|
-
// ========================================
|
|
954
|
-
// Generate workflow file for coding agents
|
|
955
|
-
// ========================================
|
|
956
|
-
const workflowFile = path.join(targetDir, '.atris-workflow.json');
|
|
957
|
-
generateWorkflowFile(workflowFile, {
|
|
958
|
-
feature: topicSummary,
|
|
959
|
-
userStory,
|
|
960
|
-
constraints,
|
|
961
|
-
successCriteria,
|
|
962
|
-
riskNotes,
|
|
963
|
-
logFile: path.relative(process.cwd(), logFile)
|
|
964
|
-
});
|
|
965
|
-
|
|
966
|
-
console.log('');
|
|
967
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
968
|
-
console.log('📄 WORKFLOW FILE GENERATED: .atris-workflow.json');
|
|
969
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
970
|
-
console.log(' Coding agents can read this file to enter workflow states');
|
|
971
|
-
console.log(' Each state includes all context needed for execution');
|
|
972
|
-
console.log('');
|
|
973
|
-
|
|
974
|
-
// ========================================
|
|
975
|
-
// STEP 2-4: Automated plan → do → review loop
|
|
976
|
-
// ========================================
|
|
977
|
-
let iteration = 1;
|
|
978
|
-
while (true) {
|
|
979
|
-
console.log(`\n${'═'.repeat(70)}`);
|
|
980
|
-
console.log(`AUTOPILOT ITERATION ${iteration}`);
|
|
981
|
-
console.log(`${'═'.repeat(70)}\n`);
|
|
982
|
-
|
|
983
|
-
// Plan
|
|
984
|
-
console.log('[STATE:NAVIGATOR]');
|
|
985
|
-
console.log('[1/4] 📋 Plan — Navigator creating tasks...');
|
|
986
|
-
planAtris();
|
|
987
|
-
updateWorkflowState(workflowFile, 'NAVIGATOR', iteration);
|
|
988
|
-
console.log(' ✓ Planning prompt displayed to agent');
|
|
989
|
-
console.log(' ✓ Workflow state updated: [STATE:NAVIGATOR]\n');
|
|
990
|
-
|
|
991
|
-
// Do
|
|
992
|
-
console.log('[STATE:EXECUTOR]');
|
|
993
|
-
console.log('[2/4] 🔨 Do — Executor building...');
|
|
994
|
-
doAtris();
|
|
995
|
-
updateWorkflowState(workflowFile, 'EXECUTOR', iteration);
|
|
996
|
-
console.log(' ✓ Execution prompt displayed to agent');
|
|
997
|
-
console.log(' ✓ Workflow state updated: [STATE:EXECUTOR]\n');
|
|
998
|
-
|
|
999
|
-
// Review
|
|
1000
|
-
console.log('[STATE:VALIDATOR]');
|
|
1001
|
-
console.log('[3/4] ✅ Review — Validator checking...');
|
|
1002
|
-
reviewAtris();
|
|
1003
|
-
updateWorkflowState(workflowFile, 'VALIDATOR', iteration);
|
|
1004
|
-
console.log(' ✓ Validation prompt displayed to agent');
|
|
1005
|
-
console.log(' ✓ Workflow state updated: [STATE:VALIDATOR]\n');
|
|
1006
|
-
|
|
1007
|
-
// Check if success (auto-approve in auto mode, validator will verify)
|
|
1008
|
-
console.log(`${'─'.repeat(70)}`);
|
|
1009
|
-
let isSuccess;
|
|
1010
|
-
if (isAutoMode) {
|
|
1011
|
-
// In auto mode, assume success if we got through review without errors
|
|
1012
|
-
// Validator should have caught issues already
|
|
1013
|
-
isSuccess = true;
|
|
1014
|
-
console.log('✓ Auto mode: Assuming success (validator verified)');
|
|
1015
|
-
} else {
|
|
1016
|
-
isSuccess = await askYesNo(`Did we meet the success criteria? (y/n): `);
|
|
1017
|
-
}
|
|
1018
|
-
console.log('');
|
|
1019
|
-
|
|
1020
|
-
if (isSuccess) {
|
|
1021
|
-
const successNotes = isAutoMode ? 'Completed via autopilot' : await ask('Notes for the log (optional): ', { allowEmpty: true, defaultValue: 'Completed via autopilot' });
|
|
1022
|
-
recordAutopilotIteration(
|
|
1023
|
-
logFile,
|
|
1024
|
-
iteration,
|
|
1025
|
-
'Success',
|
|
1026
|
-
successNotes ? successNotes : ''
|
|
1027
|
-
);
|
|
1028
|
-
|
|
1029
|
-
console.log('');
|
|
1030
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
1031
|
-
console.log('✓ Success criteria met!');
|
|
1032
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
1033
|
-
console.log('');
|
|
1034
|
-
|
|
1035
|
-
// ========================================
|
|
1036
|
-
// STEP 5: Launch
|
|
1037
|
-
// ========================================
|
|
1038
|
-
console.log('[STATE:LAUNCHER]');
|
|
1039
|
-
console.log('[5/5] 🚀 Launch — Launcher shipping...');
|
|
1040
|
-
console.log(' ✓ Launch instructions are available via "atris" and your journal.');
|
|
1041
|
-
updateWorkflowState(workflowFile, 'LAUNCHER', iteration);
|
|
1042
|
-
console.log(' ✓ Workflow state updated: [STATE:LAUNCHER]\n');
|
|
1043
|
-
|
|
1044
|
-
recordAutopilotSuccess(
|
|
1045
|
-
logFile,
|
|
1046
|
-
selectedInboxItem ? selectedInboxItem.id : null,
|
|
1047
|
-
topicSummary
|
|
1048
|
-
);
|
|
1049
|
-
|
|
1050
|
-
console.log('');
|
|
1051
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
1052
|
-
console.log('🎉 AUTOPILOT COMPLETE');
|
|
1053
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
1054
|
-
console.log('');
|
|
1055
|
-
console.log('✓ Feature completed and shipped');
|
|
1056
|
-
console.log('✓ All prompts displayed for agent workflow');
|
|
1057
|
-
console.log('✓ Success recorded in journal');
|
|
1058
|
-
console.log('');
|
|
1059
|
-
break;
|
|
1060
|
-
} else {
|
|
1061
|
-
const followUp = isAutoMode ? 'Issues detected, needs iteration' : await ask('Describe remaining blockers / next steps (optional): ', {
|
|
1062
|
-
allowEmpty: true,
|
|
1063
|
-
defaultValue: 'Issues detected, needs iteration'
|
|
1064
|
-
});
|
|
1065
|
-
recordAutopilotIteration(
|
|
1066
|
-
logFile,
|
|
1067
|
-
iteration,
|
|
1068
|
-
'Follow-up required',
|
|
1069
|
-
followUp || ''
|
|
1070
|
-
);
|
|
1071
|
-
const continueLoop = await askYesNo('Continue with another iteration? (y/n): ', isAutoMode);
|
|
1072
|
-
if (!continueLoop) {
|
|
1073
|
-
console.log('\nAutopilot paused. Success criteria not yet met.');
|
|
1074
|
-
break;
|
|
1075
|
-
}
|
|
1076
|
-
iteration += 1;
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
} finally {
|
|
1080
|
-
if (rl && isInteractive) {
|
|
1081
|
-
rl.close();
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
function autopilotAbortError() {
|
|
1087
|
-
const error = new Error('Autopilot cancelled by user.');
|
|
1088
|
-
error.__autopilotAbort = true;
|
|
1089
|
-
return error;
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
354
|
function addInboxIdea(logFile, summary) {
|
|
1093
355
|
const content = fs.readFileSync(logFile, 'utf8');
|
|
1094
356
|
const nextId = getNextInboxId(content);
|
|
@@ -1152,56 +414,6 @@ function getNextInboxId(content) {
|
|
|
1152
414
|
return items.reduce((max, item) => (item.id > max ? item.id : max), 0) + 1;
|
|
1153
415
|
}
|
|
1154
416
|
|
|
1155
|
-
function parseCompletionItems(content) {
|
|
1156
|
-
const match = content.match(/## Completed ✅\n([\s\S]*?)(?=\n##|\n---|$)/);
|
|
1157
|
-
if (!match) {
|
|
1158
|
-
return [];
|
|
1159
|
-
}
|
|
1160
|
-
const body = match[1];
|
|
1161
|
-
const lines = body.split('\n');
|
|
1162
|
-
const items = [];
|
|
1163
|
-
lines.forEach((line) => {
|
|
1164
|
-
const trimmed = line.trim();
|
|
1165
|
-
if (!trimmed) return;
|
|
1166
|
-
if (trimmed.startsWith('(Empty')) return;
|
|
1167
|
-
const parsed = trimmed.match(/^- \*\*C(\d+):\*\*\s*(.+)$|^- \*\*C(\d+):\s+(.+)$/);
|
|
1168
|
-
if (parsed) {
|
|
1169
|
-
const id = parseInt(parsed[1] || parsed[3], 10);
|
|
1170
|
-
const text = parsed[2] || parsed[4];
|
|
1171
|
-
items.push({ id, text, line: trimmed });
|
|
1172
|
-
}
|
|
1173
|
-
});
|
|
1174
|
-
return items;
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
function replaceCompletedSection(content, items) {
|
|
1178
|
-
const regex = /(## Completed ✅\n)([\s\S]*?)(\n---|\n##|$)/;
|
|
1179
|
-
if (!regex.test(content)) {
|
|
1180
|
-
const lines = items.length ? items.map((item) => item.line).join('\n') : '';
|
|
1181
|
-
return `${content}\n\n## Completed ✅\n\n${lines}\n`;
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
return content.replace(regex, (match, header, body, suffix) => {
|
|
1185
|
-
const inner = items.length
|
|
1186
|
-
? `\n${items.map((item) => item.line).join('\n')}\n`
|
|
1187
|
-
: '\n';
|
|
1188
|
-
return `${header}${inner}${suffix}`;
|
|
1189
|
-
});
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
function addCompletionItemToContent(content, id, summary) {
|
|
1193
|
-
const items = parseCompletionItems(content).filter((item) => item.id !== id);
|
|
1194
|
-
const newItem = { id, text: summary, line: `- **C${id}:** ${summary}` };
|
|
1195
|
-
const updatedItems = [...items, newItem];
|
|
1196
|
-
return replaceCompletedSection(content, updatedItems);
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
function getNextCompletionId(content) {
|
|
1200
|
-
const items = parseCompletionItems(content);
|
|
1201
|
-
if (items.length === 0) return 1;
|
|
1202
|
-
return items.reduce((max, item) => (item.id > max ? item.id : max), 0) + 1;
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
417
|
function insertIntoNotesSection(content, block) {
|
|
1206
418
|
const regex = /(## Notes\n)([\s\S]*?)(\n---|\n##|$)/;
|
|
1207
419
|
const match = content.match(regex);
|
|
@@ -1225,47 +437,6 @@ function getTimeLabel() {
|
|
|
1225
437
|
return `${hours}:${minutes}`;
|
|
1226
438
|
}
|
|
1227
439
|
|
|
1228
|
-
function recordAutopilotVision(logFile, sourceLabel, summary, successCriteria, riskNotes) {
|
|
1229
|
-
const content = fs.readFileSync(logFile, 'utf8');
|
|
1230
|
-
const lines = [
|
|
1231
|
-
`### Autopilot Vision — ${getTimeLabel()}`,
|
|
1232
|
-
`**Source:** ${sourceLabel}`,
|
|
1233
|
-
`**Summary:** ${summary}`,
|
|
1234
|
-
'**Success Criteria:**',
|
|
1235
|
-
...successCriteria.map((item) => `- ${item}`),
|
|
1236
|
-
];
|
|
1237
|
-
if (riskNotes && riskNotes.trim()) {
|
|
1238
|
-
lines.push(`**Risks / Notes:** ${riskNotes}`);
|
|
1239
|
-
}
|
|
1240
|
-
const block = lines.join('\n');
|
|
1241
|
-
const updated = insertIntoNotesSection(content, block);
|
|
1242
|
-
fs.writeFileSync(logFile, updated);
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
function recordAutopilotIteration(logFile, iteration, result, notes) {
|
|
1246
|
-
const content = fs.readFileSync(logFile, 'utf8');
|
|
1247
|
-
const lines = [
|
|
1248
|
-
`### Autopilot Iteration ${iteration} — ${getTimeLabel()}`,
|
|
1249
|
-
`**Validator Result:** ${result}`,
|
|
1250
|
-
];
|
|
1251
|
-
if (notes && notes.trim()) {
|
|
1252
|
-
lines.push(`**Notes:** ${notes}`);
|
|
1253
|
-
}
|
|
1254
|
-
const block = lines.join('\n');
|
|
1255
|
-
const updated = insertIntoNotesSection(content, block);
|
|
1256
|
-
fs.writeFileSync(logFile, updated);
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
function recordAutopilotSuccess(logFile, inboxId, summary) {
|
|
1260
|
-
let content = fs.readFileSync(logFile, 'utf8');
|
|
1261
|
-
if (typeof inboxId === 'number' && !Number.isNaN(inboxId)) {
|
|
1262
|
-
content = removeInboxItemFromContent(content, inboxId);
|
|
1263
|
-
}
|
|
1264
|
-
const nextId = getNextCompletionId(content);
|
|
1265
|
-
content = addCompletionItemToContent(content, nextId, `Autopilot — ${summary}`);
|
|
1266
|
-
fs.writeFileSync(logFile, content);
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
440
|
function recordBrainstormSession(
|
|
1270
441
|
logFile,
|
|
1271
442
|
sourceLabel,
|