atris 3.53.0 → 3.55.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 (119) hide show
  1. package/FOR_AGENTS.md +7 -0
  2. package/README.md +1 -1
  3. package/atris/policies/ANTISLOP.md +2 -1
  4. package/atris/skills/copy-editor/SKILL.md +2 -1
  5. package/atris/skills/design/SKILL.md +1 -1
  6. package/atris/skills/x-search/SKILL.md +24 -22
  7. package/atris/skills/youtube/SKILL.md +82 -33
  8. package/atris/team/_template/SOUL.md +40 -0
  9. package/atris/team/validator/MEMBER.md +1 -2
  10. package/bin/atris +21 -0
  11. package/bin/atris.js +563 -373
  12. package/commands/api-key.js +170 -0
  13. package/commands/auth.js +453 -43
  14. package/commands/autoland.js +3 -1
  15. package/commands/autopilot-front.js +58 -11
  16. package/commands/avail.js +5 -4
  17. package/commands/balance.js +55 -0
  18. package/commands/bench.js +29 -1
  19. package/commands/brain.js +63 -12
  20. package/commands/brainstorm.js +18 -0
  21. package/commands/business.js +149 -1
  22. package/commands/chat-scan.js +17 -4
  23. package/commands/close.js +5 -1
  24. package/commands/doctor.js +174 -0
  25. package/commands/dream.js +7 -0
  26. package/commands/drill.js +34 -2
  27. package/commands/engine.js +47 -35
  28. package/commands/errors.js +58 -8
  29. package/commands/experiments.js +14 -0
  30. package/commands/feedback.js +61 -2
  31. package/commands/fleet-report.js +27 -7
  32. package/commands/fleet.js +98 -11
  33. package/commands/founder.js +60 -21
  34. package/commands/human-missions.js +79 -4
  35. package/commands/improve.js +54 -4
  36. package/commands/init.js +68 -92
  37. package/commands/install.js +40 -0
  38. package/commands/integrations.js +399 -30
  39. package/commands/interview.js +8 -0
  40. package/commands/land.js +48 -17
  41. package/commands/learn.js +29 -4
  42. package/commands/log.js +51 -0
  43. package/commands/member.js +68 -10
  44. package/commands/mission.js +287 -50
  45. package/commands/now.js +15 -3
  46. package/commands/orb.js +97 -2
  47. package/commands/pack.js +83 -9
  48. package/commands/playbook.js +381 -0
  49. package/commands/plugin.js +16 -0
  50. package/commands/radar.js +31 -7
  51. package/commands/recap.js +128 -21
  52. package/commands/release.js +102 -2
  53. package/commands/review.js +5 -10
  54. package/commands/revisions.js +55 -0
  55. package/commands/run-front.js +12 -4
  56. package/commands/scout.js +12 -1
  57. package/commands/search.js +123 -28
  58. package/commands/sign.js +2 -2
  59. package/commands/signup.js +12 -3
  60. package/commands/site-deploy.js +17 -4
  61. package/commands/site.js +2 -2
  62. package/commands/skill-eval.js +289 -0
  63. package/commands/skill.js +56 -6
  64. package/commands/social.js +603 -0
  65. package/commands/spaceship.js +69 -6
  66. package/commands/stream.js +8 -1
  67. package/commands/study.js +32 -19
  68. package/commands/sync.js +28 -22
  69. package/commands/task.js +418 -60
  70. package/commands/teach.js +72 -63
  71. package/commands/terminal.js +25 -9
  72. package/commands/topup.js +88 -0
  73. package/commands/truth.js +52 -6
  74. package/commands/usage.js +91 -0
  75. package/commands/verify.js +3 -2
  76. package/commands/version.js +3 -1
  77. package/commands/voice.js +5 -1
  78. package/commands/who.js +69 -11
  79. package/commands/wiki.js +7 -4
  80. package/commands/wish.js +76 -5
  81. package/commands/workflow.js +327 -145
  82. package/commands/worktree.js +1 -0
  83. package/commands/write.js +5 -0
  84. package/commands/x-search.js +340 -0
  85. package/commands/xp.js +5 -1
  86. package/commands/youtube.js +551 -25
  87. package/lib/account-bound.js +47 -0
  88. package/lib/auto-accept-certified.js +8 -1
  89. package/lib/auto-lessons.js +186 -0
  90. package/lib/autoland.js +4 -2
  91. package/lib/chat-log-scan.js +7 -4
  92. package/lib/cli-json.js +77 -0
  93. package/lib/cli-scope.js +26 -0
  94. package/lib/codex-flight.js +1 -0
  95. package/lib/conductor-artifacts.js +1 -1
  96. package/lib/context-gatherer.js +11 -0
  97. package/lib/developer-api.js +116 -0
  98. package/lib/feedback-triage.js +274 -0
  99. package/lib/first-minute.js +398 -0
  100. package/lib/fleet.js +115 -34
  101. package/lib/functional-owner.js +22 -0
  102. package/lib/known-commands.js +31 -5
  103. package/lib/member-scaffold.js +197 -0
  104. package/lib/mission-root.js +4 -2
  105. package/lib/noninteractive.js +88 -0
  106. package/lib/policy-lessons.js +4 -1
  107. package/lib/revision-metric.js +279 -0
  108. package/lib/scratch-root.js +48 -0
  109. package/lib/skill-eval-gate.js +249 -0
  110. package/lib/task-db.js +4 -0
  111. package/lib/task-proof.js +69 -8
  112. package/lib/verifier-quality.js +69 -0
  113. package/lib/wish-audit.js +2 -2
  114. package/lib/wish-delegate.js +10 -1
  115. package/lib/workspace-scaffold.js +270 -0
  116. package/package.json +3 -2
  117. package/scripts/det/checklist-score.js +191 -0
  118. package/scripts/det/ytsearch +31 -0
  119. package/utils/update-check.js +15 -0
@@ -1,6 +1,20 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { getLogPath } = require('../lib/journal');
4
+ const {
5
+ buildFirstMinute,
6
+ folderName,
7
+ freshMinuteJson,
8
+ isCertifiedReview,
9
+ isFreshWorkspace,
10
+ personName,
11
+ pickNext,
12
+ renderWorkspace,
13
+ speakFirstMinute,
14
+ taskCommand,
15
+ } = require('../lib/first-minute');
16
+ const { isNonInteractive } = require('../lib/noninteractive');
17
+ const { loadContext } = require('../lib/state-detection');
4
18
  const { buildToolResultBody } = require('../lib/tool-result-encode');
5
19
 
6
20
  function wrapWorkflowText(text, width = 76) {
@@ -42,6 +56,98 @@ function printWorkflowBrief(lines) {
42
56
  console.log('');
43
57
  }
44
58
 
59
+ function reviewSoftTitle(title, maxWords = 5) {
60
+ const words = String(title || '').replace(/\s+/g, ' ').trim().split(' ').filter(Boolean);
61
+ if (!words.length) return '';
62
+ const text = words.slice(0, maxWords).join(' ').replace(/[.,;:!?]+$/g, '');
63
+ return `"${text.toLowerCase()}"`;
64
+ }
65
+
66
+ function isHumanDeskNext(command) {
67
+ return /^atris task (?:claim|ready|accept)\b/.test(String(command || ''));
68
+ }
69
+
70
+ function loadReviewTasks(root = process.cwd()) {
71
+ try {
72
+ const taskDb = require('../lib/task-db');
73
+ const db = taskDb.open();
74
+ const workspaceRoot = taskDb.workspaceRoot(root);
75
+ const rows = taskDb.listTasks(db, { workspaceRoot, limit: 200 });
76
+ if (Array.isArray(rows) && rows.length) return taskDb.withTaskDisplayRefs(rows);
77
+ } catch {
78
+ // Fall through to the local projection. Tests and fresh folders often have no db.
79
+ }
80
+ const projectionPath = path.join(root, '.atris', 'state', 'tasks.projection.json');
81
+ try {
82
+ const parsed = JSON.parse(fs.readFileSync(projectionPath, 'utf8'));
83
+ return Array.isArray(parsed.tasks) ? parsed.tasks : [];
84
+ } catch {
85
+ return [];
86
+ }
87
+ }
88
+
89
+ function wantsReviewQueue(args = []) {
90
+ const list = Array.isArray(args) ? args : [];
91
+ return list.includes('--json')
92
+ || list.includes('--all')
93
+ || list.includes('--limit')
94
+ || list.includes('--group-by');
95
+ }
96
+
97
+ function renderReviewMinute({
98
+ root = process.cwd(),
99
+ person,
100
+ tasks,
101
+ } = {}) {
102
+ const who = person != null ? person : personName();
103
+ const greet = who ? `hey ${who}, ` : '';
104
+ const all = Array.isArray(tasks) ? tasks : loadReviewTasks(root);
105
+ const reviews = all.filter((task) => task && task.status === 'review');
106
+ const checking = reviews.filter((task) => !isCertifiedReview(task));
107
+ const picked = pickNext({ tasks: reviews, person: who });
108
+ const task = picked.task || null;
109
+ const title = task ? reviewSoftTitle(task.title) : '';
110
+
111
+ if (task && isCertifiedReview(task)) {
112
+ const win = title
113
+ ? `${greet}${title} is waiting for your ok.`
114
+ : `${greet}one finished thing is waiting for your ok.`;
115
+ const lines = [win];
116
+ if (checking.length === 1) lines.push('1 still being checked.');
117
+ if (checking.length > 1) lines.push(`${checking.length} still being checked.`);
118
+ lines.push('');
119
+ lines.push(`next: ${taskCommand(task, who)}`);
120
+ return lines.join('\n');
121
+ }
122
+
123
+ if (checking.length === 1) {
124
+ const named = title || reviewSoftTitle(checking[0] && checking[0].title);
125
+ if (named) return `${greet}${named} is still being checked.`;
126
+ return `${greet}1 finished thing is still being checked.`;
127
+ }
128
+ if (checking.length > 1) {
129
+ return `${greet}${checking.length} finished things are still being checked.`;
130
+ }
131
+
132
+ // Review is the human desk. If first-minute next is claim, ready, or
133
+ // accept, say that same next. "nothing is waiting" is only for an empty desk.
134
+ if (tasks === undefined) {
135
+ const screen = buildFirstMinute({ root, person: who });
136
+ if (isHumanDeskNext(screen.nextCommand)) return screen.text;
137
+ } else {
138
+ const room = pickNext({ tasks: all, person: who });
139
+ if (isHumanDeskNext(room.command)) {
140
+ return renderWorkspace({
141
+ person: who,
142
+ folder: folderName(root),
143
+ task: room.task || null,
144
+ nextCommand: room.command,
145
+ });
146
+ }
147
+ }
148
+ return 'nothing is waiting on you.';
149
+ }
150
+
45
151
  const CONFIDENCE_GATE_LINES = [
46
152
  'Confidence Gate:',
47
153
  '1) Ask: am I factually confident enough to move this forward?',
@@ -375,6 +481,11 @@ async function planAtris(userInput = null) {
375
481
  const args = process.argv.slice(3);
376
482
  const executeFlag = args.includes('--execute');
377
483
  const showFull = args.includes('--full') || args.includes('--verbose');
484
+ const leftoverRequest = args
485
+ .filter((token) => !['--execute', '--full', '--verbose', '--json', '--prompt'].includes(token))
486
+ .join(' ')
487
+ .trim();
488
+ const showPrompt = showFull || args.includes('--prompt');
378
489
 
379
490
  const config = loadConfig();
380
491
  // Auto-enable local execution mode for "2 fast" / "2 pro" product aliases.
@@ -388,21 +499,32 @@ async function planAtris(userInput = null) {
388
499
  return;
389
500
  }
390
501
 
391
- const targetDir = path.join(process.cwd(), 'atris');
392
- const navigatorFile = fs.existsSync(path.join(targetDir, 'team', 'navigator', 'MEMBER.md'))
393
- ? path.join(targetDir, 'team', 'navigator', 'MEMBER.md')
394
- : path.join(targetDir, 'team', 'navigator.md');
502
+ const cwd = process.cwd();
503
+ const targetDir = path.join(cwd, 'atris');
504
+
505
+ // Empty folder talks like bare atris. Missing navigator.md after
506
+ // init --minimal is optional context, not a factory bounce.
507
+ if (!fs.existsSync(targetDir)) {
508
+ if (args.includes('--json')) {
509
+ console.log(JSON.stringify(freshMinuteJson(), null, 2));
510
+ process.exit(2);
511
+ }
512
+ const screen = buildFirstMinute({ root: cwd, fresh: true });
513
+ console.log('');
514
+ console.log(screen.text);
515
+ return;
516
+ }
517
+
518
+ const memberNavigator = path.join(targetDir, 'team', 'navigator', 'MEMBER.md');
519
+ const legacyNavigator = path.join(targetDir, 'team', 'navigator.md');
520
+ const navigatorFile = fs.existsSync(memberNavigator)
521
+ ? memberNavigator
522
+ : (fs.existsSync(legacyNavigator) ? legacyNavigator : null);
395
523
  const personaPath = path.join(targetDir, 'PERSONA.md');
396
524
  const mapFilePath = path.join(targetDir, 'MAP.md');
397
525
  const featuresReadmePath = path.join(targetDir, 'features', 'README.md');
398
526
 
399
- if (!fs.existsSync(navigatorFile)) {
400
- console.log('✗ navigator.md not found. Run "atris init" first.');
401
- process.exit(1);
402
- }
403
-
404
- // Read navigator.md
405
- const navigatorSpec = fs.readFileSync(navigatorFile, 'utf8');
527
+ const navigatorSpec = navigatorFile ? fs.readFileSync(navigatorFile, 'utf8') : '';
406
528
 
407
529
  // Read journal Inbox for context
408
530
  const { logFile } = getLogPath();
@@ -429,14 +551,15 @@ async function planAtris(userInput = null) {
429
551
 
430
552
  // Detect uncertainty in inbox context (or direct user input)
431
553
  const uncertaintySignals = ['not sure', 'maybe', 'but ', 'thinking about', 'uncertain', 'unclear', 'unsure', 'don\'t know'];
432
- const combinedContext = [userInput, inboxContext].filter(Boolean).join('\n');
554
+ const requestText = String(userInput || leftoverRequest || '').trim();
555
+ const combinedContext = [requestText, inboxContext].filter(Boolean).join('\n');
433
556
  const hasUncertainty = combinedContext && uncertaintySignals.some(signal =>
434
557
  combinedContext.toLowerCase().includes(signal)
435
558
  );
436
559
 
437
560
  const taskSourcePath = taskFilePath ? path.relative(process.cwd(), taskFilePath) : null;
438
561
  const journalPath = path.relative(process.cwd(), logFile);
439
- const navigatorPath = path.relative(process.cwd(), navigatorFile);
562
+ const navigatorPath = navigatorFile ? path.relative(process.cwd(), navigatorFile) : null;
440
563
  const personaFileRef = fs.existsSync(personaPath) ? path.relative(process.cwd(), personaPath) : null;
441
564
  const mapFileRef = fs.existsSync(mapFilePath) ? path.relative(process.cwd(), mapFilePath) : null;
442
565
  const featuresReadmeRef = fs.existsSync(featuresReadmePath) ? path.relative(process.cwd(), featuresReadmePath) : null;
@@ -460,7 +583,30 @@ async function planAtris(userInput = null) {
460
583
  .length
461
584
  : 0;
462
585
 
463
- console.log('');
586
+ let firstMinute = null;
587
+ try {
588
+ firstMinute = buildFirstMinute({
589
+ root: cwd,
590
+ context: loadContext(cwd) || {},
591
+ });
592
+ } catch {
593
+ firstMinute = null;
594
+ }
595
+ const showFactory = showPrompt || Boolean(requestText);
596
+
597
+ if (showFactory || executionMode !== 'prompt') {
598
+ console.log(executionMode === 'prompt' ? 'PROMPT ONLY' : 'ACTION TAKEN');
599
+ console.log('');
600
+ if (firstMinute && firstMinute.text) {
601
+ console.log(firstMinute.text);
602
+ console.log('');
603
+ }
604
+ } else if (firstMinute && firstMinute.text) {
605
+ console.log('');
606
+ console.log(firstMinute.text);
607
+ }
608
+
609
+ if (showFactory) {
464
610
  console.log('┌─────────────────────────────────────────────────────────────┐');
465
611
  console.log('│ Atris Plan — Navigator Agent Activated │');
466
612
  console.log('└─────────────────────────────────────────────────────────────┘');
@@ -479,16 +625,16 @@ async function planAtris(userInput = null) {
479
625
  console.log('');
480
626
  }
481
627
 
482
- if (userInput) {
628
+ if (requestText) {
483
629
  console.log('🎯 DIRECT REQUEST:');
484
630
  console.log('─────────────────────────────────────────────────────────────');
485
- console.log(userInput);
631
+ console.log(requestText);
486
632
  console.log('');
487
633
  console.log('─────────────────────────────────────────────────────────────');
488
634
  console.log('');
489
635
  }
490
636
  console.log('📁 CONTEXT FILES (agent should read):');
491
- console.log(`- Navigator spec: ${navigatorPath}`);
637
+ console.log(`- Navigator spec: ${navigatorPath || 'atris/team/navigator/MEMBER.md (missing)'}`);
492
638
  console.log(`- Persona: ${personaFileRef || 'atris/PERSONA.md (missing)'}`);
493
639
  const mapDisplay = mapFileRef
494
640
  ? `${mapFileRef}${mapIsPlaceholder ? ' (placeholder — generate first)' : ''}`
@@ -519,10 +665,12 @@ async function planAtris(userInput = null) {
519
665
  console.log('');
520
666
 
521
667
  if (showFull) {
522
- console.log('📋 NAVIGATOR SPEC (full):');
523
- console.log('─────────────────────────────────────────────────────────────');
524
- console.log(navigatorSpec);
525
- console.log('');
668
+ if (navigatorSpec) {
669
+ console.log('📋 NAVIGATOR SPEC (full):');
670
+ console.log('─────────────────────────────────────────────────────────────');
671
+ console.log(navigatorSpec);
672
+ console.log('');
673
+ }
526
674
  console.log('📥 INBOX CONTEXT (full):');
527
675
  console.log('─────────────────────────────────────────────────────────────');
528
676
  console.log(inboxContext || '(No items in Inbox)');
@@ -540,7 +688,7 @@ async function planAtris(userInput = null) {
540
688
  console.log('You are the Navigator.');
541
689
  console.log('');
542
690
  console.log('Read these files:');
543
- console.log(`- ${navigatorPath}`);
691
+ if (navigatorPath) console.log(`- ${navigatorPath}`);
544
692
  if (personaFileRef) console.log(`- ${personaFileRef}`);
545
693
  if (mapFileRef) console.log(`- ${mapFileRef}`);
546
694
  if (taskSourcePath) console.log(`- ${taskSourcePath}`);
@@ -552,9 +700,9 @@ async function planAtris(userInput = null) {
552
700
  console.log('Note: If `atris/MAP.md` is missing or placeholder, generate it from `atris/atris.md` before writing tasks.');
553
701
  console.log('');
554
702
  }
555
- if (userInput) {
703
+ if (requestText) {
556
704
  console.log('Direct request:');
557
- console.log(userInput);
705
+ console.log(requestText);
558
706
  console.log('');
559
707
  }
560
708
  console.log('Workflow:');
@@ -578,6 +726,7 @@ async function planAtris(userInput = null) {
578
726
  }
579
727
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
580
728
  console.log('');
729
+ }
581
730
 
582
731
  // Check execution mode
583
732
  if (executionMode === 'agent') {
@@ -613,8 +762,8 @@ async function planAtris(userInput = null) {
613
762
  let userPrompt = `You are the Navigator. Take ideas from Inbox → break them down into perfect, manageable tasks.\n\n`;
614
763
  userPrompt += `⚠️ CRITICAL: You MUST create visualizations BEFORE writing tasks!\n\n`;
615
764
 
616
- if (userInput) {
617
- userPrompt += `## DIRECT REQUEST:\n${userInput}\n\n`;
765
+ if (requestText) {
766
+ userPrompt += `## DIRECT REQUEST:\n${requestText}\n\n`;
618
767
  }
619
768
 
620
769
  if (inboxContext) {
@@ -720,22 +869,33 @@ async function doAtris() {
720
869
  const { executeCodeExecution } = require('../utils/claude_sdk');
721
870
  const args = process.argv.slice(3);
722
871
  const executeFlag = args.includes('--execute');
723
- const showFull = args.includes('--full') || args.includes('--verbose');
872
+ const showFull = args.includes('--full') || args.includes('--verbose') || args.includes('--prompt');
724
873
 
725
874
  const config = loadConfig();
726
875
  const executionMode = executeFlag ? 'agent' : (config.execution_mode || 'prompt');
727
876
 
728
877
  const cwd = process.cwd();
729
878
  const targetDir = path.join(cwd, 'atris');
730
- const executorFile = fs.existsSync(path.join(targetDir, 'team', 'executor', 'MEMBER.md'))
731
- ? path.join(targetDir, 'team', 'executor', 'MEMBER.md')
732
- : path.join(targetDir, 'team', 'executor.md');
733
879
 
734
- if (!fs.existsSync(executorFile)) {
735
- console.log('✗ executor.md not found. Run "atris init" first.');
736
- process.exit(1);
880
+ // Empty folder talks like bare atris. Missing executor.md after
881
+ // init --minimal is optional context, not a factory bounce.
882
+ if (!fs.existsSync(targetDir)) {
883
+ if (args.includes('--json')) {
884
+ console.log(JSON.stringify(freshMinuteJson(), null, 2));
885
+ process.exit(2);
886
+ }
887
+ const screen = buildFirstMinute({ root: cwd, fresh: true });
888
+ console.log('');
889
+ console.log(screen.text);
890
+ return;
737
891
  }
738
892
 
893
+ const memberExecutor = path.join(targetDir, 'team', 'executor', 'MEMBER.md');
894
+ const legacyExecutor = path.join(targetDir, 'team', 'executor.md');
895
+ const executorFile = fs.existsSync(memberExecutor)
896
+ ? memberExecutor
897
+ : (fs.existsSync(legacyExecutor) ? legacyExecutor : null);
898
+
739
899
  // Load project profile for context
740
900
  let context = 'ROOT';
741
901
  let profile = null;
@@ -754,8 +914,7 @@ async function doAtris() {
754
914
  }
755
915
  }
756
916
 
757
- // Load executor spec
758
- const executorSpec = fs.readFileSync(executorFile, 'utf8');
917
+ const executorSpec = executorFile ? fs.readFileSync(executorFile, 'utf8') : '';
759
918
 
760
919
  // Load PERSONA.md
761
920
  const personaFile = path.join(targetDir, 'PERSONA.md');
@@ -798,7 +957,7 @@ async function doAtris() {
798
957
  // All tasks available (no tag filtering)
799
958
  const filteredTasks = tasksContent;
800
959
 
801
- const executorPath = path.relative(process.cwd(), executorFile);
960
+ const executorPath = executorFile ? path.relative(process.cwd(), executorFile) : null;
802
961
  const personaFileRef = fs.existsSync(personaFile) ? path.relative(process.cwd(), personaFile) : null;
803
962
  const taskSourcePath = taskFilePath ? path.relative(process.cwd(), taskFilePath) : null;
804
963
  const featuresReadmePath = path.join(targetDir, 'features', 'README.md');
@@ -829,44 +988,35 @@ async function doAtris() {
829
988
 
830
989
  let workspaceSummary = null;
831
990
  try {
832
- const { loadContext } = require('../lib/state-detection');
833
991
  workspaceSummary = loadContext(cwd);
834
992
  } catch {
835
993
  workspaceSummary = null;
836
994
  }
837
995
 
838
- // Prompt-mode output (keep concise by default)
839
- console.log('');
840
- console.log('┌─────────────────────────────────────────────────────────────┐');
841
- console.log('│ Atris Do — Executor Agent Activated │');
842
- console.log(`│ Context: ${context} │`);
843
- console.log('└─────────────────────────────────────────────────────────────┘');
844
- console.log('');
845
-
846
- console.log('📁 CONTEXT FILES (agent should read):');
847
- console.log(`- Executor spec: ${executorPath}`);
848
- console.log(`- Persona: ${personaFileRef || 'atris/PERSONA.md (missing)'}`);
849
- const mapDisplay = mapPath
850
- ? `${mapPath}${mapIsPlaceholder ? ' (placeholder — generate first)' : ''}`
851
- : 'atris/MAP.md (missing)';
852
- console.log(`- MAP: ${mapDisplay}`);
853
- console.log(`- TODO: ${taskSourcePath || 'atris/TODO.md (missing)'}`);
854
- console.log(`- Features index: ${featuresReadmeRef || 'atris/features/README.md (missing)'}`);
855
-
856
- // Show top learnings during execution
996
+ let firstMinute = null;
857
997
  try {
858
- const { loadLearnings } = require('../lib/learnings');
859
- const learnings = loadLearnings().filter(e => e._effectiveConfidence >= 7 && e.insight !== '[REMOVED]').slice(0, 3);
860
- if (learnings.length > 0) {
998
+ firstMinute = buildFirstMinute({
999
+ root: cwd,
1000
+ context: workspaceSummary || {},
1001
+ });
1002
+ } catch {
1003
+ firstMinute = null;
1004
+ }
1005
+ const liveStatus = firstMinute && firstMinute.task && firstMinute.task.status;
1006
+ const hasLiveTask = liveStatus === 'claimed' || liveStatus === 'review' || liveStatus === 'open';
1007
+
1008
+ // Default stays the first-minute two lines. The factory paste stays on --prompt.
1009
+ if (showFull || executionMode !== 'prompt') {
1010
+ console.log(executionMode === 'prompt' ? 'PROMPT ONLY' : 'ACTION TAKEN');
1011
+ console.log('');
1012
+ if (firstMinute && firstMinute.text) {
1013
+ console.log(firstMinute.text);
861
1014
  console.log('');
862
- console.log('🧠 Prior learnings (apply during build):');
863
- for (const l of learnings) {
864
- console.log(` [${l._effectiveConfidence}/10] ${l.type}/${l.key}: ${l.insight}`);
865
- }
866
1015
  }
867
- } catch {}
868
-
869
- console.log('');
1016
+ } else if (firstMinute && firstMinute.text) {
1017
+ console.log('');
1018
+ console.log(firstMinute.text);
1019
+ }
870
1020
 
871
1021
  const backlogCount = workspaceSummary && Array.isArray(workspaceSummary.backlogTasks)
872
1022
  ? workspaceSummary.backlogTasks.length
@@ -875,51 +1025,77 @@ async function doAtris() {
875
1025
  ? workspaceSummary.inProgressFeatures.length
876
1026
  : 0;
877
1027
 
878
- if (inProgressCount > 0) {
879
- console.log(`🔨 In-progress features: ${workspaceSummary.inProgressFeatures.join(', ')}`);
880
- }
881
- console.log(`🧱 Feature build plans found: ${featureBuildPlanRefs.length}`);
882
- if (featureBuildPlanRefs.length > 0) {
883
- featureBuildPlanRefs.slice(0, 3).forEach((ref) => console.log(`- ${ref}`));
884
- if (featureBuildPlanRefs.length > 3) {
885
- console.log(`- ... (+${featureBuildPlanRefs.length - 3} more)`);
1028
+ if (showFull) {
1029
+ console.log('📁 CONTEXT FILES (agent should read):');
1030
+ console.log(`- Executor spec: ${executorPath || 'atris/team/executor/MEMBER.md (missing)'}`);
1031
+ console.log(`- Persona: ${personaFileRef || 'atris/PERSONA.md (missing)'}`);
1032
+ const mapDisplay = mapPath
1033
+ ? `${mapPath}${mapIsPlaceholder ? ' (placeholder — generate first)' : ''}`
1034
+ : 'atris/MAP.md (missing)';
1035
+ console.log(`- MAP: ${mapDisplay}`);
1036
+ console.log(`- TODO: ${taskSourcePath || 'atris/TODO.md (missing)'}`);
1037
+ console.log(`- Features index: ${featuresReadmeRef || 'atris/features/README.md (missing)'}`);
1038
+
1039
+ try {
1040
+ const { loadLearnings } = require('../lib/learnings');
1041
+ const learnings = loadLearnings().filter(e => e._effectiveConfidence >= 7 && e.insight !== '[REMOVED]').slice(0, 3);
1042
+ if (learnings.length > 0) {
1043
+ console.log('');
1044
+ console.log('🧠 Prior learnings (apply during build):');
1045
+ for (const l of learnings) {
1046
+ console.log(` [${l._effectiveConfidence}/10] ${l.type}/${l.key}: ${l.insight}`);
1047
+ }
1048
+ }
1049
+ } catch {}
1050
+
1051
+ console.log('');
1052
+
1053
+ if (inProgressCount > 0) {
1054
+ console.log(`🔨 In-progress features: ${workspaceSummary.inProgressFeatures.join(', ')}`);
886
1055
  }
887
- }
888
- console.log(`📋 Backlog tasks: ${backlogCount}`);
889
- console.log('');
1056
+ console.log(`🧱 Feature build plans found: ${featureBuildPlanRefs.length}`);
1057
+ if (featureBuildPlanRefs.length > 0) {
1058
+ featureBuildPlanRefs.slice(0, 3).forEach((ref) => console.log(`- ${ref}`));
1059
+ if (featureBuildPlanRefs.length > 3) {
1060
+ console.log(`- ... (+${featureBuildPlanRefs.length - 3} more)`);
1061
+ }
1062
+ }
1063
+ if (!(hasLiveTask && backlogCount === 0)) {
1064
+ console.log(`📋 Backlog tasks: ${backlogCount}`);
1065
+ }
1066
+ console.log('');
890
1067
 
891
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
892
- console.log('📋 COPY/PASTE PROMPT FOR YOUR CODING AGENT:');
893
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
894
- console.log('');
895
- console.log('You are the Executor.');
896
- console.log('');
897
- console.log('Read these files:');
898
- console.log(`- ${executorPath}`);
899
- if (personaFileRef) console.log(`- ${personaFileRef}`);
900
- if (mapPath) console.log(`- ${mapPath}`);
901
- if (taskSourcePath) console.log(`- ${taskSourcePath}`);
902
- if (featuresReadmeRef) console.log(`- ${featuresReadmeRef}`);
903
- console.log('');
904
- if (!mapPath || mapIsPlaceholder) {
905
- console.log('Note: If `atris/MAP.md` is missing or placeholder, generate it from `atris/atris.md` before navigating the codebase.');
1068
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
1069
+ console.log('📋 COPY/PASTE PROMPT FOR YOUR CODING AGENT:');
1070
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
1071
+ console.log('');
1072
+ console.log('You are the Executor.');
1073
+ console.log('');
1074
+ console.log('Read these files:');
1075
+ if (executorPath) console.log(`- ${executorPath}`);
1076
+ if (personaFileRef) console.log(`- ${personaFileRef}`);
1077
+ if (mapPath) console.log(`- ${mapPath}`);
1078
+ if (taskSourcePath) console.log(`- ${taskSourcePath}`);
1079
+ if (featuresReadmeRef) console.log(`- ${featuresReadmeRef}`);
1080
+ console.log('');
1081
+ if (!mapPath || mapIsPlaceholder) {
1082
+ console.log('Note: If `atris/MAP.md` is missing or placeholder, generate it from `atris/atris.md` before navigating the codebase.');
1083
+ console.log('');
1084
+ }
1085
+ console.log('Workflow:');
1086
+ console.log('1) Read atris/TODO.md — claim next unclaimed Backlog task');
1087
+ console.log(' Move to ## In Progress: add "Claimed by: executor at YYYY-MM-DD HH:MM"');
1088
+ console.log('2) Run the Confidence Gate against the task before editing');
1089
+ printConfidenceGate(' ');
1090
+ console.log('3) Execute step-by-step. Run tests as you go.');
1091
+ console.log('4) Before completion, rerun the gate against proof and residual risk');
1092
+ console.log('5) When done, move task to ## Completed');
1093
+ console.log('6) Log to atris/team/executor/logs/YYYY-MM-DD.md');
1094
+ console.log(' (Task, Delivered, Errors hit, Learned)');
1095
+ console.log('');
1096
+ console.log('⛔ Do NOT plan — just execute what\'s written.');
906
1097
  console.log('');
907
- }
908
- console.log('Workflow:');
909
- console.log('1) Read atris/TODO.md — claim next unclaimed Backlog task');
910
- console.log(' Move to ## In Progress: add "Claimed by: executor at YYYY-MM-DD HH:MM"');
911
- console.log('2) Run the Confidence Gate against the task before editing');
912
- printConfidenceGate(' ');
913
- console.log('3) Execute step-by-step. Run tests as you go.');
914
- console.log('4) Before completion, rerun the gate against proof and residual risk');
915
- console.log('5) When done, move task to ## Completed');
916
- console.log('6) Log to atris/team/executor/logs/YYYY-MM-DD.md');
917
- console.log(' (Task, Delivered, Errors hit, Learned)');
918
- console.log('');
919
- console.log('⛔ Do NOT plan — just execute what\'s written.');
920
- console.log('');
921
1098
 
922
- if (showFull) {
923
1099
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
924
1100
  console.log('📎 APPENDIX (full context dumps):');
925
1101
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
@@ -932,10 +1108,12 @@ async function doAtris() {
932
1108
  console.log('');
933
1109
  }
934
1110
 
935
- console.log('🔧 EXECUTOR SPEC (full):');
936
- console.log('─────────────────────────────────────────────────────────────');
937
- console.log(executorSpec);
938
- console.log('');
1111
+ if (executorSpec) {
1112
+ console.log('🔧 EXECUTOR SPEC (full):');
1113
+ console.log('─────────────────────────────────────────────────────────────');
1114
+ console.log(executorSpec);
1115
+ console.log('');
1116
+ }
939
1117
 
940
1118
  if (filteredTasks) {
941
1119
  console.log(`📋 TASKS TO EXECUTE (full, from ${taskSource}):`);
@@ -944,16 +1122,14 @@ async function doAtris() {
944
1122
  console.log('');
945
1123
  }
946
1124
 
1125
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
1126
+ if (!hasLiveTask) {
1127
+ console.log('💡 Next: Run "atris review" after execution');
1128
+ }
1129
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
1130
+ console.log('');
947
1131
  }
948
1132
 
949
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
950
- console.log('💡 Next: Run "atris review" after execution');
951
- if (!showFull) {
952
- console.log(' Tip: `atris do --full` prints full spec/context for copy/paste.');
953
- }
954
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
955
- console.log('');
956
-
957
1133
  // Check execution mode
958
1134
  if (executionMode === 'agent') {
959
1135
  // Agent mode: execute via backend API
@@ -1075,41 +1251,44 @@ async function reviewAtris() {
1075
1251
  const executeFlag = args.includes('--execute');
1076
1252
  const showFull = args.includes('--full') || args.includes('--verbose');
1077
1253
  const wantsTaskJson = args.includes('--json');
1254
+ const root = process.cwd();
1255
+ const queueShape = args.some((arg) => arg === '--all' || arg === '--limit' || arg === '--group-by');
1256
+
1257
+ // Empty folder talks like bare atris, including --verbose. After init, stay
1258
+ // in the room. A missing validator spec is optional context, not "run init".
1259
+ if (isFreshWorkspace(root) && !queueShape) {
1260
+ const code = speakFirstMinute({
1261
+ root,
1262
+ fresh: true,
1263
+ asJson: wantsTaskJson,
1264
+ });
1265
+ if (code !== 0) process.exit(code);
1266
+ return;
1267
+ }
1078
1268
 
1079
1269
  if (!executeFlag && !showFull) {
1080
1270
  const forwarded = ['reviews', ...args.filter(arg => !['--execute', '--full', '--verbose'].includes(arg))];
1081
1271
  const { run: runTaskCommand } = require('./task');
1082
- if (!wantsTaskJson) {
1083
- printWorkflowBrief([
1084
- 'Atris Review is the human checkpoint for proof-ready work.',
1085
- 'Accept only when the proof is real; revise when the claim is vague, stale, or too narrow.',
1086
- 'Agents can add review proof here, but XP waits for human accept.',
1087
- ]);
1088
- }
1089
- await runTaskCommand(forwarded);
1090
- if (!wantsTaskJson) {
1091
- printWorkflowBrief([
1092
- 'Need the legacy Validator prompt? Run `atris review --verbose`.',
1093
- ]);
1272
+ if (wantsTaskJson || wantsReviewQueue(args)) {
1273
+ await runTaskCommand(forwarded);
1274
+ return;
1094
1275
  }
1276
+ console.log('');
1277
+ console.log(renderReviewMinute());
1095
1278
  return;
1096
1279
  }
1097
1280
 
1098
1281
  const config = loadConfig();
1099
1282
  const executionMode = executeFlag ? 'agent' : (config.execution_mode || 'prompt');
1100
1283
 
1101
- const targetDir = path.join(process.cwd(), 'atris');
1102
- const validatorFile = fs.existsSync(path.join(targetDir, 'team', 'validator', 'MEMBER.md'))
1103
- ? path.join(targetDir, 'team', 'validator', 'MEMBER.md')
1104
- : path.join(targetDir, 'team', 'validator.md');
1105
-
1106
- if (!fs.existsSync(validatorFile)) {
1107
- console.log('✗ validator.md not found. Run "atris init" first.');
1108
- process.exit(1);
1109
- }
1284
+ const targetDir = path.join(root, 'atris');
1285
+ const memberValidator = path.join(targetDir, 'team', 'validator', 'MEMBER.md');
1286
+ const legacyValidator = path.join(targetDir, 'team', 'validator.md');
1287
+ const validatorFile = fs.existsSync(memberValidator)
1288
+ ? memberValidator
1289
+ : (fs.existsSync(legacyValidator) ? legacyValidator : null);
1110
1290
 
1111
- // Read validator.md
1112
- const validatorSpec = fs.readFileSync(validatorFile, 'utf8');
1291
+ const validatorSpec = validatorFile ? fs.readFileSync(validatorFile, 'utf8') : '';
1113
1292
 
1114
1293
  // Read project-specific testing guide if it exists (optional - projects can add their own)
1115
1294
  // Checks common locations: root, backend/, atris/ directories
@@ -1188,7 +1367,9 @@ async function reviewAtris() {
1188
1367
  }
1189
1368
  })();
1190
1369
 
1191
- const validatorPath = path.relative(process.cwd(), validatorFile);
1370
+ const validatorPath = validatorFile
1371
+ ? path.relative(process.cwd(), validatorFile)
1372
+ : 'atris/team/validator/MEMBER.md (missing)';
1192
1373
  const todoPathRef = taskFilePath ? path.relative(process.cwd(), taskFilePath) : null;
1193
1374
  const journalPathRef = path.relative(process.cwd(), logFile);
1194
1375
  const personaPath = path.join(targetDir, 'PERSONA.md');
@@ -1494,8 +1675,8 @@ async function reviewAtris() {
1494
1675
  }
1495
1676
  }
1496
1677
 
1497
- // Prompt for learnings (skip if stdin is not a TTY)
1498
- if (!process.stdin.isTTY) return;
1678
+ // Prompt for learnings. Headless and forced non-interactive never ask.
1679
+ if (isNonInteractive()) return;
1499
1680
 
1500
1681
  console.log('');
1501
1682
  if (showFull) {
@@ -1648,6 +1829,7 @@ module.exports = {
1648
1829
  planAtris,
1649
1830
  doAtris,
1650
1831
  reviewAtris,
1832
+ renderReviewMinute,
1651
1833
  executeAgentSDKFast,
1652
1834
  makeCloudExecutor,
1653
1835
  postToolResult