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.
Files changed (44) hide show
  1. package/AGENTS.md +2 -2
  2. package/GETTING_STARTED.md +1 -1
  3. package/PERSONA.md +4 -4
  4. package/README.md +11 -11
  5. package/atris/skills/copy-editor/SKILL.md +30 -4
  6. package/atris/skills/improve/SKILL.md +18 -20
  7. package/atris/wiki/concepts/agent-activation-contract.md +5 -3
  8. package/atris/wiki/concepts/workspace-initialization-contract.md +4 -4
  9. package/atris/wiki/index.md +1 -0
  10. package/ax +522 -73
  11. package/bin/atris.js +32 -31
  12. package/commands/align.js +0 -14
  13. package/commands/apps.js +102 -1
  14. package/commands/autopilot.js +197 -22
  15. package/commands/brain.js +219 -34
  16. package/commands/brainstorm.js +0 -829
  17. package/commands/computer.js +45 -83
  18. package/commands/improve.js +501 -0
  19. package/commands/integrations.js +228 -0
  20. package/commands/lesson.js +44 -0
  21. package/commands/member.js +4498 -226
  22. package/commands/mission.js +302 -27
  23. package/commands/now.js +89 -1
  24. package/commands/radar.js +181 -56
  25. package/commands/skill.js +37 -6
  26. package/commands/soul.js +0 -4
  27. package/commands/task.js +5582 -517
  28. package/commands/terminal.js +14 -10
  29. package/commands/wiki.js +87 -1
  30. package/commands/workflow.js +288 -73
  31. package/commands/worktree.js +52 -15
  32. package/commands/xp.js +41 -65
  33. package/lib/auto-accept-certified.js +294 -0
  34. package/lib/file-ops.js +0 -184
  35. package/lib/member-alive.js +232 -0
  36. package/lib/policy-lessons.js +280 -0
  37. package/lib/receipt-evidence.js +64 -0
  38. package/lib/state-detection.js +34 -0
  39. package/lib/task-db.js +568 -16
  40. package/lib/task-proof.js +43 -0
  41. package/package.json +1 -1
  42. package/utils/auth.js +13 -4
  43. package/commands/research.js +0 -52
  44. package/lib/section-merge.js +0 -196
@@ -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,