atris 3.35.0 → 3.36.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 (133) hide show
  1. package/AGENTS.md +37 -0
  2. package/README.md +5 -3
  3. package/atris/GETTING_STARTED.md +1 -1
  4. package/atris/atris.md +3 -0
  5. package/atris/policies/day-loop-voice.md +102 -0
  6. package/atris/policies/outbound-artifact-gate.md +2 -0
  7. package/atris/skills/design/SKILL.md +56 -32
  8. package/atris/skills/endgame/SKILL.md +12 -6
  9. package/atris/skills/engines/SKILL.md +22 -4
  10. package/atris/skills/fable-method/SKILL.md +66 -0
  11. package/atris/skills/improve/SKILL.md +65 -45
  12. package/atris/skills/youtube/SKILL.md +10 -1
  13. package/atris.md +2 -0
  14. package/ax +147 -19
  15. package/bin/atris.js +565 -265
  16. package/commands/activate.js +194 -88
  17. package/commands/agents.js +166 -0
  18. package/commands/autoland.js +459 -107
  19. package/commands/autopilot-front.js +20 -2
  20. package/commands/autopilot.js +118 -2
  21. package/commands/avail.js +407 -0
  22. package/commands/bench.js +188 -0
  23. package/commands/brain.js +3 -0
  24. package/commands/brief.js +651 -0
  25. package/commands/business-sync.js +192 -6
  26. package/commands/clean.js +50 -24
  27. package/commands/close.js +1083 -0
  28. package/commands/cloud.js +245 -0
  29. package/commands/compile.js +292 -1
  30. package/commands/computer.js +150 -3
  31. package/commands/dream.js +365 -0
  32. package/commands/drill.js +371 -0
  33. package/commands/engine.js +993 -32
  34. package/commands/experiments.js +28 -0
  35. package/commands/feedback.js +34 -12
  36. package/commands/fleet-report.js +206 -0
  37. package/commands/gm.js +23 -0
  38. package/commands/goal.js +247 -0
  39. package/commands/improve.js +642 -26
  40. package/commands/init.js +72 -44
  41. package/commands/interview.js +67 -1
  42. package/commands/land.js +152 -52
  43. package/commands/lifecycle.js +39 -3
  44. package/commands/log.js +84 -1
  45. package/commands/loops.js +220 -16
  46. package/commands/meet.js +220 -0
  47. package/commands/member.js +511 -34
  48. package/commands/mission.js +3029 -339
  49. package/commands/next.js +137 -0
  50. package/commands/now.js +220 -25
  51. package/commands/one-lap.js +776 -0
  52. package/commands/orb.js +314 -0
  53. package/commands/pack-craft.js +179 -0
  54. package/commands/pack.js +823 -0
  55. package/commands/play.js +3 -2
  56. package/commands/probe.js +30 -3
  57. package/commands/pulse.js +241 -46
  58. package/commands/push.js +260 -82
  59. package/commands/rainmaker.js +49 -0
  60. package/commands/report.js +415 -0
  61. package/commands/scout.js +147 -0
  62. package/commands/search.js +363 -0
  63. package/commands/skill.js +47 -3
  64. package/commands/slop.js +50 -2
  65. package/commands/soul.js +1 -1
  66. package/commands/stream.js +861 -0
  67. package/commands/study.js +693 -0
  68. package/commands/sync.js +67 -54
  69. package/commands/task.js +1346 -117
  70. package/commands/team.js +73 -0
  71. package/commands/verify.js +96 -0
  72. package/commands/watch.js +303 -0
  73. package/commands/wish.js +500 -0
  74. package/commands/workflow.js +11 -5
  75. package/commands/worktree.js +234 -13
  76. package/commands/xp.js +29 -11
  77. package/lib/auto-accept-certified.js +331 -34
  78. package/lib/autoland.js +319 -54
  79. package/lib/ax-auto-lane.js +79 -0
  80. package/lib/bench/context.js +147 -0
  81. package/lib/bench/engines.js +141 -0
  82. package/lib/bench/report.js +140 -0
  83. package/lib/bench/runner.js +512 -0
  84. package/lib/brief-ledger.js +350 -0
  85. package/lib/cloud-mission.js +259 -0
  86. package/lib/codex-flight.js +154 -0
  87. package/lib/default-runner.js +45 -0
  88. package/lib/default-verifier.js +70 -0
  89. package/lib/engine-registry.js +232 -0
  90. package/lib/experiments/daily.js +640 -0
  91. package/lib/fleet.js +2219 -67
  92. package/lib/improve-vitals-html.js +171 -0
  93. package/lib/known-commands.js +58 -0
  94. package/lib/loop-doctor.js +416 -0
  95. package/lib/member-switches.js +144 -0
  96. package/lib/mission-room.js +1 -0
  97. package/lib/mission-root.js +52 -0
  98. package/lib/next-moves.js +327 -10
  99. package/lib/one-lap-validator.js +60 -0
  100. package/lib/orb-context.js +477 -0
  101. package/lib/orb-scorecard.js +224 -0
  102. package/lib/policy-lessons.js +52 -1
  103. package/lib/pulse.js +277 -3
  104. package/lib/receipt-block.js +168 -0
  105. package/lib/receipt-evidence.js +65 -4
  106. package/lib/router-brain.js +352 -0
  107. package/lib/runner-command.js +10 -0
  108. package/lib/self-drive.js +258 -0
  109. package/lib/short-name.js +103 -0
  110. package/lib/spawn-env.js +18 -0
  111. package/lib/state-detection.js +56 -1
  112. package/lib/sync-status.js +59 -0
  113. package/lib/task-db.js +108 -29
  114. package/lib/task-proof.js +23 -1
  115. package/lib/team-presence.js +260 -0
  116. package/lib/tool-result-encode.js +7 -0
  117. package/lib/trust-tiers.js +90 -0
  118. package/lib/usage.js +107 -0
  119. package/lib/voice-gate.js +163 -0
  120. package/lib/wish-audit.js +1368 -0
  121. package/lib/wish-delegate.js +1840 -0
  122. package/lib/wish-design.js +110 -0
  123. package/lib/wish-stats.js +183 -0
  124. package/lib/wish-store.js +354 -0
  125. package/lib/zip.js +221 -0
  126. package/package.json +3 -1
  127. package/templates/loops/atris/loops/LOOPS.md +55 -0
  128. package/templates/loops/atris/loops/TICK.md +24 -0
  129. package/templates/loops/atris/loops/feedback.md +22 -0
  130. package/templates/loops/atris/loops/quality.md +22 -0
  131. package/templates/loops/atris/wiki/systems/loops.md +41 -0
  132. package/utils/api.js +5 -1
  133. package/utils/auth.js +57 -21
package/bin/atris.js CHANGED
@@ -65,6 +65,8 @@ const {
65
65
  DEFAULT_CLIENT_ID, DEFAULT_USER_AGENT,
66
66
  } = require('../utils/api');
67
67
  const missionRuntime = require('../lib/mission-runtime-loop');
68
+ const { knownCommands, suggestCommand } = require('../lib/known-commands');
69
+ const { recordUsage } = require('../lib/usage');
68
70
 
69
71
  // Bind DI wrappers (utils/auth uses dependency injection for apiRequestJson)
70
72
  const validateAccessToken = (token) => _validateAccessToken(token, apiRequestJson);
@@ -85,7 +87,8 @@ const helpRequested = updateCommand === 'help'
85
87
  || updateArgs.includes('--help')
86
88
  || updateArgs.includes('-h')
87
89
  || updateArgs[0] === 'help';
88
- const jsonRequested = updateArgs.includes('--json');
90
+ const jsonRequested = process.argv.slice(2).includes('--json');
91
+ const dryRunRequested = updateArgs.includes('--dry-run');
89
92
  const skipUpdateCheck = Boolean(process.env.ATRIS_SKIP_UPDATE_CHECK || process.env.NO_UPDATE_NOTIFIER || helpRequested || jsonRequested);
90
93
  if (!skipUpdateCheck && (!updateCommand || (updateCommand && !['version', 'update'].includes(updateCommand)))) {
91
94
  updateCheckPromise = checkForUpdates()
@@ -107,6 +110,12 @@ let command = process.argv[2];
107
110
  const commandArgs = process.argv.slice(3);
108
111
  const firstCommandArg = process.argv[3];
109
112
  const RUNNER_FLAG_NAMES = ['--runner-bin', '--runner-template', '--runner-model', '--runner-profile'];
113
+ const NATURAL_VALUE_FLAGS = [...RUNNER_FLAG_NAMES, '--engine', '--verify'];
114
+ const SINGLE_WORD_NATURAL_INTENTS = new Set([
115
+ 'add', 'analyze', 'build', 'change', 'check', 'create', 'debug', 'document',
116
+ 'edit', 'fix', 'implement', 'inspect', 'investigate', 'make', 'patch',
117
+ 'refactor', 'remove', 'rename', 'research', 'test', 'update', 'validate', 'write',
118
+ ]);
110
119
 
111
120
  function readOptionArg(args, name) {
112
121
  const prefix = `${name}=`;
@@ -121,6 +130,50 @@ function isOptionValue(args, index, optionNames) {
121
130
  return index > 0 && optionNames.includes(args[index - 1]);
122
131
  }
123
132
 
133
+ function parseNaturalEntryArgs(args = []) {
134
+ const positionals = [];
135
+ let asJson = false;
136
+ let engine = '';
137
+ let verifier = '';
138
+ let error = '';
139
+ for (let i = 0; i < args.length; i += 1) {
140
+ const value = String(args[i] || '');
141
+ if (value === '--json') {
142
+ asJson = true;
143
+ continue;
144
+ }
145
+ const inlineFlag = NATURAL_VALUE_FLAGS.find((name) => value.startsWith(`${name}=`));
146
+ if (inlineFlag) {
147
+ const optionValue = value.slice(inlineFlag.length + 1);
148
+ if (!optionValue) error = `${inlineFlag} needs a value`;
149
+ if (inlineFlag === '--engine') engine = optionValue;
150
+ if (inlineFlag === '--verify') verifier = optionValue;
151
+ continue;
152
+ }
153
+ if (NATURAL_VALUE_FLAGS.includes(value)) {
154
+ const optionValue = String(args[i + 1] || '');
155
+ if (!optionValue || optionValue.startsWith('--')) {
156
+ error = `${value} needs a value`;
157
+ } else {
158
+ if (value === '--engine') engine = optionValue;
159
+ if (value === '--verify') verifier = optionValue;
160
+ i += 1;
161
+ }
162
+ continue;
163
+ }
164
+ positionals.push(value);
165
+ }
166
+ return {
167
+ asJson,
168
+ engine,
169
+ verifier,
170
+ error,
171
+ positionals,
172
+ input: positionals.join(' ').replace(/\s+/g, ' ').trim(),
173
+ multiword: positionals.length > 1 || positionals.some((value) => /\s/.test(value.trim())),
174
+ };
175
+ }
176
+
124
177
  function applyRunnerFlags(args) {
125
178
  // --engine <name> is the operator-facing spelling of --runner-profile:
126
179
  // one flag rents a specific intelligence for this run.
@@ -196,13 +249,14 @@ const isBusinessSyncSafetyCommand = command === 'sync'
196
249
  || firstCommandArg === 'resolve'
197
250
  );
198
251
 
199
- // Auto-sync skills only for commands that modify workspace state
200
- if (['init', 'update', 'upgrade'].includes(command) || (command === 'sync' && !isBusinessSyncSafetyCommand)) {
252
+ // Auto-sync skills only for commands that modify workspace state. Help must
253
+ // stay read-only, including global ~/.claude and ~/.codex skill directories.
254
+ if (!helpRequested && !dryRunRequested && (['init', 'update', 'upgrade'].includes(command) || (command === 'sync' && !isBusinessSyncSafetyCommand))) {
201
255
  try {
202
256
  const { syncSkills } = require('../commands/sync');
203
257
  const skillsUpdated = syncSkills({ silent: true });
204
258
  if (skillsUpdated > 0) {
205
- console.log(`⬆️ ${skillsUpdated} skill${skillsUpdated > 1 ? 's' : ''} updated`);
259
+ console.log(`${skillsUpdated} skill${skillsUpdated > 1 ? 's' : ''} updated`);
206
260
  }
207
261
  } catch (e) {
208
262
  // Non-critical
@@ -345,76 +399,7 @@ function printAtrisGoalBanner(workspaceDir = process.cwd(), label = 'Atris goal'
345
399
  }
346
400
 
347
401
  function showSearchHelp() {
348
- console.log('Usage: atris search <keyword>');
349
- console.log('Example: atris search auth');
350
- }
351
-
352
- function searchJournal(keyword) {
353
- if (!keyword) {
354
- showSearchHelp();
355
- process.exit(1);
356
- }
357
-
358
- if (keyword === '--help' || keyword === '-h') {
359
- showSearchHelp();
360
- process.exit(0);
361
- }
362
-
363
- if (process.argv.slice(4).includes('--help') || process.argv.slice(4).includes('-h')) {
364
- showSearchHelp();
365
- process.exit(1);
366
- }
367
-
368
- const logsDir = path.join(process.cwd(), 'atris', 'logs');
369
- if (!fs.existsSync(logsDir)) {
370
- console.log('No atris/logs/ directory found. Run "atris init" first.');
371
- process.exit(1);
372
- }
373
-
374
- console.log(`Searching for "${keyword}" in atris/logs/**/*.md...\n`);
375
-
376
- const results = [];
377
- const keywordLower = keyword.toLowerCase();
378
-
379
- // Recursively find all .md files in logs directory
380
- function walkDir(dir) {
381
- let files;
382
- try { files = fs.readdirSync(dir); } catch { return; }
383
- for (const file of files) {
384
- try {
385
- const filePath = path.join(dir, file);
386
- const stat = fs.statSync(filePath);
387
- if (stat.isDirectory()) {
388
- walkDir(filePath);
389
- } else if (file.endsWith('.md')) {
390
- const content = fs.readFileSync(filePath, 'utf8');
391
- const lines = content.split('\n');
392
- lines.forEach((line, idx) => {
393
- if (line.toLowerCase().includes(keywordLower)) {
394
- results.push({
395
- file: path.relative(process.cwd(), filePath),
396
- line: idx + 1,
397
- content: line.trim()
398
- });
399
- }
400
- });
401
- }
402
- } catch { /* skip unreadable files */ }
403
- }
404
- }
405
-
406
- walkDir(logsDir);
407
-
408
- if (results.length === 0) {
409
- console.log('No matches found.');
410
- } else {
411
- console.log(`Found ${results.length} match${results.length > 1 ? 'es' : ''}:\n`);
412
- results.forEach(r => {
413
- console.log(`${r.file}:${r.line}`);
414
- console.log(` ${r.content.substring(0, 100)}${r.content.length > 100 ? '...' : ''}`);
415
- console.log('');
416
- });
417
- }
402
+ require('../commands/search').showSearchHelp();
418
403
  }
419
404
 
420
405
  function consoleCmd() {
@@ -453,7 +438,8 @@ function consoleCmd() {
453
438
  function showHelp() {
454
439
  console.log('');
455
440
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
456
- console.log('atris — an operating system for intelligence');
441
+ console.log('atris');
442
+ console.log('you say what you want in plain words. atris builds it, checks it, and shows you proof.');
457
443
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
458
444
  console.log('');
459
445
  console.log('Quick Start:');
@@ -464,11 +450,15 @@ function showHelp() {
464
450
  console.log(' 3. Atris acts with context, memory, tools, and a review loop');
465
451
  console.log('');
466
452
  console.log('Common invocations:');
453
+ console.log(' atris "<request>" Build one isolated change, verify it, and stop in Review');
454
+ console.log(' atris "<request>" --verify "<cmd>" --json');
455
+ console.log(' Supply proof explicitly and return one JSON result');
467
456
  console.log(' atris init [--yes] Global install: initialize this project');
468
457
  console.log(' npx atris init [--yes] Local install: initialize this project');
469
458
  console.log(' atris computer');
470
459
  console.log(' atris business init "My Company"');
471
460
  console.log(' atris run');
461
+ console.log(' atris drill');
472
462
  console.log(' atris status');
473
463
  console.log(' atris soul');
474
464
  console.log(' atris fleet status');
@@ -498,17 +488,26 @@ function showHelp() {
498
488
  console.log('');
499
489
  console.log('Context & tracking:');
500
490
  console.log(' log - Add ideas to inbox');
491
+ console.log(' wish - Say one plain sentence, then Atris asks only for gaps or delegates it');
501
492
  console.log(' now - Show atris/now.md, the current operating truth');
493
+ console.log(' goal - goal, distance, and what moved today (alias: wtf)');
494
+ console.log(' orb - Pick next moves while engine jobs work in the background');
502
495
  console.log(' activate - Load Atris context');
503
496
  console.log(' radar - Show live agents joined with tasks, missions, and worktrees');
497
+ console.log(' stream - Watch the whole team work live in one terminal');
498
+ console.log(' team - Show who is awake, what they are doing, and running loops');
499
+ console.log(' watch - Turn one sentence into an always-on background watcher');
504
500
  console.log(' ctop - Show a process-first live agent CPU/memory view');
505
501
  console.log(' launchpad - Show the next action from local brain, task, mission, and proof state');
502
+ console.log(' brief - Show the one-glance operator brief');
506
503
  console.log(' status - See local work and completions (`atris status <business>` for remote)');
507
504
  console.log(' recap - What your AI team did, in plain English (--share for paste-ready)');
505
+ console.log(' report - Weekly block: landings, journal completions, and Career XP');
508
506
  console.log(' xp - Show Career XP and contribution graph');
509
507
  console.log(' analytics - Show recent productivity from journals');
510
- console.log(' search - Search journal history (atris search <keyword>)');
508
+ console.log(' search - Search workspace memory (atris search <keyword>)');
511
509
  console.log(' clean - Housekeeping (stale tasks, archive journals, broken refs)');
510
+ console.log(' close - track open loops with deadlines and daily escalation');
512
511
  console.log(' harvest - Find bugs and next actions from receipts, run logs, and thinking');
513
512
  console.log(' verify - Validate work is done (tests, MAP.md, changes)');
514
513
  console.log(' task - Local agent task plane (atomic claims, TODO import)');
@@ -518,9 +517,13 @@ function showHelp() {
518
517
  console.log(' ... build ...');
519
518
  console.log(' atris task ready <id> --verify');
520
519
  console.log(' atris autoland tick # second check runs, task lands');
521
- console.log(' mission - Goal + loop + member owner + verifier + receipt');
520
+ console.log(' mission - Goal + loop + member owner + verifier + receipt; --budget quick|long|deep sets bounded tiers');
522
521
  console.log(' release - Tag release, bump version, create GitHub release, draft /launch');
523
522
  console.log(' learn - Project learnings (patterns, pitfalls, preferences)');
523
+ console.log(' study - On-demand learning feed: ingest topic, start server, open browser');
524
+ console.log(' rainmaker - Relationship manager dashboard (atrisos-backend/scripts/rainmaker.py)');
525
+ console.log(' meet - onboard a stranger in one sitting and print their /book link');
526
+ console.log(' avail - Booking availability (/book/{username} weekly windows)');
524
527
  console.log(' brain - Compile MAP/TODO/wiki/state into a loadable agent brain');
525
528
  console.log(' lesson - Append a one-line lesson to atris/lessons.md (mine: distill receipts/episodes/scorecards into policy lessons)');
526
529
  console.log(' ingest - Local-first wiki ingest into atris/wiki/');
@@ -537,7 +540,7 @@ function showHelp() {
537
540
  console.log(' land - The landing: what is actually done vs still in the air; --reap backs up + clears overdue');
538
541
  console.log(' drive - One self-driving tick: mission doctor -> auto-fix -> count disengagements');
539
542
  console.log(' autoland - Approve the policy once; certified work lands itself, you keep irreversible calls');
540
- console.log(' engine - Bring any intelligence: roster of installed coding CLIs, default engine, --engine per run, `engine test` preflight, `engine dispatch <task-id> --engine <name>` one-command claim/build/verify/ship');
543
+ console.log(' engine - Engine registry: list/resolve roles, health flips, default engine, `engine test`, and dispatch flights');
541
544
  console.log(' sign - Co-author trailer on every commit in an atris workspace (on/off/status)');
542
545
  console.log(' visualize - Generate a Slack/deck-ready visual from a prompt');
543
546
  console.log(' youtube - Process YouTube videos with timestamped transcript-first analysis');
@@ -547,6 +550,7 @@ function showHelp() {
547
550
  console.log(' experiments validate - Validate experiment packs');
548
551
  console.log(' experiments run <slug> - Execute a pack or record an Endstate receipt');
549
552
  console.log(' experiments benchmark [m] - Run validate/runtime experiment benchmarks');
553
+ console.log(' bench - run core benchmark gates');
550
554
  console.log('');
551
555
  console.log('Compile loop (learn like AI, run like code):');
552
556
  console.log(' compile record <name> - Append an execution record (--input/--output)');
@@ -562,6 +566,7 @@ function showHelp() {
562
566
  console.log('Sync:');
563
567
  console.log(' pull - Pull journals + member data from cloud');
564
568
  console.log(' push - Push workspace files to cloud');
569
+ console.log(' cloud - Delete cloud files not present locally (cloud clean --dry-run|--yes)');
565
570
  console.log(' live - Keep a business brain fresh (doctor, pull, watch, push)');
566
571
  console.log(' clean-workspace <slug> - Analyze & remove junk files from a workspace (alias: cw)');
567
572
  console.log('');
@@ -569,8 +574,9 @@ function showHelp() {
569
574
  console.log(' browse [query] - Discover workspace templates');
570
575
  console.log(' fork <template> - Clone a template into a new workspace');
571
576
  console.log(' publish - Share your workspace as a template');
572
- console.log(' sleep [business] - Pause workspace compute (context saved)');
573
- console.log(' wake [business] - Resume workspace (agents restart)');
577
+ console.log(' pack - Publish or install an Atris brain zip');
578
+ console.log(' sleep [business|member] [--loop id] - Pause compute or flip a member switch');
579
+ console.log(' wake [business|member] [--loop id] - Resume compute or flip a member switch');
574
580
  console.log('');
575
581
  console.log('Business:');
576
582
  console.log(' business init <name> - Create shared owner + first/default computer');
@@ -597,7 +603,8 @@ function showHelp() {
597
603
  console.log(' console - Start/attach always-on coding console (tmux daemon)');
598
604
  console.log(' soul - Show, snapshot, or fork workspace identity');
599
605
  console.log(' fleet - Inspect local fleet status');
600
- console.log(' loops - Background loops board: what runs, what died, start/stop');
606
+ console.log(' loops - Self-improving loop audit/scaffold (`init`, `audit`, `tick`, `board`)');
607
+ console.log(' self-improve - Alias for `atris loops init`');
601
608
  console.log(' agent - Select cloud agent, spawn worker requests, or run `agent doctor`');
602
609
  console.log(' chat - Chat with Atris 2 Fast in this workspace (--agent for cloud agent; or: atris chat scan)');
603
610
  console.log(' fast - Chat with Atris2 Fast');
@@ -762,12 +769,18 @@ function showVerifyHelp() {
762
769
  console.log('');
763
770
  console.log('Usage: atris verify [task]');
764
771
  console.log('Usage: atris verify <feature-slug> --section <name>');
772
+ console.log('Usage: atris verify artifact <path> [--objective "<text>"] [--min-lines N] [--max-age-hours H] [--json]');
765
773
  console.log('');
766
774
  console.log('Description:');
767
775
  console.log(' Validate workspace health, a specific task, or a feature rubric section.');
776
+ console.log(' The artifact form runs deterministic substance checks on a mission artifact');
777
+ console.log(' (a pre-filter for empty/skeleton/placeholder output, not a quality judgment).');
768
778
  console.log('');
769
779
  console.log('Options:');
770
780
  console.log(' --section <name> Run a fenced bash check from atris/features/<slug>/validate.md.');
781
+ console.log(' --objective <t> Require the artifact to cover the objective vocabulary.');
782
+ console.log(' --min-lines <n> Minimum substantive lines (default 10).');
783
+ console.log(' --max-age-hours <h> Require the artifact to be modified within this window.');
771
784
  console.log(' --help, -h Show this help.');
772
785
  console.log('');
773
786
  }
@@ -968,6 +981,7 @@ const { logAtris: logCmd } = require('../commands/log');
968
981
  const { activateAtris: activateCmd } = require('../commands/activate');
969
982
  const { statusAtris: statusCmd } = require('../commands/status');
970
983
  const { planAtris: planCmd, doAtris: doCmd, reviewAtris: reviewCmd } = require('../commands/workflow');
984
+ const { runOrb: orbCmd } = require('../commands/orb');
971
985
 
972
986
  // All other commands are lazy-loaded inline (require() only when invoked)
973
987
 
@@ -983,12 +997,6 @@ if (command === '2' && ['fast', 'pro'].includes(String(firstCommandArg || '').to
983
997
  }
984
998
 
985
999
  // Check if this is a known command or natural language input
986
- const knownCommands = ['init', 'log', 'now', 'radar', 'ctop', 'launchpad', 'status', 'analytics', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', '_start', 'plan', 'do', 'review', 'release',
987
- 'activate', '_activate', 'agent', 'chat', 'fast', 'ax', 'console', 'serve', 'login', 'logout', 'whoami', 'switch', 'use', 'accounts', '_resolve', '_profile-email', '_switch-session', 'shell-init', 'update', 'upgrade', 'version', 'help', 'next', 'atris',
988
- 'clean', 'harvest', 'verify', 'search', 'skill', 'member', 'codex-goal', 'app', 'apps', 'learn', 'lesson', 'plugin', 'experiments', 'receipt', 'proof', 'openclaw', 'pull', 'push', 'live', 'align', 'terminal', 'computer', 'diff', 'business', 'sync', 'youtube',
989
- 'ingest', 'query', 'lint', 'loop', 'pulse', 'task', 'mission', 'probe', 'worktree', 'land', 'autoland', 'drive', 'aeo', 'slop', 'strings', 'write', 'security-review', 'secure', 'deck', 'site', 'theme', 'card', 'reel', 'improve', 'xp', 'play', 'gm', 'x', 'recap', 'signup', 'clarity', 'interview', 'moves', 'unknowns',
990
- 'github', 'vercel', 'supabase', 'linear', 'stripe', 'gmail', 'calendar', 'twitter', 'slack', 'imessage', 'integrations', 'setup', 'clean-workspace', 'cw',
991
- 'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'loops', 'compile', 'spaceship', 'truth', 'sign', 'engine', 'engines', 'feed'];
992
1000
 
993
1001
  // Check if command is an atris.md spec file - triggers welcome visualization
994
1002
  function isSpecFile(cmd) {
@@ -1043,11 +1051,7 @@ const voiceTriggers = {
1043
1051
  if (!command || !knownCommands.includes(command)) {
1044
1052
  // Check voice triggers before falling through to natural language
1045
1053
  const fullInput = process.argv.slice(2).join(' ').toLowerCase().trim();
1046
- const fullInputWithoutFlags = process.argv.slice(2)
1047
- .filter((arg, index, args) => !String(arg).startsWith('-') && !isOptionValue(args, index, RUNNER_FLAG_NAMES))
1048
- .join(' ')
1049
- .toLowerCase()
1050
- .trim();
1054
+ const fullInputWithoutFlags = parseNaturalEntryArgs(process.argv.slice(2)).input.toLowerCase();
1051
1055
  const triggered = voiceTriggers[fullInput] || voiceTriggers[fullInputWithoutFlags];
1052
1056
  if (triggered) {
1053
1057
  command = triggered;
@@ -1061,28 +1065,36 @@ if (!command || !knownCommands.includes(command)) {
1061
1065
  }
1062
1066
 
1063
1067
  if (!command || !knownCommands.includes(command)) {
1064
- const userInput = process.argv.slice(2).join(' ');
1068
+ const rawNaturalArgs = process.argv.slice(2);
1069
+ const natural = parseNaturalEntryArgs(rawNaturalArgs);
1070
+ const userInput = natural.input;
1071
+ const directSingleWordNatural = !natural.multiword
1072
+ && SINGLE_WORD_NATURAL_INTENTS.has(userInput.toLowerCase());
1065
1073
 
1066
- if (process.argv.includes('--json')) {
1074
+ if (natural.asJson && !natural.multiword && !directSingleWordNatural) {
1067
1075
  console.log(JSON.stringify({
1068
1076
  ok: false,
1069
1077
  error: command ? `unknown command: ${command}` : 'unknown command',
1070
1078
  command: command || null,
1071
- input: userInput,
1079
+ input: rawNaturalArgs.join(' '),
1072
1080
  usage: 'atris help',
1073
1081
  }, null, 2));
1074
1082
  process.exit(2);
1075
1083
  }
1076
1084
 
1077
1085
  // Warn if this looks like a mistyped single-word command (no spaces)
1078
- if (command && !userInput.includes(' ')) {
1086
+ if (command && !natural.multiword && !directSingleWordNatural) {
1079
1087
  console.log(`⚠ Unknown command: "${command}". Run "atris help" for available commands.`);
1088
+ const suggestion = suggestCommand(command);
1089
+ if (suggestion) {
1090
+ console.log(` Did you mean "atris ${suggestion}"?`);
1091
+ }
1080
1092
  console.log(' Treating as natural language input...\n');
1081
1093
  }
1082
1094
 
1083
1095
  // Launch interactive entry (the "Performance")
1084
- interactiveEntry(userInput)
1085
- .then(() => process.exit(0))
1096
+ interactiveEntry(userInput, { oneLap: true, asJson: natural.asJson, engine: natural.engine, verifier: natural.verifier, optionError: natural.error })
1097
+ .then((code) => process.exit(Number.isInteger(code) ? code : 0))
1086
1098
  .catch((error) => {
1087
1099
  console.error(`✗ Error: ${error.message || error}`);
1088
1100
  process.exit(1);
@@ -1090,6 +1102,12 @@ if (!command || !knownCommands.includes(command)) {
1090
1102
  return;
1091
1103
  }
1092
1104
 
1105
+ // Help and previews promise a write-free workspace. Business workspaces
1106
+ // already have .atris/, so ordinary usage telemetry would break that promise.
1107
+ if (!(helpRequested || dryRunRequested)) {
1108
+ recordUsage(command, process.cwd());
1109
+ }
1110
+
1093
1111
  function printAtrisOverview() {
1094
1112
  console.log('');
1095
1113
  console.log('Atris is an AI computer for a workspace.');
@@ -1111,23 +1129,90 @@ function shouldSkipContextGatherer() {
1111
1129
  return !useInteractiveAtrisUi() || initNonInteractiveFlag();
1112
1130
  }
1113
1131
 
1114
- async function interactiveEntry(userInput) {
1132
+ function firstUseCommand() {
1133
+ return 'atris "help me choose the first useful step for this project"';
1134
+ }
1135
+
1136
+ function firstMissionObjective() {
1137
+ return 'Verify this Atris workspace is ready';
1138
+ }
1139
+
1140
+ function localOwnerName() {
1141
+ return process.env.USER || os.userInfo?.().username || 'operator';
1142
+ }
1143
+
1144
+ function firstMissionOwner(root = process.cwd()) {
1145
+ const defaultOwner = path.join(root, 'atris', 'team', 'validator', 'MEMBER.md');
1146
+ return fs.existsSync(defaultOwner) ? 'validator' : localOwnerName();
1147
+ }
1148
+
1149
+ function firstMissionCommand() {
1150
+ return `atris mission start "${firstMissionObjective()}" --owner ${firstMissionOwner()} --runner manual --lane workspace --verify "node -e \\"require('fs').accessSync('atris/atris.md')\\"" --stop "workspace readiness is verified"`;
1151
+ }
1152
+
1153
+ function printFirstUseNext() {
1154
+ const row = (label, value) => ` ${label.padEnd(9)}${value}`;
1155
+ console.log(row('next', 'run `atris` and describe what you want in plain words.'));
1156
+ console.log(`agents: ${firstMissionCommand()}`);
1157
+ console.log(`Then: ${firstUseCommand()}`);
1158
+ }
1159
+
1160
+ function printStarterTaskNext(starter) {
1161
+ console.log('next setup: open atris/MAP.md, then claim the starter task.');
1162
+ if (starter && starter.display_id) {
1163
+ console.log(`Next: atris task claim ${starter.display_id} --as ${localOwnerName()}`);
1164
+ return;
1165
+ }
1166
+ console.log('Next: atris task next --as ' + localOwnerName());
1167
+ }
1168
+
1169
+ async function interactiveEntry(userInput, options = {}) {
1115
1170
  const workspaceDir = process.cwd();
1116
1171
  const state = detectWorkspaceState(workspaceDir);
1117
1172
  const context = loadContext(workspaceDir);
1118
1173
 
1174
+ if (options.asJson && !String(userInput || '').trim()) {
1175
+ console.log(JSON.stringify({
1176
+ schema: 'atris.one_lap.v1',
1177
+ ok: false,
1178
+ status: 'stuck',
1179
+ reason: 'a request is required',
1180
+ next_action: 'atris "<request>" --json',
1181
+ }, null, 2));
1182
+ return 2;
1183
+ }
1184
+
1119
1185
  if (isAtrisMetaQuestion(userInput)) {
1186
+ if (options.asJson) {
1187
+ console.log(JSON.stringify({
1188
+ schema: 'atris.overview.v1',
1189
+ ok: true,
1190
+ product: 'Atris',
1191
+ description: 'An AI computer for a workspace with context, tasks, memory, tools, and proof.',
1192
+ workflow: ['plan', 'do', 'review'],
1193
+ }, null, 2));
1194
+ return 0;
1195
+ }
1120
1196
  printAtrisOverview();
1121
1197
  return;
1122
1198
  }
1123
1199
 
1124
1200
  // Fresh install - offer init
1125
1201
  if (state.state === 'fresh') {
1202
+ if (options.asJson) {
1203
+ console.log(JSON.stringify({
1204
+ schema: 'atris.one_lap.v1',
1205
+ ok: false,
1206
+ status: 'stuck',
1207
+ reason: 'this workspace is not initialized',
1208
+ next_action: 'atris init --yes',
1209
+ }, null, 2));
1210
+ return 2;
1211
+ }
1126
1212
  console.log('\nNo atris/ folder found.');
1127
1213
  console.log('');
1128
- console.log('Start here:');
1129
- console.log(' atris init if Atris is installed globally');
1130
- console.log(' npx atris init if Atris was installed in this project');
1214
+ console.log('Next: atris init');
1215
+ console.log('Local project install instead? Run: npx atris init');
1131
1216
  return;
1132
1217
  }
1133
1218
 
@@ -1155,6 +1240,55 @@ async function interactiveEntry(userInput) {
1155
1240
  // commitment that hasn't been closed yet.
1156
1241
  const activeMissions = loadActiveMissions(workspaceDir);
1157
1242
  const liveMissionsCount = activeMissions.length;
1243
+ const wipCount = inProgressTasksCount + inProgressFeaturesCount;
1244
+ const mapStatus = context.mapStatus || (context.mapExists ? 'ready' : 'missing');
1245
+ const gatherContext = shouldGatherContext({
1246
+ root: workspaceDir,
1247
+ userInput,
1248
+ mapStatus,
1249
+ liveMissionsCount,
1250
+ wipCount,
1251
+ backlogCount,
1252
+ inboxCount,
1253
+ completedTasksCount,
1254
+ });
1255
+
1256
+ if (options.optionError) {
1257
+ const result = {
1258
+ schema: 'atris.one_lap.v1',
1259
+ ok: false,
1260
+ status: 'stuck',
1261
+ reason: options.optionError,
1262
+ next_action: 'atris "<request>" [--engine <id>] [--verify "<cmd>"] [--json]',
1263
+ };
1264
+ if (options.asJson) console.log(JSON.stringify(result, null, 2));
1265
+ else {
1266
+ console.log('lap: stuck');
1267
+ console.log(`why it matters: ${result.reason}`);
1268
+ console.log(`next: ${result.next_action}`);
1269
+ }
1270
+ return 2;
1271
+ }
1272
+
1273
+ if (userInput && mapStatus === 'ready' && !gatherContext && options.oneLap !== false) {
1274
+ return require('../commands/one-lap').runOneLap(userInput, {
1275
+ root: workspaceDir,
1276
+ asJson: options.asJson === true,
1277
+ engine: options.engine || '',
1278
+ verifier: options.verifier || '',
1279
+ });
1280
+ }
1281
+
1282
+ if (options.asJson && userInput) {
1283
+ console.log(JSON.stringify({
1284
+ schema: 'atris.one_lap.v1',
1285
+ ok: false,
1286
+ status: 'stuck',
1287
+ reason: mapStatus !== 'ready' ? 'the workspace map is not ready' : 'first-contact context is required',
1288
+ next_action: mapStatus !== 'ready' ? 'atris init --yes' : 'atris "<first direction>"',
1289
+ }, null, 2));
1290
+ return 2;
1291
+ }
1158
1292
  // Mission needs a tick when: it has a verifier configured AND that verifier
1159
1293
  // hasn't passed yet. Planning-state missions count too — first tick is what
1160
1294
  // moves them to running.
@@ -1164,41 +1298,29 @@ async function interactiveEntry(userInput) {
1164
1298
 
1165
1299
  // Build status line
1166
1300
  const parts = [];
1167
- const wipCount = inProgressTasksCount + inProgressFeaturesCount;
1168
1301
  if (wipCount > 0) {
1169
- parts.push(`WIP: ${wipCount}`);
1302
+ parts.push(`work in progress: ${wipCount}`);
1170
1303
  }
1171
1304
  if (liveMissionsCount > 0) {
1172
- parts.push(`Missions: ${liveMissionsCount}`);
1305
+ parts.push(`missions: ${liveMissionsCount}`);
1173
1306
  }
1174
1307
  if (inboxCount > 0) {
1175
- parts.push(`Inbox: ${inboxCount}`);
1308
+ parts.push(`inbox: ${inboxCount}`);
1176
1309
  }
1177
1310
  if (backlogCount > 0) {
1178
- parts.push(`Backlog: ${backlogCount}`);
1311
+ parts.push(`backlog: ${backlogCount}`);
1179
1312
  }
1180
1313
  if (completedTasksCount > 0) {
1181
- parts.push(`Done: ${completedTasksCount}`);
1314
+ parts.push(`done: ${completedTasksCount}`);
1182
1315
  }
1183
- const statusLine = parts.length > 0 ? parts.join(' | ') : 'Clean slate';
1316
+ const statusLine = parts.length > 0 ? parts.join(' | ') : 'clean slate';
1184
1317
 
1185
1318
  console.log('');
1186
- console.log('┌─────────────────────────────────────────────────────────────┐');
1187
- console.log('│ CONTEXT LOADED │');
1188
- console.log('├─────────────────────────────────────────────────────────────┤');
1189
- console.log(`│ ${statusLine.padEnd(60)}│`);
1190
- console.log('└─────────────────────────────────────────────────────────────┘');
1319
+ const contextRow = (label, value) => ` ${label.padEnd(9)}${value}`;
1320
+ console.log(contextRow('context', 'loaded'));
1321
+ console.log(contextRow('status', statusLine));
1191
1322
 
1192
- const mapStatus = context.mapStatus || (context.mapExists ? 'ready' : 'missing');
1193
- if (shouldGatherContext({
1194
- root: workspaceDir,
1195
- userInput,
1196
- mapStatus,
1197
- liveMissionsCount,
1198
- wipCount,
1199
- backlogCount,
1200
- inboxCount,
1201
- })) {
1323
+ if (gatherContext) {
1202
1324
  const hotAnswer = String(userInput || '').trim();
1203
1325
  if (hotAnswer) {
1204
1326
  const answer = hotAnswer;
@@ -1217,7 +1339,7 @@ async function interactiveEntry(userInput) {
1217
1339
  console.log(`First task: ${starter.title}`);
1218
1340
  }
1219
1341
  if (mapStatus !== 'ready') {
1220
- printMapBootstrap({ userInput: answer, prefix: 'Next setup step' });
1342
+ printStarterTaskNext(starter);
1221
1343
  return;
1222
1344
  }
1223
1345
  await planCmd(answer);
@@ -1225,7 +1347,11 @@ async function interactiveEntry(userInput) {
1225
1347
  }
1226
1348
  if (shouldSkipContextGatherer()) {
1227
1349
  console.log('');
1228
- console.log("context gatherer skipped (non-interactive). run 'atris plan' when you're ready.");
1350
+ if (process.argv.includes('--verbose')) {
1351
+ console.log('context gatherer skipped (non-interactive).');
1352
+ }
1353
+ printFirstUseNext();
1354
+ return;
1229
1355
  } else {
1230
1356
  const answer = await askContextGatherer(workspaceDir);
1231
1357
  if (isAtrisMetaQuestion(answer)) {
@@ -1249,7 +1375,7 @@ async function interactiveEntry(userInput) {
1249
1375
  console.log(`First task: ${starter.title}`);
1250
1376
  }
1251
1377
  if (mapStatus !== 'ready') {
1252
- printMapBootstrap({ userInput: answer, prefix: 'Next setup step' });
1378
+ printStarterTaskNext(starter);
1253
1379
  return;
1254
1380
  }
1255
1381
  await planCmd(answer);
@@ -1384,54 +1510,62 @@ function printMapBootstrap({ userInput, prefix = 'Bootstrap required' } = {}) {
1384
1510
  console.log('');
1385
1511
  }
1386
1512
 
1387
- // ASCII Welcome Visualization
1513
+ // Boot status: plain rows, honest numbers, one next action.
1514
+ // The banner only renders in a real terminal; when a hook or agent captures
1515
+ // this output it stays compact so it costs almost nothing in context.
1388
1516
  function showWelcomeVisualization() {
1389
- const { getTaskCounts } = require('../lib/state-detection');
1517
+ const { getTaskGlance } = require('../lib/state-detection');
1390
1518
  const { readEndgameState } = require('../commands/autopilot');
1391
1519
  const cwd = process.cwd();
1392
1520
  const atrisDir = path.join(cwd, 'atris');
1393
1521
  const projectName = path.basename(cwd);
1522
+ const row = (label, value) => ` ${label.padEnd(9)}${value}`;
1523
+ const sub = (text) => ` ${' '.repeat(9)}- ${text}`;
1524
+ const trimTitle = (t) => (String(t).length > 64 ? `${String(t).slice(0, 61)}...` : String(t));
1394
1525
 
1395
- // Gather workspace stats
1396
- let filesIndexed = 0;
1397
- let tasksInBacklog = 0;
1398
- let tasksInProgress = 0;
1399
- let tasksInReview = 0;
1400
- let tasksCertified = 0;
1526
+ let glance = {
1527
+ backlog: 0, active: 0, review: 0, reviewCertified: 0,
1528
+ activeTitles: [], backlogTitles: [], certifiedTitles: []
1529
+ };
1401
1530
  let journalEntries = 0;
1402
- let hasMap = false;
1403
- let isInitialized = fs.existsSync(atrisDir);
1531
+ let latestBriefName = '';
1532
+ let latestBriefTitle = '';
1533
+ const isInitialized = fs.existsSync(atrisDir);
1404
1534
  let endgameState = { slug: 'unset', horizon: '' };
1405
1535
 
1406
1536
  if (isInitialized) {
1407
- // Check MAP.md
1408
- const mapPath = path.join(atrisDir, 'MAP.md');
1409
- if (fs.existsSync(mapPath)) {
1410
- hasMap = true;
1411
- const mapContent = fs.readFileSync(mapPath, 'utf8');
1412
- // Count file references (lines with file paths)
1413
- const fileRefs = mapContent.match(/`[^`]+\.(js|ts|py|go|rs|md|json|yaml|yml)`/g);
1414
- filesIndexed = fileRefs ? fileRefs.length : 0;
1415
- }
1416
-
1417
- // Task lane counts — DB truth first, TODO.md parse as fallback
1418
1537
  try {
1419
- const counts = getTaskCounts(atrisDir);
1420
- tasksInBacklog = counts.backlog;
1421
- tasksInProgress = counts.active;
1422
- tasksInReview = counts.review;
1423
- tasksCertified = counts.reviewCertified;
1538
+ glance = getTaskGlance(atrisDir);
1424
1539
  } catch {
1425
1540
  // Silently fail - show 0 tasks if reading fails
1426
1541
  }
1427
1542
 
1428
- // Read endgame state
1429
1543
  try {
1430
1544
  endgameState = readEndgameState(cwd);
1431
1545
  } catch {
1432
1546
  // Silently fail - show unset if reading fails
1433
1547
  }
1434
1548
 
1549
+ try {
1550
+ const briefsDir = path.join(atrisDir, 'wiki', 'briefs');
1551
+ latestBriefName = fs.readdirSync(briefsDir, { withFileTypes: true })
1552
+ .filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
1553
+ .map((entry) => ({ name: entry.name, mtimeMs: fs.statSync(path.join(briefsDir, entry.name)).mtimeMs }))
1554
+ .sort((a, b) => b.mtimeMs - a.mtimeMs || a.name.localeCompare(b.name))[0]?.name || '';
1555
+ if (latestBriefName) {
1556
+ // Prefer the brief's own H1 over its filename; strip miner boilerplate
1557
+ // like "YouTube brief: <slug>" so boot reads like a sentence.
1558
+ const head = fs.readFileSync(path.join(briefsDir, latestBriefName), 'utf8').slice(0, 2000);
1559
+ const h1 = head.split('\n').find((line) => line.startsWith('# '));
1560
+ let title = h1 ? h1.slice(2).trim() : '';
1561
+ title = title.replace(/^youtube brief:\s*/i, '').trim();
1562
+ const slug = path.basename(latestBriefName, '.md');
1563
+ latestBriefTitle = title && title.toLowerCase() !== slug.toLowerCase() ? title : slug;
1564
+ }
1565
+ } catch {
1566
+ // Briefs are optional, so missing or unreadable directories stay silent.
1567
+ }
1568
+
1435
1569
  // Count journal entries today
1436
1570
  const today = new Date();
1437
1571
  const year = today.getFullYear();
@@ -1446,119 +1580,136 @@ function showWelcomeVisualization() {
1446
1580
  }
1447
1581
 
1448
1582
  console.log('');
1449
- console.log(' ╭──────────────────────────────────────────╮');
1450
- console.log(' │ │');
1451
- console.log(' │ █████╗ ████████╗██████╗ ██╗███████╗ │');
1452
- console.log(' │ ██╔══██╗╚══██╔══╝██╔══██╗██║██╔════╝ │');
1453
- console.log(' │ ███████║ ██║ ██████╔╝██║███████╗ │');
1454
- console.log(' │ ██╔══██║ ██║ ██╔══██╗██║╚════██║ │');
1455
- console.log(' │ ██║ ██║ ██║ ██║ ██║██║███████║ │');
1456
- console.log(' │ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝ │');
1457
- console.log(' │ │');
1458
- console.log(' ╰──────────────────────────────────────────╯');
1583
+ if (process.stdout.isTTY) {
1584
+ console.log(' ╭──────────────────────────────────────────╮');
1585
+ console.log(' │ │');
1586
+ console.log(' │ █████╗ ████████╗██████╗ ██╗███████╗ │');
1587
+ console.log(' │ ██╔══██╗╚══██╔══╝██╔══██╗██║██╔════╝ │');
1588
+ console.log(' │ ███████║ ██║ ██████╔╝██║███████╗ │');
1589
+ console.log(' │ ██╔══██║ ██║ ██╔══██╗██║╚════██║ │');
1590
+ console.log(' │ ██║ ██║ ██║ ██║ ██║██║███████║ │');
1591
+ console.log(' │ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝ │');
1592
+ console.log(' │ │');
1593
+ console.log(' ╰──────────────────────────────────────────╯');
1594
+ console.log('');
1595
+ }
1596
+ console.log(` atris v${CLI_VERSION} · ${projectName}`);
1459
1597
  console.log('');
1460
1598
 
1461
1599
  if (!isInitialized) {
1462
- console.log(' ⚡ Spec detected. No workspace found.');
1600
+ console.log(' no atris workspace here yet.');
1463
1601
  console.log('');
1464
- console.log(' ┌─ READY TO INITIALIZE ────────────────────┐');
1465
- console.log(' │ │');
1466
- console.log(` │ 📍 Project: ${projectName.substring(0, 25).padEnd(25)}│`);
1467
- console.log(` │ 📄 Spec: atris.md v${CLI_VERSION.padEnd(18)}│`);
1468
- console.log(' │ │');
1469
- console.log(' │ Run "atris init" to create workspace │');
1470
- console.log(' │ │');
1471
- console.log(' └──────────────────────────────────────────┘');
1472
- } else {
1473
- console.log(' ⚡ Scanning spec...');
1602
+ console.log(row('next', 'atris init (set up this folder)'));
1474
1603
  console.log('');
1475
- console.log(' ┌─ WORKSPACE DETECTED ─────────────────────┐');
1476
- console.log(' │ │');
1477
- console.log(` │ 📍 Project: ${projectName.substring(0, 25).padEnd(25)}│`);
1478
- console.log(` │ 📄 Spec: atris.md v${CLI_VERSION.padEnd(18)}│`);
1479
- console.log(` │ 🗺️ Map: ${hasMap ? (filesIndexed + ' files indexed').padEnd(26) : 'not generated yet'.padEnd(26)}│`);
1480
- console.log(` │ 📋 Tasks: ${(tasksInBacklog + ' backlog, ' + tasksInProgress + ' active').padEnd(26)}│`);
1481
- if (tasksInReview > 0) {
1482
- const reviewText = tasksCertified > 0
1483
- ? `${tasksInReview} waiting (${tasksCertified} certified)`
1484
- : `${tasksInReview} waiting`;
1485
- console.log(` │ ⏳ Review: ${reviewText.padEnd(26)}│`);
1604
+ return;
1605
+ }
1606
+
1607
+ if (latestBriefName) {
1608
+ console.log(` learned \"${latestBriefTitle}\" overnight -> atris/wiki/briefs/${latestBriefName}`);
1609
+ }
1610
+
1611
+ // Show the work itself, not counts. A newcomer in any domain (code, docs,
1612
+ // a travel plan) should read actual task names and know what's happening.
1613
+ // Waiting-on-you comes first: the one thing only a human can do.
1614
+ if (glance.reviewCertified > 0) {
1615
+ console.log(row('you', `${glance.reviewCertified} done, waiting for your ok:`));
1616
+ glance.certifiedTitles.forEach((t) => console.log(sub(trimTitle(t))));
1617
+ }
1618
+
1619
+ if (glance.active > 0) {
1620
+ console.log(row('now', trimTitle(glance.activeTitles[0] || 'work moving')));
1621
+ glance.activeTitles.slice(1).forEach((t) => console.log(sub(trimTitle(t))));
1622
+ const tail = [];
1623
+ const moreActive = glance.active - glance.activeTitles.length;
1624
+ if (moreActive > 0) tail.push(`${moreActive} more moving`);
1625
+ if (glance.backlog > 0) tail.push(`${glance.backlog} waiting to start`);
1626
+ if (glance.review > 0) tail.push(`${glance.review} getting a final look`);
1627
+ if (tail.length) console.log(` ${' '.repeat(9)}...and ${tail.join(', ')}`);
1628
+ } else if (glance.backlog > 0) {
1629
+ console.log(row('soon', trimTitle(glance.backlogTitles[0] || 'work queued')));
1630
+ glance.backlogTitles.slice(1).forEach((t) => console.log(sub(trimTitle(t))));
1631
+ const moreBacklog = glance.backlog - glance.backlogTitles.length;
1632
+ if (moreBacklog > 0) console.log(` ${' '.repeat(9)}...and ${moreBacklog} more waiting`);
1633
+ } else {
1634
+ console.log(row('now', 'nothing on the list yet'));
1635
+ }
1636
+
1637
+ // landSummary is expensive (git board classification) - compute once per boot.
1638
+ let landInfo = null;
1639
+ try { landInfo = require('../commands/land').landSummary(cwd); } catch (err) { landInfo = null; }
1640
+ let rotInfo = null;
1641
+ try {
1642
+ const { parseLessons } = require('../lib/memory-view');
1643
+ const resolved = path.resolve(cwd);
1644
+ const parent = path.dirname(resolved);
1645
+ const grandparent = path.dirname(parent);
1646
+ let worktreeDir;
1647
+ if (path.basename(grandparent) === '.agent-worktrees') {
1648
+ worktreeDir = parent;
1649
+ } else {
1650
+ worktreeDir = path.join(path.dirname(resolved), '.agent-worktrees', path.basename(resolved));
1486
1651
  }
1487
- let landInfo = null;
1488
- try { landInfo = require('../commands/land').landSummary(process.cwd()); } catch (err) { landInfo = null; }
1489
- if (landInfo && landInfo.branches > 0) {
1490
- const landText = `${landInfo.branches} in the air, ${landInfo.due} overdue`;
1491
- console.log(` │ 🛬 Land: ${landText.padEnd(26)}│`);
1652
+ let worktrees = 0;
1653
+ if (fs.existsSync(worktreeDir)) {
1654
+ worktrees = fs.readdirSync(worktreeDir, { withFileTypes: true })
1655
+ .filter((entry) => entry.isDirectory()).length;
1492
1656
  }
1493
- let rotInfo = null;
1657
+ let lessonsText = '';
1494
1658
  try {
1495
- const { parseLessons } = require('../lib/memory-view');
1496
- const resolved = path.resolve(cwd);
1497
- const parent = path.dirname(resolved);
1498
- const grandparent = path.dirname(parent);
1499
- let worktreeDir;
1500
- if (path.basename(grandparent) === '.agent-worktrees') {
1501
- worktreeDir = parent;
1502
- } else {
1503
- worktreeDir = path.join(path.dirname(resolved), '.agent-worktrees', path.basename(resolved));
1504
- }
1505
- let worktrees = 0;
1506
- if (fs.existsSync(worktreeDir)) {
1507
- worktrees = fs.readdirSync(worktreeDir, { withFileTypes: true })
1508
- .filter((entry) => entry.isDirectory()).length;
1509
- }
1510
- let lessonsText = '';
1511
- try {
1512
- lessonsText = fs.readFileSync(path.join(atrisDir, 'lessons.md'), 'utf8');
1513
- } catch (err) {
1514
- lessonsText = '';
1515
- }
1516
- // rot = fail lessons nobody has resolved. A `pass` lesson is knowledge
1517
- // that worked — it has nothing to resolve and counting it guilt-trips
1518
- // the operator with a number (600+) no one can ever drive to zero.
1519
- const unresolvedLessons = parseLessons(lessonsText)
1520
- .filter((lesson) => lesson.status === 'fail' && !lesson.resolved && !/\[resolved\]/i.test(lesson.text)).length;
1521
- if (worktrees > 0 || unresolvedLessons > 0) {
1522
- rotInfo = { worktrees, lessons: unresolvedLessons };
1523
- }
1659
+ lessonsText = fs.readFileSync(path.join(atrisDir, 'lessons.md'), 'utf8');
1524
1660
  } catch (err) {
1525
- rotInfo = null;
1526
- }
1527
- if (rotInfo) {
1528
- const rotText = `${rotInfo.worktrees} stale worktree${rotInfo.worktrees === 1 ? '' : 's'}, ${rotInfo.lessons} unresolved lesson${rotInfo.lessons === 1 ? '' : 's'}`;
1529
- console.log(` │ 🧹 rot: ${rotText.padEnd(26)}│`);
1661
+ lessonsText = '';
1530
1662
  }
1531
- console.log(` │ 📝 Journal: ${(journalEntries + ' entries today').padEnd(26)}│`);
1532
- if (endgameState.slug !== 'unset' && endgameState.horizon) {
1533
- const endgameLine = endgameState.slug + ' — ' + endgameState.horizon;
1534
- const paddedEndgame = endgameLine.padEnd(26);
1535
- console.log(` │ 🎯 Endgame: ${paddedEndgame}│`);
1663
+ // cleanup = fail lessons nobody has resolved. A `pass` lesson is knowledge
1664
+ // that worked - it has nothing to resolve and counting it guilt-trips
1665
+ // the operator with a number (600+) no one can ever drive to zero.
1666
+ const unresolvedLessons = parseLessons(lessonsText)
1667
+ .filter((lesson) => lesson.status === 'fail' && !lesson.resolved && !/\[resolved[\]:]/i.test(lesson.text)).length;
1668
+ if (worktrees > 0 || unresolvedLessons > 0) {
1669
+ rotInfo = { worktrees, lessons: unresolvedLessons };
1536
1670
  }
1537
- console.log(' │ │');
1538
- console.log(' │ ┌──────────────────────────────────┐ │');
1539
- console.log(' │ │ MAP.md ←──── YOU ARE HERE │ │');
1540
- console.log(' │ │ ↓ │ │');
1541
- const taskText = `${tasksInBacklog} task${tasksInBacklog === 1 ? '' : 's'} waiting`;
1542
- console.log(` │ │ TODO.md ←── ${taskText.padEnd(20)}│ │`);
1543
- console.log(' │ │ ↓ │ │');
1544
- console.log(' │ │ navigator → executor → validator│ │');
1545
- console.log(' │ └──────────────────────────────────┘ │');
1546
- console.log(' │ │');
1547
- console.log(' └──────────────────────────────────────────┘');
1548
- }
1549
- console.log('');
1550
- if (tasksCertified > 0) {
1551
- console.log(` Ready. ${tasksCertified} certified await accept — run 'atris task reviews'.`);
1671
+ } catch (err) {
1672
+ rotInfo = null;
1673
+ }
1674
+ // One tidy row for all loose ends, in words that work outside engineering:
1675
+ // unlanded finished work = "to put away", stale worktrees = "old copies".
1676
+ const tidyBits = [];
1677
+ if (landInfo && landInfo.branches > 0) {
1678
+ let landText = `${landInfo.branches} finished piece${landInfo.branches === 1 ? '' : 's'} to put away`;
1679
+ if (landInfo.due > 0) landText += ` (${landInfo.due} overdue)`;
1680
+ tidyBits.push(landText);
1681
+ }
1682
+ if (rotInfo && rotInfo.worktrees > 0) tidyBits.push(`${rotInfo.worktrees} old cop${rotInfo.worktrees === 1 ? 'y' : 'ies'} to toss`);
1683
+ if (rotInfo && rotInfo.lessons > 0) tidyBits.push(`${rotInfo.lessons} open problem${rotInfo.lessons === 1 ? '' : 's'}`);
1684
+ if (tidyBits.length) {
1685
+ console.log(row('tidy', tidyBits.join(', ')));
1686
+ }
1687
+
1688
+ console.log(row('logs', journalEntries > 0
1689
+ ? `${journalEntries} note${journalEntries === 1 ? '' : 's'} today`
1690
+ : 'nothing yet today'));
1691
+
1692
+ if (endgameState.slug !== 'unset') {
1693
+ // Repeated impression: the horizon sentence renders verbatim every boot
1694
+ // so agents keep the target in mind (test/boot-impression.test.js).
1695
+ console.log(row('goal', endgameState.horizon || endgameState.slug));
1696
+ }
1697
+
1698
+ // The next command always carries a plain-english gloss: a newcomer should
1699
+ // know what typing it will do before they type it.
1700
+ let next;
1701
+ if (glance.reviewCertified > 0) {
1702
+ next = `atris task reviews (approve the finished work)`;
1703
+ } else if (landInfo && landInfo.due > 0) {
1704
+ next = `atris land --reap (put away the overdue work)`;
1705
+ } else if (endgameState.slug !== 'unset') {
1706
+ next = 'atris autopilot (do the next piece of work)';
1552
1707
  } else {
1553
- let landHint = null;
1554
- try { landHint = require('../commands/land').landSummary(process.cwd()); } catch (err) { landHint = null; }
1555
- if (landHint && landHint.due > 0) {
1556
- console.log(` Ready. ${landHint.due} overdue in the landing — run 'atris land --reap'.`);
1557
- } else {
1558
- console.log(` Ready. Run 'atris plan' to start.`);
1559
- }
1708
+ next = 'atris plan (plan the first tasks)';
1560
1709
  }
1561
1710
  console.log('');
1711
+ console.log(row('next', next));
1712
+ console.log('');
1562
1713
  }
1563
1714
 
1564
1715
  if (command === 'init') {
@@ -1616,10 +1767,38 @@ if (command === 'init') {
1616
1767
  Promise.resolve(require('../commands/task').run(process.argv.slice(3)))
1617
1768
  .then(() => process.exit(0))
1618
1769
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1770
+ } else if (command === 'team') {
1771
+ Promise.resolve(require('../commands/team').teamCommand(process.argv.slice(3)))
1772
+ .then((code) => process.exit(code || 0))
1773
+ .catch((err) => { console.error(`\nerror: ${err.message || err}`); process.exit(1); });
1774
+ } else if (command === 'wish') {
1775
+ Promise.resolve(require('../commands/wish').wishCommand(process.argv.slice(3)))
1776
+ .then((code) => process.exit(code || 0))
1777
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1778
+ } else if (command === 'drill') {
1779
+ Promise.resolve(require('../commands/drill').drillCommand(process.argv.slice(3)))
1780
+ .then((code) => process.exit(code || 0))
1781
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1782
+ } else if (command === 'bench') {
1783
+ Promise.resolve(require('../commands/bench').benchCommand(process.argv.slice(3)))
1784
+ .then((code) => process.exit(code || 0))
1785
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(2); });
1619
1786
  } else if (command === 'mission') {
1787
+ // process.exit() can outrun a piped stdout: writes beyond the 64KB pipe
1788
+ // buffer are async, so large --json payloads truncate at 64KB multiples.
1789
+ // Queue an empty write and exit from its callback — it fires only after
1790
+ // every earlier buffered write has drained (BCK-1306).
1791
+ const exitAfterStdoutDrain = (code) => {
1792
+ if (process.stdout.writableLength === 0) process.exit(code);
1793
+ else process.stdout.write('', () => process.exit(code));
1794
+ };
1620
1795
  Promise.resolve(require('../commands/mission').missionCommand(process.argv.slice(3)))
1621
- .then(() => process.exit(process.exitCode || 0))
1622
- .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1796
+ .then(() => exitAfterStdoutDrain(process.exitCode || 0))
1797
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); exitAfterStdoutDrain(1); });
1798
+ } else if (command === 'agents') {
1799
+ // Glanceable view of every member's state: stuck, waiting on you, working, resting.
1800
+ const code = require('../commands/agents').agentsCommand(process.argv.slice(3));
1801
+ process.exit(code || 0);
1623
1802
  } else if (command === 'pulse') {
1624
1803
  // Pulse: durable overnight self-improvement heartbeat (OS cron) for atris-cli.
1625
1804
  Promise.resolve(require('../commands/pulse').pulseCommand(process.argv.slice(3)))
@@ -1642,16 +1821,32 @@ if (command === 'init') {
1642
1821
  Promise.resolve(require('../commands/land').landCommand(process.argv.slice(3)))
1643
1822
  .then((code) => process.exit(code || 0))
1644
1823
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1824
+ } else if (command === 'close') {
1825
+ Promise.resolve(require('../commands/close').run(process.argv.slice(3)))
1826
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1827
+ .catch((err) => { console.error(`\nerror: ${err.message || err}`); process.exit(1); });
1828
+ } else if (command === 'goal' || command === 'wtf') {
1829
+ Promise.resolve(require('../commands/goal').run(process.argv.slice(3)))
1830
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1831
+ .catch((err) => { console.error(`\nerror: ${err.message || err}`); process.exit(1); });
1645
1832
  } else if (command === 'drive') {
1646
1833
  // Drive: one self-driving tick — mission doctor -> auto-fix safe findings -> count disengagements.
1647
1834
  Promise.resolve(require('../commands/drive').driveCommand(process.argv.slice(3)))
1648
1835
  .then((code) => process.exit(code || 0))
1649
1836
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1837
+ } else if (command === 'orb') {
1838
+ Promise.resolve(orbCmd(process.argv.slice(3)))
1839
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1840
+ .catch((err) => { console.error(`\nError: ${err.message || err}`); process.exit(1); });
1650
1841
  } else if (command === 'radar' || command === 'ctop') {
1651
1842
  const radarArgs = command === 'ctop' ? ['--agents', ...process.argv.slice(3)] : process.argv.slice(3);
1652
1843
  Promise.resolve(require('../commands/radar').radarCommand(radarArgs))
1653
1844
  .then((code) => process.exit(code || 0))
1654
1845
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1846
+ } else if (command === 'stream') {
1847
+ Promise.resolve(require('../commands/stream').streamCommand(process.argv.slice(3)))
1848
+ .then((code) => process.exit(code || 0))
1849
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1655
1850
  } else if (command === 'truth') {
1656
1851
  // Truth: one table rolling up mission state, tasks, feature proof receipts, and loop heartbeats.
1657
1852
  Promise.resolve(require('../commands/truth').truthCommand(process.argv.slice(3)))
@@ -1671,6 +1866,22 @@ if (command === 'init') {
1671
1866
  Promise.resolve(require('../commands/improve').run(process.argv.slice(3)))
1672
1867
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
1673
1868
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1869
+ } else if (command === 'study') {
1870
+ // Study: on-demand learning feed ingest + local server + browser open.
1871
+ Promise.resolve(require('../commands/study').run(process.argv.slice(3)))
1872
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1873
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1874
+ } else if (command === 'rainmaker') {
1875
+ const code = require('../commands/rainmaker').rainmakerCommand(process.argv.slice(3));
1876
+ process.exit(typeof code === 'number' ? code : 0);
1877
+ } else if (command === 'avail') {
1878
+ Promise.resolve(require('../commands/avail').availCommand(process.argv.slice(3)))
1879
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1880
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1881
+ } else if (command === 'meet') {
1882
+ Promise.resolve(require('../commands/meet').meetCommand(process.argv.slice(3)))
1883
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1884
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1674
1885
  } else if (command === 'brain') {
1675
1886
  Promise.resolve()
1676
1887
  .then(() => require('../commands/brain').brainCommand(process.argv.slice(3)))
@@ -1710,6 +1921,14 @@ if (command === 'init') {
1710
1921
  } else {
1711
1922
  logCmd();
1712
1923
  }
1924
+ } else if (command === 'logs') {
1925
+ try {
1926
+ require('../commands/log').logsDigest(process.argv.slice(3));
1927
+ process.exit(0);
1928
+ } catch (error) {
1929
+ console.error(error.message || String(error));
1930
+ process.exit(1);
1931
+ }
1713
1932
  } else if (command === 'now') {
1714
1933
  const args = process.argv.slice(3);
1715
1934
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
@@ -1726,6 +1945,8 @@ if (command === 'init') {
1726
1945
  process.exit(0);
1727
1946
  }
1728
1947
  activateCmd();
1948
+ } else if (command === 'watch') {
1949
+ require('../commands/watch').watchAtris();
1729
1950
  } else if (command === 'update' || command === 'sync') {
1730
1951
  const args = process.argv.slice(3);
1731
1952
  const firstSyncArg = process.argv[3];
@@ -1756,7 +1977,10 @@ if (command === 'init') {
1756
1977
  .then(() => process.exit(0))
1757
1978
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1758
1979
  } else {
1759
- syncCmd();
1980
+ syncCmd({
1981
+ dryRun: args.includes('--dry-run'),
1982
+ force: args.includes('--force') || args.includes('--yes') || args.includes('-y'),
1983
+ });
1760
1984
  }
1761
1985
  } else if (command === 'upgrade') {
1762
1986
  const args = process.argv.slice(3);
@@ -2100,15 +2324,36 @@ if (command === 'init') {
2100
2324
  console.error(`✗ Brainstorm failed: ${error.message || error}`);
2101
2325
  process.exit(1);
2102
2326
  });
2103
- } else if (command === 'next' || command === 'atris') {
2327
+ } else if (command === 'next') {
2328
+ Promise.resolve(require('../commands/next').nextCommand(process.argv.slice(3)))
2329
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2330
+ .catch((error) => {
2331
+ console.error(`✗ Error: ${error.message || error}`);
2332
+ process.exit(1);
2333
+ });
2334
+ } else if (command === 'dream') {
2335
+ Promise.resolve(require('../commands/dream').dreamCommand(process.argv.slice(3)))
2336
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2337
+ .catch((error) => {
2338
+ console.log('No dreams tonight: could not finish dream');
2339
+ console.log('Run me nightly: atris dream');
2340
+ process.exit(0);
2341
+ });
2342
+ } else if (command === 'atris') {
2104
2343
  const rawArgs = process.argv.slice(3);
2105
2344
  if (rawArgs.includes('--help') || rawArgs.includes('-h') || rawArgs[0] === 'help') {
2106
2345
  showNextHelp(command);
2107
2346
  process.exit(0);
2108
2347
  }
2109
- const userInput = rawArgs.filter((arg) => !arg.startsWith('-')).join(' ').trim();
2110
- interactiveEntry(userInput || null)
2111
- .then(() => process.exit(0))
2348
+ const natural = parseNaturalEntryArgs(rawArgs);
2349
+ interactiveEntry(natural.input || null, {
2350
+ oneLap: Boolean(natural.input),
2351
+ asJson: natural.asJson,
2352
+ engine: natural.engine,
2353
+ verifier: natural.verifier,
2354
+ optionError: natural.error,
2355
+ })
2356
+ .then((code) => process.exit(Number.isInteger(code) ? code : 0))
2112
2357
  .catch((error) => {
2113
2358
  console.error(`✗ Error: ${error.message || error}`);
2114
2359
  process.exit(1);
@@ -2194,6 +2439,26 @@ if (command === 'init') {
2194
2439
  showVerifyHelp();
2195
2440
  process.exit(0);
2196
2441
  }
2442
+ if (args[0] === 'artifact') {
2443
+ const target = args[1] && !args[1].startsWith('--') ? args[1] : null;
2444
+ if (!target) {
2445
+ showVerifyHelp();
2446
+ process.exit(2);
2447
+ }
2448
+ const readValue = (flag) => {
2449
+ const idx = args.indexOf(flag);
2450
+ return idx > 0 && args[idx + 1] ? args[idx + 1] : null;
2451
+ };
2452
+ const minLinesRaw = readValue('--min-lines');
2453
+ const maxAgeRaw = readValue('--max-age-hours');
2454
+ const code = require('../commands/verify').verifyArtifact(target, {
2455
+ objective: readValue('--objective') || undefined,
2456
+ minLines: minLinesRaw !== null ? Number(minLinesRaw) : undefined,
2457
+ maxAgeHours: maxAgeRaw !== null ? Number(maxAgeRaw) : undefined,
2458
+ json: args.includes('--json'),
2459
+ });
2460
+ process.exit(code);
2461
+ }
2197
2462
  const sectionIdx = process.argv.indexOf('--section');
2198
2463
  if (sectionIdx > 0 && process.argv[sectionIdx + 1]) {
2199
2464
  const slug = process.argv[3] && !process.argv[3].startsWith('--') ? process.argv[3] : null;
@@ -2214,12 +2479,24 @@ if (command === 'init') {
2214
2479
  .then(() => process.exit(0))
2215
2480
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2216
2481
  } else if (command === 'search') {
2217
- const keyword = process.argv.slice(3).join(' ');
2218
- searchJournal(keyword);
2482
+ const code = require('../commands/search').searchCommand(process.argv.slice(3));
2483
+ process.exitCode = code;
2484
+ } else if (command === 'scout') {
2485
+ require('../commands/scout').scoutCommand(process.argv.slice(3))
2486
+ .then((code) => { process.exitCode = code; })
2487
+ .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
2219
2488
  } else if (command === 'xp') {
2220
2489
  require('../commands/xp').xpCommand(...process.argv.slice(3))
2221
2490
  .then(() => { process.exitCode = 0; })
2222
2491
  .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
2492
+ } else if (command === 'report') {
2493
+ const args = process.argv.slice(3);
2494
+ const { reportCommand, showReportHelp } = require('../commands/report');
2495
+ if (args.includes('--help') || args.includes('-h')) {
2496
+ showReportHelp();
2497
+ process.exit(0);
2498
+ }
2499
+ process.exit(reportCommand(args));
2223
2500
  } else if (command === 'play') {
2224
2501
  require('../commands/play').playCommand(...process.argv.slice(3))
2225
2502
  .then(() => process.exit(0))
@@ -2334,10 +2611,15 @@ if (command === 'init') {
2334
2611
  require('../commands/align').alignAtris()
2335
2612
  .then(() => process.exit(0))
2336
2613
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2614
+ } else if (command === 'cloud') {
2615
+ require('../commands/cloud').cloudAtris();
2337
2616
  } else if (command === 'terminal') {
2338
2617
  require('../commands/terminal').terminalAtris()
2339
2618
  .then(() => process.exit(0))
2340
2619
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2620
+ } else if (command === 'fleet-report') {
2621
+ require('../commands/fleet-report').fleetReport()
2622
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2341
2623
  } else if (command === 'x') {
2342
2624
  // Fast Agent SDK execution - like "atris x echo hello" or "atris x ls -la"
2343
2625
  const userInput = process.argv.slice(3).join(' ').trim();
@@ -2385,10 +2667,19 @@ if (command === 'init') {
2385
2667
  require('../commands/fleet').fleet(args)
2386
2668
  .then(() => process.exit(0))
2387
2669
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2388
- } else if (command === 'loops') {
2670
+ } else if (command === 'loops' || command === 'self-improve') {
2389
2671
  try {
2390
- require('../commands/loops').loopsCommand(process.argv[3], ...process.argv.slice(4));
2391
- process.exit(0);
2672
+ const loops = require('../commands/loops');
2673
+ const aliasArgs = process.argv.slice(3);
2674
+ const aliasWantsHelp = ['help', '--help', '-h'].includes(aliasArgs[0]);
2675
+ const subcommand = command === 'self-improve'
2676
+ ? (aliasWantsHelp ? aliasArgs[0] : 'init')
2677
+ : process.argv[3];
2678
+ const args = command === 'self-improve'
2679
+ ? (aliasWantsHelp ? aliasArgs.slice(1) : aliasArgs)
2680
+ : process.argv.slice(4);
2681
+ const exitCode = loops.loopsCommand(subcommand, ...args);
2682
+ process.exit(typeof exitCode === 'number' ? exitCode : 0);
2392
2683
  } catch (error) {
2393
2684
  console.error(`\n✗ Error: ${error.message || error}`);
2394
2685
  process.exit(1);
@@ -2527,6 +2818,15 @@ if (command === 'init') {
2527
2818
  Promise.resolve(require('../commands/card').run(process.argv.slice(3)))
2528
2819
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
2529
2820
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2821
+ } else if (command === 'brief') {
2822
+ // Brief: one-glance operator surface for landings, waits, and next moves.
2823
+ Promise.resolve(require('../commands/brief').run(process.argv.slice(3)))
2824
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2825
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2826
+ } else if (command === 'pack') {
2827
+ Promise.resolve(require('../commands/pack').run(process.argv.slice(3)))
2828
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2829
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2530
2830
  } else if (command === 'reel') {
2531
2831
  // Reel: one line of text into a short on-brand video (an animated card; frames via Chrome + ffmpeg).
2532
2832
  Promise.resolve(require('../commands/reel').run(process.argv.slice(3)))