atris 3.34.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 (163) hide show
  1. package/AGENTS.md +35 -0
  2. package/FOR_AGENTS.md +5 -3
  3. package/README.md +5 -3
  4. package/atris/GETTING_STARTED.md +1 -1
  5. package/atris/atris.md +3 -0
  6. package/atris/policies/day-loop-voice.md +102 -0
  7. package/atris/policies/outbound-artifact-gate.md +2 -0
  8. package/atris/skills/design/SKILL.md +56 -32
  9. package/atris/skills/endgame/SKILL.md +12 -6
  10. package/atris/skills/engines/SKILL.md +35 -8
  11. package/atris/skills/fable-method/SKILL.md +66 -0
  12. package/atris/skills/improve/SKILL.md +65 -45
  13. package/atris/skills/render-cli/SKILL.md +88 -0
  14. package/atris/skills/youtube/SKILL.md +10 -1
  15. package/atris.md +6 -2
  16. package/ax +617 -31
  17. package/bin/atris.js +719 -266
  18. package/commands/activate.js +194 -88
  19. package/commands/aeo.js +52 -0
  20. package/commands/agents.js +166 -0
  21. package/commands/autoland.js +718 -72
  22. package/commands/autopilot-front.js +20 -2
  23. package/commands/autopilot.js +118 -2
  24. package/commands/avail.js +407 -0
  25. package/commands/bench.js +188 -0
  26. package/commands/brain.js +3 -0
  27. package/commands/brief.js +651 -0
  28. package/commands/business-sync.js +192 -6
  29. package/commands/business.js +91 -8
  30. package/commands/clean.js +50 -24
  31. package/commands/close.js +1083 -0
  32. package/commands/cloud.js +245 -0
  33. package/commands/codex-goal.js +26 -2
  34. package/commands/compile.js +292 -1
  35. package/commands/computer.js +150 -3
  36. package/commands/dream.js +365 -0
  37. package/commands/drill.js +371 -0
  38. package/commands/drive.js +187 -0
  39. package/commands/engine.js +1061 -29
  40. package/commands/experiments.js +28 -0
  41. package/commands/feed.js +202 -0
  42. package/commands/feedback.js +34 -12
  43. package/commands/fleet-report.js +206 -0
  44. package/commands/github.js +38 -0
  45. package/commands/gm.js +285 -3
  46. package/commands/goal.js +247 -0
  47. package/commands/improve.js +642 -26
  48. package/commands/init.js +83 -43
  49. package/commands/integrations.js +39 -11
  50. package/commands/interview.js +209 -0
  51. package/commands/land.js +253 -52
  52. package/commands/lesson.js +112 -1
  53. package/commands/lifecycle.js +39 -3
  54. package/commands/linear.js +38 -0
  55. package/commands/log.js +84 -1
  56. package/commands/loops.js +220 -16
  57. package/commands/meet.js +220 -0
  58. package/commands/member.js +899 -66
  59. package/commands/mission.js +3512 -332
  60. package/commands/next.js +137 -0
  61. package/commands/now.js +240 -21
  62. package/commands/one-lap.js +776 -0
  63. package/commands/orb.js +314 -0
  64. package/commands/pack-craft.js +179 -0
  65. package/commands/pack.js +823 -0
  66. package/commands/play.js +3 -2
  67. package/commands/probe.js +30 -3
  68. package/commands/pulse.js +241 -46
  69. package/commands/push.js +260 -82
  70. package/commands/radar.js +259 -14
  71. package/commands/rainmaker.js +49 -0
  72. package/commands/report.js +415 -0
  73. package/commands/scout.js +147 -0
  74. package/commands/search.js +363 -0
  75. package/commands/serve.js +54 -0
  76. package/commands/skill.js +47 -3
  77. package/commands/slop.js +50 -2
  78. package/commands/soul.js +1 -1
  79. package/commands/status.js +50 -5
  80. package/commands/stream.js +861 -0
  81. package/commands/stripe.js +38 -0
  82. package/commands/study.js +693 -0
  83. package/commands/supabase.js +39 -0
  84. package/commands/sync.js +67 -54
  85. package/commands/task.js +2275 -182
  86. package/commands/team.js +73 -0
  87. package/commands/truth.js +29 -3
  88. package/commands/unknowns.js +627 -0
  89. package/commands/update.js +44 -0
  90. package/commands/vercel.js +38 -0
  91. package/commands/verify.js +96 -0
  92. package/commands/watch.js +303 -0
  93. package/commands/wish.js +500 -0
  94. package/commands/workflow.js +11 -5
  95. package/commands/worktree.js +299 -20
  96. package/commands/write.js +399 -0
  97. package/commands/xp.js +29 -11
  98. package/lib/auto-accept-certified.js +391 -43
  99. package/lib/autoland.js +353 -52
  100. package/lib/ax-auto-lane.js +79 -0
  101. package/lib/bench/context.js +147 -0
  102. package/lib/bench/engines.js +141 -0
  103. package/lib/bench/report.js +140 -0
  104. package/lib/bench/runner.js +512 -0
  105. package/lib/brief-ledger.js +350 -0
  106. package/lib/cloud-mission.js +259 -0
  107. package/lib/codex-flight.js +154 -0
  108. package/lib/default-runner.js +45 -0
  109. package/lib/default-verifier.js +70 -0
  110. package/lib/engine-registry.js +232 -0
  111. package/lib/experiments/daily.js +640 -0
  112. package/lib/fleet.js +2431 -38
  113. package/lib/improve-vitals-html.js +171 -0
  114. package/lib/known-commands.js +58 -0
  115. package/lib/loop-doctor.js +416 -0
  116. package/lib/member-switches.js +144 -0
  117. package/lib/memory-view.js +14 -5
  118. package/lib/mission-room.js +1 -0
  119. package/lib/mission-root.js +52 -0
  120. package/lib/mission-runtime-loop.js +7 -0
  121. package/lib/next-moves.js +327 -10
  122. package/lib/official-cli-integration.js +174 -0
  123. package/lib/one-lap-validator.js +60 -0
  124. package/lib/orb-context.js +477 -0
  125. package/lib/orb-scorecard.js +224 -0
  126. package/lib/outbound-send-gate.js +165 -0
  127. package/lib/permission-grants.js +293 -0
  128. package/lib/policy-lessons.js +52 -1
  129. package/lib/pulse.js +277 -3
  130. package/lib/receipt-block.js +168 -0
  131. package/lib/receipt-evidence.js +65 -4
  132. package/lib/review-integrity.js +147 -0
  133. package/lib/router-brain.js +352 -0
  134. package/lib/runner-command.js +33 -0
  135. package/lib/self-drive.js +258 -0
  136. package/lib/short-name.js +103 -0
  137. package/lib/spawn-env.js +18 -0
  138. package/lib/state-detection.js +56 -1
  139. package/lib/sync-status.js +59 -0
  140. package/lib/task-db.js +319 -27
  141. package/lib/task-proof.js +43 -1
  142. package/lib/task-receipt.js +93 -0
  143. package/lib/team-presence.js +260 -0
  144. package/lib/tool-result-encode.js +7 -0
  145. package/lib/trust-tiers.js +90 -0
  146. package/lib/usage.js +107 -0
  147. package/lib/voice-gate.js +163 -0
  148. package/lib/wish-audit.js +1368 -0
  149. package/lib/wish-delegate.js +1840 -0
  150. package/lib/wish-design.js +110 -0
  151. package/lib/wish-stats.js +183 -0
  152. package/lib/wish-store.js +354 -0
  153. package/lib/zip.js +221 -0
  154. package/package.json +3 -1
  155. package/templates/loops/atris/loops/LOOPS.md +55 -0
  156. package/templates/loops/atris/loops/TICK.md +24 -0
  157. package/templates/loops/atris/loops/feedback.md +22 -0
  158. package/templates/loops/atris/loops/quality.md +22 -0
  159. package/templates/loops/atris/wiki/systems/loops.md +41 -0
  160. package/utils/api.js +5 -1
  161. package/utils/auth.js +57 -21
  162. package/utils/update-check.js +27 -6
  163. package/atris/learnings.jsonl +0 -1
package/bin/atris.js CHANGED
@@ -29,7 +29,6 @@ try {
29
29
  const {
30
30
  checkForUpdates,
31
31
  showUpdateNotification,
32
- autoUpdate,
33
32
  inspectInstallGitState,
34
33
  formatInstallGitWarning,
35
34
  } = require('../utils/update-check');
@@ -66,6 +65,8 @@ const {
66
65
  DEFAULT_CLIENT_ID, DEFAULT_USER_AGENT,
67
66
  } = require('../utils/api');
68
67
  const missionRuntime = require('../lib/mission-runtime-loop');
68
+ const { knownCommands, suggestCommand } = require('../lib/known-commands');
69
+ const { recordUsage } = require('../lib/usage');
69
70
 
70
71
  // Bind DI wrappers (utils/auth uses dependency injection for apiRequestJson)
71
72
  const validateAccessToken = (token) => _validateAccessToken(token, apiRequestJson);
@@ -86,19 +87,16 @@ const helpRequested = updateCommand === 'help'
86
87
  || updateArgs.includes('--help')
87
88
  || updateArgs.includes('-h')
88
89
  || updateArgs[0] === 'help';
89
- const jsonRequested = updateArgs.includes('--json');
90
+ const jsonRequested = process.argv.slice(2).includes('--json');
91
+ const dryRunRequested = updateArgs.includes('--dry-run');
90
92
  const skipUpdateCheck = Boolean(process.env.ATRIS_SKIP_UPDATE_CHECK || process.env.NO_UPDATE_NOTIFIER || helpRequested || jsonRequested);
91
93
  if (!skipUpdateCheck && (!updateCommand || (updateCommand && !['version', 'update'].includes(updateCommand)))) {
92
94
  updateCheckPromise = checkForUpdates()
93
95
  .then((updateInfo) => {
94
- // Show notification if update available (after command completes)
95
96
  if (updateInfo) {
96
- const autoUpdateStarted = autoUpdate(updateInfo, {
97
+ showUpdateNotification(updateInfo, {
97
98
  packageRoot: path.join(__dirname, '..'),
98
99
  });
99
- if (!autoUpdateStarted) {
100
- showUpdateNotification(updateInfo);
101
- }
102
100
  }
103
101
  return updateInfo;
104
102
  })
@@ -112,6 +110,12 @@ let command = process.argv[2];
112
110
  const commandArgs = process.argv.slice(3);
113
111
  const firstCommandArg = process.argv[3];
114
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
+ ]);
115
119
 
116
120
  function readOptionArg(args, name) {
117
121
  const prefix = `${name}=`;
@@ -126,6 +130,50 @@ function isOptionValue(args, index, optionNames) {
126
130
  return index > 0 && optionNames.includes(args[index - 1]);
127
131
  }
128
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
+
129
177
  function applyRunnerFlags(args) {
130
178
  // --engine <name> is the operator-facing spelling of --runner-profile:
131
179
  // one flag rents a specific intelligence for this run.
@@ -201,13 +249,14 @@ const isBusinessSyncSafetyCommand = command === 'sync'
201
249
  || firstCommandArg === 'resolve'
202
250
  );
203
251
 
204
- // Auto-sync skills only for commands that modify workspace state
205
- 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))) {
206
255
  try {
207
256
  const { syncSkills } = require('../commands/sync');
208
257
  const skillsUpdated = syncSkills({ silent: true });
209
258
  if (skillsUpdated > 0) {
210
- console.log(`⬆️ ${skillsUpdated} skill${skillsUpdated > 1 ? 's' : ''} updated`);
259
+ console.log(`${skillsUpdated} skill${skillsUpdated > 1 ? 's' : ''} updated`);
211
260
  }
212
261
  } catch (e) {
213
262
  // Non-critical
@@ -350,76 +399,7 @@ function printAtrisGoalBanner(workspaceDir = process.cwd(), label = 'Atris goal'
350
399
  }
351
400
 
352
401
  function showSearchHelp() {
353
- console.log('Usage: atris search <keyword>');
354
- console.log('Example: atris search auth');
355
- }
356
-
357
- function searchJournal(keyword) {
358
- if (!keyword) {
359
- showSearchHelp();
360
- process.exit(1);
361
- }
362
-
363
- if (keyword === '--help' || keyword === '-h') {
364
- showSearchHelp();
365
- process.exit(0);
366
- }
367
-
368
- if (process.argv.slice(4).includes('--help') || process.argv.slice(4).includes('-h')) {
369
- showSearchHelp();
370
- process.exit(1);
371
- }
372
-
373
- const logsDir = path.join(process.cwd(), 'atris', 'logs');
374
- if (!fs.existsSync(logsDir)) {
375
- console.log('No atris/logs/ directory found. Run "atris init" first.');
376
- process.exit(1);
377
- }
378
-
379
- console.log(`Searching for "${keyword}" in atris/logs/**/*.md...\n`);
380
-
381
- const results = [];
382
- const keywordLower = keyword.toLowerCase();
383
-
384
- // Recursively find all .md files in logs directory
385
- function walkDir(dir) {
386
- let files;
387
- try { files = fs.readdirSync(dir); } catch { return; }
388
- for (const file of files) {
389
- try {
390
- const filePath = path.join(dir, file);
391
- const stat = fs.statSync(filePath);
392
- if (stat.isDirectory()) {
393
- walkDir(filePath);
394
- } else if (file.endsWith('.md')) {
395
- const content = fs.readFileSync(filePath, 'utf8');
396
- const lines = content.split('\n');
397
- lines.forEach((line, idx) => {
398
- if (line.toLowerCase().includes(keywordLower)) {
399
- results.push({
400
- file: path.relative(process.cwd(), filePath),
401
- line: idx + 1,
402
- content: line.trim()
403
- });
404
- }
405
- });
406
- }
407
- } catch { /* skip unreadable files */ }
408
- }
409
- }
410
-
411
- walkDir(logsDir);
412
-
413
- if (results.length === 0) {
414
- console.log('No matches found.');
415
- } else {
416
- console.log(`Found ${results.length} match${results.length > 1 ? 'es' : ''}:\n`);
417
- results.forEach(r => {
418
- console.log(`${r.file}:${r.line}`);
419
- console.log(` ${r.content.substring(0, 100)}${r.content.length > 100 ? '...' : ''}`);
420
- console.log('');
421
- });
422
- }
402
+ require('../commands/search').showSearchHelp();
423
403
  }
424
404
 
425
405
  function consoleCmd() {
@@ -458,7 +438,8 @@ function consoleCmd() {
458
438
  function showHelp() {
459
439
  console.log('');
460
440
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
461
- 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.');
462
443
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
463
444
  console.log('');
464
445
  console.log('Quick Start:');
@@ -469,11 +450,15 @@ function showHelp() {
469
450
  console.log(' 3. Atris acts with context, memory, tools, and a review loop');
470
451
  console.log('');
471
452
  console.log('Common invocations:');
472
- console.log(' atris init Global install: initialize this project');
473
- console.log(' npx atris init Local install: initialize this project');
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');
456
+ console.log(' atris init [--yes] Global install: initialize this project');
457
+ console.log(' npx atris init [--yes] Local install: initialize this project');
474
458
  console.log(' atris computer');
475
459
  console.log(' atris business init "My Company"');
476
460
  console.log(' atris run');
461
+ console.log(' atris drill');
477
462
  console.log(' atris status');
478
463
  console.log(' atris soul');
479
464
  console.log(' atris fleet status');
@@ -487,7 +472,7 @@ function showHelp() {
487
472
  console.log('');
488
473
  console.log('Setup:');
489
474
  console.log(' setup - Guided first-time setup (login, pick business, pull)');
490
- console.log(' init - Initialize Atris in current project');
475
+ console.log(' init - Initialize Atris in current project (--yes skips prompts)');
491
476
  console.log(' update - Update local files to latest version');
492
477
  console.log(' upgrade - Install latest Atris from npm');
493
478
  console.log('');
@@ -503,29 +488,49 @@ function showHelp() {
503
488
  console.log('');
504
489
  console.log('Context & tracking:');
505
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');
506
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');
507
495
  console.log(' activate - Load Atris context');
508
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');
509
500
  console.log(' ctop - Show a process-first live agent CPU/memory view');
510
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');
511
503
  console.log(' status - See local work and completions (`atris status <business>` for remote)');
512
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');
513
506
  console.log(' xp - Show Career XP and contribution graph');
514
507
  console.log(' analytics - Show recent productivity from journals');
515
- console.log(' search - Search journal history (atris search <keyword>)');
508
+ console.log(' search - Search workspace memory (atris search <keyword>)');
516
509
  console.log(' clean - Housekeeping (stale tasks, archive journals, broken refs)');
510
+ console.log(' close - track open loops with deadlines and daily escalation');
517
511
  console.log(' harvest - Find bugs and next actions from receipts, run logs, and thinking');
518
512
  console.log(' verify - Validate work is done (tests, MAP.md, changes)');
519
513
  console.log(' task - Local agent task plane (atomic claims, TODO import)');
520
- console.log(' mission - Goal + loop + member owner + verifier + receipt');
514
+ console.log(' golden path (zero human turns):');
515
+ console.log(' atris task delegate "fix the login bug" --to <member>');
516
+ console.log(' atris task claim <id> --as <member>');
517
+ console.log(' ... build ...');
518
+ console.log(' atris task ready <id> --verify');
519
+ console.log(' atris autoland tick # second check runs, task lands');
520
+ console.log(' mission - Goal + loop + member owner + verifier + receipt; --budget quick|long|deep sets bounded tiers');
521
521
  console.log(' release - Tag release, bump version, create GitHub release, draft /launch');
522
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)');
523
527
  console.log(' brain - Compile MAP/TODO/wiki/state into a loadable agent brain');
524
528
  console.log(' lesson - Append a one-line lesson to atris/lessons.md (mine: distill receipts/episodes/scorecards into policy lessons)');
525
529
  console.log(' ingest - Local-first wiki ingest into atris/wiki/');
526
530
  console.log(' query - Local-first wiki query against atris/wiki/');
527
531
  console.log(' lint - Local-first wiki lint for atris/wiki/');
528
532
  console.log(' loop - Local wiki upkeep loop (stale pages, orphans, next ingest)');
533
+ console.log(' write - Guided writing sessions: you write every word, atris structures + reviews');
529
534
  console.log('');
530
535
  console.log('Optional helpers:');
531
536
  console.log(' brainstorm - Explore ideas conversationally before planning');
@@ -533,8 +538,9 @@ function showHelp() {
533
538
  console.log(' improve - Run one paid RL tick (POST /api/improve, deducts credits)');
534
539
  console.log(' worktree - Isolated Git worktrees plus guarded ship/merge for parallel agents');
535
540
  console.log(' land - The landing: what is actually done vs still in the air; --reap backs up + clears overdue');
541
+ console.log(' drive - One self-driving tick: mission doctor -> auto-fix -> count disengagements');
536
542
  console.log(' autoland - Approve the policy once; certified work lands itself, you keep irreversible calls');
537
- console.log(' engine - Bring any intelligence: roster of installed coding CLIs, default engine, --engine per run, `engine test` preflight');
543
+ console.log(' engine - Engine registry: list/resolve roles, health flips, default engine, `engine test`, and dispatch flights');
538
544
  console.log(' sign - Co-author trailer on every commit in an atris workspace (on/off/status)');
539
545
  console.log(' visualize - Generate a Slack/deck-ready visual from a prompt');
540
546
  console.log(' youtube - Process YouTube videos with timestamped transcript-first analysis');
@@ -544,6 +550,7 @@ function showHelp() {
544
550
  console.log(' experiments validate - Validate experiment packs');
545
551
  console.log(' experiments run <slug> - Execute a pack or record an Endstate receipt');
546
552
  console.log(' experiments benchmark [m] - Run validate/runtime experiment benchmarks');
553
+ console.log(' bench - run core benchmark gates');
547
554
  console.log('');
548
555
  console.log('Compile loop (learn like AI, run like code):');
549
556
  console.log(' compile record <name> - Append an execution record (--input/--output)');
@@ -559,6 +566,7 @@ function showHelp() {
559
566
  console.log('Sync:');
560
567
  console.log(' pull - Pull journals + member data from cloud');
561
568
  console.log(' push - Push workspace files to cloud');
569
+ console.log(' cloud - Delete cloud files not present locally (cloud clean --dry-run|--yes)');
562
570
  console.log(' live - Keep a business brain fresh (doctor, pull, watch, push)');
563
571
  console.log(' clean-workspace <slug> - Analyze & remove junk files from a workspace (alias: cw)');
564
572
  console.log('');
@@ -566,8 +574,9 @@ function showHelp() {
566
574
  console.log(' browse [query] - Discover workspace templates');
567
575
  console.log(' fork <template> - Clone a template into a new workspace');
568
576
  console.log(' publish - Share your workspace as a template');
569
- console.log(' sleep [business] - Pause workspace compute (context saved)');
570
- 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');
571
580
  console.log('');
572
581
  console.log('Business:');
573
582
  console.log(' business init <name> - Create shared owner + first/default computer');
@@ -594,7 +603,8 @@ function showHelp() {
594
603
  console.log(' console - Start/attach always-on coding console (tmux daemon)');
595
604
  console.log(' soul - Show, snapshot, or fork workspace identity');
596
605
  console.log(' fleet - Inspect local fleet status');
597
- 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`');
598
608
  console.log(' agent - Select cloud agent, spawn worker requests, or run `agent doctor`');
599
609
  console.log(' chat - Chat with Atris 2 Fast in this workspace (--agent for cloud agent; or: atris chat scan)');
600
610
  console.log(' fast - Chat with Atris2 Fast');
@@ -606,6 +616,11 @@ function showHelp() {
606
616
  console.log(' accounts - Manage accounts (list, add, remove)');
607
617
  console.log('');
608
618
  console.log('Integrations:');
619
+ console.log(' github - github cli wrapper (doctor, auth, pr list/create/checks/view)');
620
+ console.log(' vercel - vercel cli wrapper (doctor, auth, deploy/ls/logs/inspect)');
621
+ console.log(' supabase - supabase cli wrapper (doctor, auth, status/db/functions)');
622
+ console.log(' linear - linear cli wrapper (doctor, auth, issue list/create/view/update)');
623
+ console.log(' stripe - stripe cli wrapper (doctor, auth, listen/trigger/products)');
609
624
  console.log(' gmail - Email commands (inbox, read, archive)');
610
625
  console.log(' calendar - Calendar commands (today, week)');
611
626
  console.log(' twitter - Twitter commands (post)');
@@ -754,19 +769,26 @@ function showVerifyHelp() {
754
769
  console.log('');
755
770
  console.log('Usage: atris verify [task]');
756
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]');
757
773
  console.log('');
758
774
  console.log('Description:');
759
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).');
760
778
  console.log('');
761
779
  console.log('Options:');
762
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.');
763
784
  console.log(' --help, -h Show this help.');
764
785
  console.log('');
765
786
  }
766
787
 
767
788
  function showUpdateHelp(commandName = 'update') {
768
789
  console.log('');
769
- console.log(`Usage: atris ${commandName} [--all] [--dry-run] [--force]`);
790
+ const selfFlag = commandName === 'update' ? ' [--self]' : '';
791
+ console.log(`Usage: atris ${commandName} [--all] [--dry-run] [--force]${selfFlag}`);
770
792
  console.log('');
771
793
  console.log('Description:');
772
794
  console.log(' Sync Atris workspace files from the installed CLI templates.');
@@ -775,6 +797,9 @@ function showUpdateHelp(commandName = 'update') {
775
797
  console.log(' --all Update Atris files across projects under the current tree.');
776
798
  console.log(' --dry-run Preview update work without writing files.');
777
799
  console.log(' --force Overwrite existing template files where supported.');
800
+ if (commandName === 'update') {
801
+ console.log(' --self Update the installed atris cli from npm (packaged installs only).');
802
+ }
778
803
  console.log(' --help, -h Show this help.');
779
804
  console.log('');
780
805
  }
@@ -956,6 +981,7 @@ const { logAtris: logCmd } = require('../commands/log');
956
981
  const { activateAtris: activateCmd } = require('../commands/activate');
957
982
  const { statusAtris: statusCmd } = require('../commands/status');
958
983
  const { planAtris: planCmd, doAtris: doCmd, reviewAtris: reviewCmd } = require('../commands/workflow');
984
+ const { runOrb: orbCmd } = require('../commands/orb');
959
985
 
960
986
  // All other commands are lazy-loaded inline (require() only when invoked)
961
987
 
@@ -971,12 +997,6 @@ if (command === '2' && ['fast', 'pro'].includes(String(firstCommandArg || '').to
971
997
  }
972
998
 
973
999
  // Check if this is a known command or natural language input
974
- const knownCommands = ['init', 'log', 'now', 'radar', 'ctop', 'launchpad', 'status', 'analytics', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', '_start', 'plan', 'do', 'review', 'release',
975
- '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',
976
- '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',
977
- 'ingest', 'query', 'lint', 'loop', 'pulse', 'task', 'mission', 'probe', 'worktree', 'land', 'autoland', 'aeo', 'slop', 'strings', 'security-review', 'secure', 'deck', 'site', 'theme', 'card', 'reel', 'improve', 'xp', 'play', 'gm', 'x', 'recap', 'signup', 'clarity', 'moves',
978
- 'gmail', 'calendar', 'twitter', 'slack', 'imessage', 'integrations', 'setup', 'clean-workspace', 'cw',
979
- 'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'loops', 'compile', 'spaceship', 'truth', 'sign', 'engine', 'engines'];
980
1000
 
981
1001
  // Check if command is an atris.md spec file - triggers welcome visualization
982
1002
  function isSpecFile(cmd) {
@@ -1031,11 +1051,7 @@ const voiceTriggers = {
1031
1051
  if (!command || !knownCommands.includes(command)) {
1032
1052
  // Check voice triggers before falling through to natural language
1033
1053
  const fullInput = process.argv.slice(2).join(' ').toLowerCase().trim();
1034
- const fullInputWithoutFlags = process.argv.slice(2)
1035
- .filter((arg, index, args) => !String(arg).startsWith('-') && !isOptionValue(args, index, RUNNER_FLAG_NAMES))
1036
- .join(' ')
1037
- .toLowerCase()
1038
- .trim();
1054
+ const fullInputWithoutFlags = parseNaturalEntryArgs(process.argv.slice(2)).input.toLowerCase();
1039
1055
  const triggered = voiceTriggers[fullInput] || voiceTriggers[fullInputWithoutFlags];
1040
1056
  if (triggered) {
1041
1057
  command = triggered;
@@ -1049,28 +1065,36 @@ if (!command || !knownCommands.includes(command)) {
1049
1065
  }
1050
1066
 
1051
1067
  if (!command || !knownCommands.includes(command)) {
1052
- 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());
1053
1073
 
1054
- if (process.argv.includes('--json')) {
1074
+ if (natural.asJson && !natural.multiword && !directSingleWordNatural) {
1055
1075
  console.log(JSON.stringify({
1056
1076
  ok: false,
1057
1077
  error: command ? `unknown command: ${command}` : 'unknown command',
1058
1078
  command: command || null,
1059
- input: userInput,
1079
+ input: rawNaturalArgs.join(' '),
1060
1080
  usage: 'atris help',
1061
1081
  }, null, 2));
1062
1082
  process.exit(2);
1063
1083
  }
1064
1084
 
1065
1085
  // Warn if this looks like a mistyped single-word command (no spaces)
1066
- if (command && !userInput.includes(' ')) {
1086
+ if (command && !natural.multiword && !directSingleWordNatural) {
1067
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
+ }
1068
1092
  console.log(' Treating as natural language input...\n');
1069
1093
  }
1070
1094
 
1071
1095
  // Launch interactive entry (the "Performance")
1072
- interactiveEntry(userInput)
1073
- .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))
1074
1098
  .catch((error) => {
1075
1099
  console.error(`✗ Error: ${error.message || error}`);
1076
1100
  process.exit(1);
@@ -1078,6 +1102,12 @@ if (!command || !knownCommands.includes(command)) {
1078
1102
  return;
1079
1103
  }
1080
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
+
1081
1111
  function printAtrisOverview() {
1082
1112
  console.log('');
1083
1113
  console.log('Atris is an AI computer for a workspace.');
@@ -1086,23 +1116,103 @@ function printAtrisOverview() {
1086
1116
  console.log('');
1087
1117
  }
1088
1118
 
1089
- async function interactiveEntry(userInput) {
1119
+ function useInteractiveAtrisUi() {
1120
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY && !process.env.ATRIS_NO_INTERACTIVE);
1121
+ }
1122
+
1123
+ function initNonInteractiveFlag() {
1124
+ const args = process.argv.slice(2);
1125
+ return args.includes('--yes') || args.includes('-y');
1126
+ }
1127
+
1128
+ function shouldSkipContextGatherer() {
1129
+ return !useInteractiveAtrisUi() || initNonInteractiveFlag();
1130
+ }
1131
+
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 = {}) {
1090
1170
  const workspaceDir = process.cwd();
1091
1171
  const state = detectWorkspaceState(workspaceDir);
1092
1172
  const context = loadContext(workspaceDir);
1093
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
+
1094
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
+ }
1095
1196
  printAtrisOverview();
1096
1197
  return;
1097
1198
  }
1098
1199
 
1099
1200
  // Fresh install - offer init
1100
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
+ }
1101
1212
  console.log('\nNo atris/ folder found.');
1102
1213
  console.log('');
1103
- console.log('Start here:');
1104
- console.log(' atris init if Atris is installed globally');
1105
- 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');
1106
1216
  return;
1107
1217
  }
1108
1218
 
@@ -1130,6 +1240,55 @@ async function interactiveEntry(userInput) {
1130
1240
  // commitment that hasn't been closed yet.
1131
1241
  const activeMissions = loadActiveMissions(workspaceDir);
1132
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
+ }
1133
1292
  // Mission needs a tick when: it has a verifier configured AND that verifier
1134
1293
  // hasn't passed yet. Planning-state missions count too — first tick is what
1135
1294
  // moves them to running.
@@ -1139,68 +1298,89 @@ async function interactiveEntry(userInput) {
1139
1298
 
1140
1299
  // Build status line
1141
1300
  const parts = [];
1142
- const wipCount = inProgressTasksCount + inProgressFeaturesCount;
1143
1301
  if (wipCount > 0) {
1144
- parts.push(`WIP: ${wipCount}`);
1302
+ parts.push(`work in progress: ${wipCount}`);
1145
1303
  }
1146
1304
  if (liveMissionsCount > 0) {
1147
- parts.push(`Missions: ${liveMissionsCount}`);
1305
+ parts.push(`missions: ${liveMissionsCount}`);
1148
1306
  }
1149
1307
  if (inboxCount > 0) {
1150
- parts.push(`Inbox: ${inboxCount}`);
1308
+ parts.push(`inbox: ${inboxCount}`);
1151
1309
  }
1152
1310
  if (backlogCount > 0) {
1153
- parts.push(`Backlog: ${backlogCount}`);
1311
+ parts.push(`backlog: ${backlogCount}`);
1154
1312
  }
1155
1313
  if (completedTasksCount > 0) {
1156
- parts.push(`Done: ${completedTasksCount}`);
1314
+ parts.push(`done: ${completedTasksCount}`);
1157
1315
  }
1158
- const statusLine = parts.length > 0 ? parts.join(' | ') : 'Clean slate';
1316
+ const statusLine = parts.length > 0 ? parts.join(' | ') : 'clean slate';
1159
1317
 
1160
1318
  console.log('');
1161
- console.log('┌─────────────────────────────────────────────────────────────┐');
1162
- console.log('│ CONTEXT LOADED │');
1163
- console.log('├─────────────────────────────────────────────────────────────┤');
1164
- console.log(`│ ${statusLine.padEnd(60)}│`);
1165
- console.log('└─────────────────────────────────────────────────────────────┘');
1319
+ const contextRow = (label, value) => ` ${label.padEnd(9)}${value}`;
1320
+ console.log(contextRow('context', 'loaded'));
1321
+ console.log(contextRow('status', statusLine));
1166
1322
 
1167
- const mapStatus = context.mapStatus || (context.mapExists ? 'ready' : 'missing');
1168
- if (shouldGatherContext({
1169
- root: workspaceDir,
1170
- userInput,
1171
- mapStatus,
1172
- liveMissionsCount,
1173
- wipCount,
1174
- backlogCount,
1175
- inboxCount,
1176
- })) {
1177
- const answer = String(userInput || '').trim() || await askContextGatherer(workspaceDir);
1178
- if (isAtrisMetaQuestion(answer)) {
1179
- printAtrisOverview();
1323
+ if (gatherContext) {
1324
+ const hotAnswer = String(userInput || '').trim();
1325
+ if (hotAnswer) {
1326
+ const answer = hotAnswer;
1327
+ if (isAtrisMetaQuestion(answer)) {
1328
+ printAtrisOverview();
1329
+ return;
1330
+ }
1331
+ const profile = saveContextProfile(workspaceDir, answer, { source: 'hot_start' });
1332
+ const starter = createStarterTask(workspaceDir, answer);
1333
+ console.log('');
1334
+ console.log('Got it. I saved your first direction.');
1335
+ console.log(`Focus: ${profile.first_answer}`);
1336
+ if (starter && starter.display_id) {
1337
+ console.log(`First task: ${starter.display_id} — ${starter.title}`);
1338
+ } else if (starter && starter.title) {
1339
+ console.log(`First task: ${starter.title}`);
1340
+ }
1341
+ if (mapStatus !== 'ready') {
1342
+ printStarterTaskNext(starter);
1343
+ return;
1344
+ }
1345
+ await planCmd(answer);
1180
1346
  return;
1181
1347
  }
1182
- if (!answer.trim()) {
1348
+ if (shouldSkipContextGatherer()) {
1183
1349
  console.log('');
1184
- console.log('No problem. When you are ready, answer in normal words.');
1185
- console.log('Example: "help me organize college applications" or "help me build a small website".');
1350
+ if (process.argv.includes('--verbose')) {
1351
+ console.log('context gatherer skipped (non-interactive).');
1352
+ }
1353
+ printFirstUseNext();
1186
1354
  return;
1187
- }
1188
- const profile = saveContextProfile(workspaceDir, answer, { source: userInput ? 'hot_start' : 'cold_start' });
1189
- const starter = createStarterTask(workspaceDir, answer);
1190
- console.log('');
1191
- console.log('Got it. I saved your first direction.');
1192
- console.log(`Focus: ${profile.first_answer}`);
1193
- if (starter && starter.display_id) {
1194
- console.log(`First task: ${starter.display_id} — ${starter.title}`);
1195
- } else if (starter && starter.title) {
1196
- console.log(`First task: ${starter.title}`);
1197
- }
1198
- if (mapStatus !== 'ready') {
1199
- printMapBootstrap({ userInput: answer, prefix: 'Next setup step' });
1355
+ } else {
1356
+ const answer = await askContextGatherer(workspaceDir);
1357
+ if (isAtrisMetaQuestion(answer)) {
1358
+ printAtrisOverview();
1359
+ return;
1360
+ }
1361
+ if (!answer.trim()) {
1362
+ console.log('');
1363
+ console.log('No problem. When you are ready, answer in normal words.');
1364
+ console.log('Example: "help me organize college applications" or "help me build a small website".');
1365
+ return;
1366
+ }
1367
+ const profile = saveContextProfile(workspaceDir, answer, { source: 'cold_start' });
1368
+ const starter = createStarterTask(workspaceDir, answer);
1369
+ console.log('');
1370
+ console.log('Got it. I saved your first direction.');
1371
+ console.log(`Focus: ${profile.first_answer}`);
1372
+ if (starter && starter.display_id) {
1373
+ console.log(`First task: ${starter.display_id} — ${starter.title}`);
1374
+ } else if (starter && starter.title) {
1375
+ console.log(`First task: ${starter.title}`);
1376
+ }
1377
+ if (mapStatus !== 'ready') {
1378
+ printStarterTaskNext(starter);
1379
+ return;
1380
+ }
1381
+ await planCmd(answer);
1200
1382
  return;
1201
1383
  }
1202
- await planCmd(answer);
1203
- return;
1204
1384
  }
1205
1385
 
1206
1386
  if (mapStatus !== 'ready') {
@@ -1330,54 +1510,62 @@ function printMapBootstrap({ userInput, prefix = 'Bootstrap required' } = {}) {
1330
1510
  console.log('');
1331
1511
  }
1332
1512
 
1333
- // 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.
1334
1516
  function showWelcomeVisualization() {
1335
- const { getTaskCounts } = require('../lib/state-detection');
1517
+ const { getTaskGlance } = require('../lib/state-detection');
1336
1518
  const { readEndgameState } = require('../commands/autopilot');
1337
1519
  const cwd = process.cwd();
1338
1520
  const atrisDir = path.join(cwd, 'atris');
1339
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));
1340
1525
 
1341
- // Gather workspace stats
1342
- let filesIndexed = 0;
1343
- let tasksInBacklog = 0;
1344
- let tasksInProgress = 0;
1345
- let tasksInReview = 0;
1346
- let tasksCertified = 0;
1526
+ let glance = {
1527
+ backlog: 0, active: 0, review: 0, reviewCertified: 0,
1528
+ activeTitles: [], backlogTitles: [], certifiedTitles: []
1529
+ };
1347
1530
  let journalEntries = 0;
1348
- let hasMap = false;
1349
- let isInitialized = fs.existsSync(atrisDir);
1531
+ let latestBriefName = '';
1532
+ let latestBriefTitle = '';
1533
+ const isInitialized = fs.existsSync(atrisDir);
1350
1534
  let endgameState = { slug: 'unset', horizon: '' };
1351
1535
 
1352
1536
  if (isInitialized) {
1353
- // Check MAP.md
1354
- const mapPath = path.join(atrisDir, 'MAP.md');
1355
- if (fs.existsSync(mapPath)) {
1356
- hasMap = true;
1357
- const mapContent = fs.readFileSync(mapPath, 'utf8');
1358
- // Count file references (lines with file paths)
1359
- const fileRefs = mapContent.match(/`[^`]+\.(js|ts|py|go|rs|md|json|yaml|yml)`/g);
1360
- filesIndexed = fileRefs ? fileRefs.length : 0;
1361
- }
1362
-
1363
- // Task lane counts — DB truth first, TODO.md parse as fallback
1364
1537
  try {
1365
- const counts = getTaskCounts(atrisDir);
1366
- tasksInBacklog = counts.backlog;
1367
- tasksInProgress = counts.active;
1368
- tasksInReview = counts.review;
1369
- tasksCertified = counts.reviewCertified;
1538
+ glance = getTaskGlance(atrisDir);
1370
1539
  } catch {
1371
1540
  // Silently fail - show 0 tasks if reading fails
1372
1541
  }
1373
1542
 
1374
- // Read endgame state
1375
1543
  try {
1376
1544
  endgameState = readEndgameState(cwd);
1377
1545
  } catch {
1378
1546
  // Silently fail - show unset if reading fails
1379
1547
  }
1380
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
+
1381
1569
  // Count journal entries today
1382
1570
  const today = new Date();
1383
1571
  const year = today.getFullYear();
@@ -1392,81 +1580,136 @@ function showWelcomeVisualization() {
1392
1580
  }
1393
1581
 
1394
1582
  console.log('');
1395
- console.log(' ╭──────────────────────────────────────────╮');
1396
- console.log(' │ │');
1397
- console.log(' │ █████╗ ████████╗██████╗ ██╗███████╗ │');
1398
- console.log(' │ ██╔══██╗╚══██╔══╝██╔══██╗██║██╔════╝ │');
1399
- console.log(' │ ███████║ ██║ ██████╔╝██║███████╗ │');
1400
- console.log(' │ ██╔══██║ ██║ ██╔══██╗██║╚════██║ │');
1401
- console.log(' │ ██║ ██║ ██║ ██║ ██║██║███████║ │');
1402
- console.log(' │ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝ │');
1403
- console.log(' │ │');
1404
- 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}`);
1405
1597
  console.log('');
1406
1598
 
1407
1599
  if (!isInitialized) {
1408
- console.log(' ⚡ Spec detected. No workspace found.');
1600
+ console.log(' no atris workspace here yet.');
1409
1601
  console.log('');
1410
- console.log(' ┌─ READY TO INITIALIZE ────────────────────┐');
1411
- console.log(' │ │');
1412
- console.log(` │ 📍 Project: ${projectName.substring(0, 25).padEnd(25)}│`);
1413
- console.log(` │ 📄 Spec: atris.md v${CLI_VERSION.padEnd(18)}│`);
1414
- console.log(' │ │');
1415
- console.log(' │ Run "atris init" to create workspace │');
1416
- console.log(' │ │');
1417
- console.log(' └──────────────────────────────────────────┘');
1418
- } else {
1419
- console.log(' ⚡ Scanning spec...');
1602
+ console.log(row('next', 'atris init (set up this folder)'));
1420
1603
  console.log('');
1421
- console.log(' ┌─ WORKSPACE DETECTED ─────────────────────┐');
1422
- console.log(' │ │');
1423
- console.log(` │ 📍 Project: ${projectName.substring(0, 25).padEnd(25)}│`);
1424
- console.log(` │ 📄 Spec: atris.md v${CLI_VERSION.padEnd(18)}│`);
1425
- console.log(` │ 🗺️ Map: ${hasMap ? (filesIndexed + ' files indexed').padEnd(26) : 'not generated yet'.padEnd(26)}│`);
1426
- console.log(` │ 📋 Tasks: ${(tasksInBacklog + ' backlog, ' + tasksInProgress + ' active').padEnd(26)}│`);
1427
- if (tasksInReview > 0) {
1428
- const reviewText = tasksCertified > 0
1429
- ? `${tasksInReview} waiting (${tasksCertified} certified)`
1430
- : `${tasksInReview} waiting`;
1431
- console.log(` │ ⏳ Review: ${reviewText.padEnd(26)}│`);
1432
- }
1433
- let landInfo = null;
1434
- try { landInfo = require('../commands/land').landSummary(process.cwd()); } catch (err) { landInfo = null; }
1435
- if (landInfo && landInfo.branches > 0) {
1436
- const landText = `${landInfo.branches} in the air, ${landInfo.due} overdue`;
1437
- console.log(` │ 🛬 Land: ${landText.padEnd(26)}│`);
1438
- }
1439
- console.log(` │ 📝 Journal: ${(journalEntries + ' entries today').padEnd(26)}│`);
1440
- if (endgameState.slug !== 'unset' && endgameState.horizon) {
1441
- const endgameLine = endgameState.slug + ' — ' + endgameState.horizon;
1442
- const paddedEndgame = endgameLine.padEnd(26);
1443
- console.log(` │ 🎯 Endgame: ${paddedEndgame}│`);
1444
- }
1445
- console.log(' │ │');
1446
- console.log(' │ ┌──────────────────────────────────┐ │');
1447
- console.log(' │ │ MAP.md ←──── YOU ARE HERE │ │');
1448
- console.log(' │ │ ↓ │ │');
1449
- const taskText = `${tasksInBacklog} task${tasksInBacklog === 1 ? '' : 's'} waiting`;
1450
- console.log(` │ │ TODO.md ←── ${taskText.padEnd(20)}│ │`);
1451
- console.log(' │ │ ↓ │ │');
1452
- console.log(' │ │ navigator → executor → validator│ │');
1453
- console.log(' │ └──────────────────────────────────┘ │');
1454
- console.log(' │ │');
1455
- console.log(' └──────────────────────────────────────────┘');
1604
+ return;
1456
1605
  }
1457
- console.log('');
1458
- if (tasksCertified > 0) {
1459
- console.log(` Ready. ${tasksCertified} certified await accept — run 'atris task reviews'.`);
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`);
1460
1633
  } else {
1461
- let landHint = null;
1462
- try { landHint = require('../commands/land').landSummary(process.cwd()); } catch (err) { landHint = null; }
1463
- if (landHint && landHint.due > 0) {
1464
- console.log(` Ready. ${landHint.due} overdue in the landing — run 'atris land --reap'.`);
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;
1465
1649
  } else {
1466
- console.log(` Ready. Run 'atris plan' to start.`);
1650
+ worktreeDir = path.join(path.dirname(resolved), '.agent-worktrees', path.basename(resolved));
1651
+ }
1652
+ let worktrees = 0;
1653
+ if (fs.existsSync(worktreeDir)) {
1654
+ worktrees = fs.readdirSync(worktreeDir, { withFileTypes: true })
1655
+ .filter((entry) => entry.isDirectory()).length;
1656
+ }
1657
+ let lessonsText = '';
1658
+ try {
1659
+ lessonsText = fs.readFileSync(path.join(atrisDir, 'lessons.md'), 'utf8');
1660
+ } catch (err) {
1661
+ lessonsText = '';
1662
+ }
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 };
1467
1670
  }
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)';
1707
+ } else {
1708
+ next = 'atris plan (plan the first tasks)';
1468
1709
  }
1469
1710
  console.log('');
1711
+ console.log(row('next', next));
1712
+ console.log('');
1470
1713
  }
1471
1714
 
1472
1715
  if (command === 'init') {
@@ -1524,10 +1767,38 @@ if (command === 'init') {
1524
1767
  Promise.resolve(require('../commands/task').run(process.argv.slice(3)))
1525
1768
  .then(() => process.exit(0))
1526
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); });
1527
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
+ };
1528
1795
  Promise.resolve(require('../commands/mission').missionCommand(process.argv.slice(3)))
1529
- .then(() => process.exit(process.exitCode || 0))
1530
- .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);
1531
1802
  } else if (command === 'pulse') {
1532
1803
  // Pulse: durable overnight self-improvement heartbeat (OS cron) for atris-cli.
1533
1804
  Promise.resolve(require('../commands/pulse').pulseCommand(process.argv.slice(3)))
@@ -1550,11 +1821,32 @@ if (command === 'init') {
1550
1821
  Promise.resolve(require('../commands/land').landCommand(process.argv.slice(3)))
1551
1822
  .then((code) => process.exit(code || 0))
1552
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); });
1832
+ } else if (command === 'drive') {
1833
+ // Drive: one self-driving tick — mission doctor -> auto-fix safe findings -> count disengagements.
1834
+ Promise.resolve(require('../commands/drive').driveCommand(process.argv.slice(3)))
1835
+ .then((code) => process.exit(code || 0))
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); });
1553
1841
  } else if (command === 'radar' || command === 'ctop') {
1554
1842
  const radarArgs = command === 'ctop' ? ['--agents', ...process.argv.slice(3)] : process.argv.slice(3);
1555
1843
  Promise.resolve(require('../commands/radar').radarCommand(radarArgs))
1556
1844
  .then((code) => process.exit(code || 0))
1557
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); });
1558
1850
  } else if (command === 'truth') {
1559
1851
  // Truth: one table rolling up mission state, tasks, feature proof receipts, and loop heartbeats.
1560
1852
  Promise.resolve(require('../commands/truth').truthCommand(process.argv.slice(3)))
@@ -1574,6 +1866,22 @@ if (command === 'init') {
1574
1866
  Promise.resolve(require('../commands/improve').run(process.argv.slice(3)))
1575
1867
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
1576
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); });
1577
1885
  } else if (command === 'brain') {
1578
1886
  Promise.resolve()
1579
1887
  .then(() => require('../commands/brain').brainCommand(process.argv.slice(3)))
@@ -1613,6 +1921,14 @@ if (command === 'init') {
1613
1921
  } else {
1614
1922
  logCmd();
1615
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
+ }
1616
1932
  } else if (command === 'now') {
1617
1933
  const args = process.argv.slice(3);
1618
1934
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
@@ -1629,6 +1945,8 @@ if (command === 'init') {
1629
1945
  process.exit(0);
1630
1946
  }
1631
1947
  activateCmd();
1948
+ } else if (command === 'watch') {
1949
+ require('../commands/watch').watchAtris();
1632
1950
  } else if (command === 'update' || command === 'sync') {
1633
1951
  const args = process.argv.slice(3);
1634
1952
  const firstSyncArg = process.argv[3];
@@ -1643,6 +1961,11 @@ if (command === 'init') {
1643
1961
  showUpdateHelp(command);
1644
1962
  process.exit(0);
1645
1963
  }
1964
+ if (command === 'update' && args.includes('--self')) {
1965
+ const { updateSelf } = require('../commands/update');
1966
+ const result = updateSelf({ packageRoot: path.join(__dirname, '..') });
1967
+ process.exit(result.ok ? 0 : 1);
1968
+ }
1646
1969
  if (isBusinessSync) {
1647
1970
  Promise.resolve(require('../commands/business-sync').businessSync(process.argv.slice(3)))
1648
1971
  .then(() => process.exit(0))
@@ -1654,7 +1977,10 @@ if (command === 'init') {
1654
1977
  .then(() => process.exit(0))
1655
1978
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1656
1979
  } else {
1657
- syncCmd();
1980
+ syncCmd({
1981
+ dryRun: args.includes('--dry-run'),
1982
+ force: args.includes('--force') || args.includes('--yes') || args.includes('-y'),
1983
+ });
1658
1984
  }
1659
1985
  } else if (command === 'upgrade') {
1660
1986
  const args = process.argv.slice(3);
@@ -1998,15 +2324,36 @@ if (command === 'init') {
1998
2324
  console.error(`✗ Brainstorm failed: ${error.message || error}`);
1999
2325
  process.exit(1);
2000
2326
  });
2001
- } 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') {
2002
2343
  const rawArgs = process.argv.slice(3);
2003
2344
  if (rawArgs.includes('--help') || rawArgs.includes('-h') || rawArgs[0] === 'help') {
2004
2345
  showNextHelp(command);
2005
2346
  process.exit(0);
2006
2347
  }
2007
- const userInput = rawArgs.filter((arg) => !arg.startsWith('-')).join(' ').trim();
2008
- interactiveEntry(userInput || null)
2009
- .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))
2010
2357
  .catch((error) => {
2011
2358
  console.error(`✗ Error: ${error.message || error}`);
2012
2359
  process.exit(1);
@@ -2092,6 +2439,26 @@ if (command === 'init') {
2092
2439
  showVerifyHelp();
2093
2440
  process.exit(0);
2094
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
+ }
2095
2462
  const sectionIdx = process.argv.indexOf('--section');
2096
2463
  if (sectionIdx > 0 && process.argv[sectionIdx + 1]) {
2097
2464
  const slug = process.argv[3] && !process.argv[3].startsWith('--') ? process.argv[3] : null;
@@ -2112,12 +2479,24 @@ if (command === 'init') {
2112
2479
  .then(() => process.exit(0))
2113
2480
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2114
2481
  } else if (command === 'search') {
2115
- const keyword = process.argv.slice(3).join(' ');
2116
- 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); });
2117
2488
  } else if (command === 'xp') {
2118
2489
  require('../commands/xp').xpCommand(...process.argv.slice(3))
2119
2490
  .then(() => { process.exitCode = 0; })
2120
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));
2121
2500
  } else if (command === 'play') {
2122
2501
  require('../commands/play').playCommand(...process.argv.slice(3))
2123
2502
  .then(() => process.exit(0))
@@ -2126,6 +2505,21 @@ if (command === 'init') {
2126
2505
  require('../commands/gm').gmCommand(...process.argv.slice(3))
2127
2506
  .then(() => process.exit(0))
2128
2507
  .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
2508
+ } else if (command === 'github') {
2509
+ const status = require('../commands/github').githubCommand(process.argv.slice(3));
2510
+ process.exit(status);
2511
+ } else if (command === 'vercel') {
2512
+ const status = require('../commands/vercel').vercelCommand(process.argv.slice(3));
2513
+ process.exit(status);
2514
+ } else if (command === 'supabase') {
2515
+ const status = require('../commands/supabase').supabaseCommand(process.argv.slice(3));
2516
+ process.exit(status);
2517
+ } else if (command === 'linear') {
2518
+ const status = require('../commands/linear').linearCommand(process.argv.slice(3));
2519
+ process.exit(status);
2520
+ } else if (command === 'stripe') {
2521
+ const status = require('../commands/stripe').stripeCommand(process.argv.slice(3));
2522
+ process.exit(status);
2129
2523
  } else if (command === 'gmail') {
2130
2524
  const { gmailCommand } = require('../commands/integrations');
2131
2525
  const subcommand = process.argv[3];
@@ -2217,10 +2611,15 @@ if (command === 'init') {
2217
2611
  require('../commands/align').alignAtris()
2218
2612
  .then(() => process.exit(0))
2219
2613
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2614
+ } else if (command === 'cloud') {
2615
+ require('../commands/cloud').cloudAtris();
2220
2616
  } else if (command === 'terminal') {
2221
2617
  require('../commands/terminal').terminalAtris()
2222
2618
  .then(() => process.exit(0))
2223
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); });
2224
2623
  } else if (command === 'x') {
2225
2624
  // Fast Agent SDK execution - like "atris x echo hello" or "atris x ls -la"
2226
2625
  const userInput = process.argv.slice(3).join(' ').trim();
@@ -2268,10 +2667,19 @@ if (command === 'init') {
2268
2667
  require('../commands/fleet').fleet(args)
2269
2668
  .then(() => process.exit(0))
2270
2669
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2271
- } else if (command === 'loops') {
2670
+ } else if (command === 'loops' || command === 'self-improve') {
2272
2671
  try {
2273
- require('../commands/loops').loopsCommand(process.argv[3], ...process.argv.slice(4));
2274
- 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);
2275
2683
  } catch (error) {
2276
2684
  console.error(`\n✗ Error: ${error.message || error}`);
2277
2685
  process.exit(1);
@@ -2326,10 +2734,13 @@ if (command === 'init') {
2326
2734
  } else if (command === 'engine' || command === 'engines') {
2327
2735
  // Engine: bring any intelligence — roster of installed coding CLIs, default
2328
2736
  // engine per workspace, --engine <name> rides any loop for one run.
2329
- try {
2330
- const engineArgs = command === 'engines' && process.argv.length <= 3 ? [] : process.argv.slice(3);
2331
- process.exit(require('../commands/engine').engineCommand(engineArgs) || 0);
2332
- } catch (err) { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); }
2737
+ // `engine dispatch` runs an async claim -> build -> ship flight, so the
2738
+ // return value may be a promise or a plain number; Promise.resolve handles
2739
+ // both (mirrors the `compile` command dispatch just above).
2740
+ const engineArgs = command === 'engines' && process.argv.length <= 3 ? [] : process.argv.slice(3);
2741
+ Promise.resolve(require('../commands/engine').engineCommand(engineArgs))
2742
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2743
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2333
2744
  } else if (command === 'sign') {
2334
2745
  // Sign: prepare-commit-msg hook that credits Atris as co-author on commits in atris workspaces.
2335
2746
  try { process.exit(require('../commands/sign').signCommand(process.argv[3]) || 0); }
@@ -2339,6 +2750,11 @@ if (command === 'init') {
2339
2750
  Promise.resolve(require('../commands/slop').slopCommand(process.argv.slice(3)))
2340
2751
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
2341
2752
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2753
+ } else if (command === 'write') {
2754
+ // Write: guided writing sessions — human types every word, atris structures + reviews (plan-do-review for prose).
2755
+ Promise.resolve(require('../commands/write').writeCommand(process.argv.slice(3)))
2756
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2757
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2342
2758
  } else if (command === 'strings') {
2343
2759
  // Strings: content design system from live code (no LLM). Extracts UI strings, flags variants,
2344
2760
  // enforces preferred terms at the gate. Exit 1 = variants/banned terms found, for CI + the gate.
@@ -2361,11 +2777,27 @@ if (command === 'init') {
2361
2777
  Promise.resolve(require('../commands/moves').movesCommand(process.argv.slice(3)))
2362
2778
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
2363
2779
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2780
+ } else if (command === 'unknowns') {
2781
+ // Unknowns: blindspot pass from local territory context into the SQLite ledger.
2782
+ Promise.resolve(require('../commands/unknowns').unknownsCommand(process.argv.slice(3)))
2783
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2784
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2785
+ } else if (command === 'feed') {
2786
+ // Feed: read and post the business group feed — receipts and state changes only.
2787
+ // Sets exitCode instead of process.exit() so large --json output flushes fully.
2788
+ Promise.resolve(require('../commands/feed').feedCommand(process.argv.slice(3)))
2789
+ .then((code) => { process.exitCode = typeof code === 'number' ? code : 0; })
2790
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exitCode = 1; });
2364
2791
  } else if (command === 'clarity') {
2365
2792
  // Clarity: interview yourself once; agents read how you work so you stop repeating it.
2366
2793
  Promise.resolve(require('../commands/clarity').clarityCommand(process.argv.slice(3)))
2367
2794
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
2368
2795
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2796
+ } else if (command === 'interview') {
2797
+ // Interview: the interlinked 1-on-1 — live interview that extracts judgment into a member file.
2798
+ Promise.resolve(require('../commands/interview').interviewCommand(process.argv.slice(3)))
2799
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2800
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2369
2801
  } else if (command === 'deck') {
2370
2802
  // Deck: premium Google Slides from a plain content spec, via the Atris deck engine (anti-slop design system).
2371
2803
  Promise.resolve(require('../commands/deck').run(process.argv.slice(3)))
@@ -2386,6 +2818,15 @@ if (command === 'init') {
2386
2818
  Promise.resolve(require('../commands/card').run(process.argv.slice(3)))
2387
2819
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
2388
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); });
2389
2830
  } else if (command === 'reel') {
2390
2831
  // Reel: one line of text into a short on-brand video (an animated card; frames via Chrome + ffmpeg).
2391
2832
  Promise.resolve(require('../commands/reel').run(process.argv.slice(3)))
@@ -2734,8 +3175,19 @@ async function agentAtris() {
2734
3175
  async function chatAtris() {
2735
3176
  // Get message from command line args; --agent forces the legacy cloud-agent lane
2736
3177
  const rawArgs = process.argv.slice(3);
2737
- const agentLane = rawArgs.includes('--agent');
2738
- const message = rawArgs.filter(arg => arg !== '--agent').join(' ').trim();
3178
+ const fastLaneFlags = [];
3179
+ const messageArgs = [];
3180
+ let agentLane = false;
3181
+ for (const arg of rawArgs) {
3182
+ if (arg === '--agent') {
3183
+ agentLane = true;
3184
+ } else if (arg === '--print' || arg === '--headless') {
3185
+ fastLaneFlags.push(arg);
3186
+ } else {
3187
+ messageArgs.push(arg);
3188
+ }
3189
+ }
3190
+ const message = messageArgs.join(' ').trim();
2739
3191
 
2740
3192
  // Respect -h / --help before any auth/state checks
2741
3193
  if (message === '-h' || message === '--help' || message === 'help') {
@@ -2746,6 +3198,7 @@ async function chatAtris() {
2746
3198
  console.log('');
2747
3199
  console.log(' atris chat Interactive chat (ax --fast --chat)');
2748
3200
  console.log(' atris chat "what now?" One-shot message (ax --fast)');
3201
+ console.log(' atris chat --print "..." Headless JSON result (ax --fast --print)');
2749
3202
  console.log(' atris chat --agent [...] Legacy cloud-agent lane (needs `atris agent`)');
2750
3203
  process.exit(0);
2751
3204
  }
@@ -2757,7 +3210,7 @@ async function chatAtris() {
2757
3210
  process.exit(1);
2758
3211
  }
2759
3212
 
2760
- const missionIntent = missionRunIntentFromFastMessage(message);
3213
+ const missionIntent = fastLaneFlags.length ? null : missionRunIntentFromFastMessage(message);
2761
3214
  if (missionIntent) {
2762
3215
  process.exit(await runLocalFastMission(missionIntent));
2763
3216
  }
@@ -2770,7 +3223,7 @@ async function chatAtris() {
2770
3223
  if (!agentLane) {
2771
3224
  try {
2772
3225
  const axPath = path.join(__dirname, '..', 'ax');
2773
- const axArgs = message ? ['--fast', message] : ['--fast', '--chat'];
3226
+ const axArgs = message || fastLaneFlags.length ? ['--fast', ...fastLaneFlags, message].filter(Boolean) : ['--fast', '--chat'];
2774
3227
  const run = spawnSync(process.execPath, [axPath, ...axArgs], { stdio: 'inherit' });
2775
3228
  process.exit(run.status || 0);
2776
3229
  } catch {