atris 3.52.0 → 3.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/FOR_AGENTS.md +7 -0
  2. package/README.md +1 -1
  3. package/atris/policies/ANTISLOP.md +2 -1
  4. package/atris/skills/copy-editor/SKILL.md +2 -1
  5. package/atris/skills/design/SKILL.md +1 -1
  6. package/atris/skills/engines/SKILL.md +8 -7
  7. package/atris/skills/x-search/SKILL.md +24 -22
  8. package/atris/skills/youtube/SKILL.md +89 -40
  9. package/atris/team/_template/SOUL.md +40 -0
  10. package/atris/team/validator/MEMBER.md +1 -2
  11. package/ax +592 -91
  12. package/bin/atris +21 -0
  13. package/bin/atris.js +580 -378
  14. package/commands/api-key.js +170 -0
  15. package/commands/auth.js +453 -43
  16. package/commands/autoland.js +3 -1
  17. package/commands/autopilot-front.js +58 -11
  18. package/commands/avail.js +5 -4
  19. package/commands/balance.js +55 -0
  20. package/commands/bench.js +29 -1
  21. package/commands/brain.js +63 -12
  22. package/commands/brainstorm.js +18 -0
  23. package/commands/business.js +149 -1
  24. package/commands/chat-scan.js +17 -4
  25. package/commands/close.js +5 -1
  26. package/commands/computer.js +2 -2
  27. package/commands/doctor.js +174 -0
  28. package/commands/dream.js +7 -0
  29. package/commands/drill.js +34 -2
  30. package/commands/engine.js +47 -35
  31. package/commands/errors.js +58 -8
  32. package/commands/experiments.js +14 -0
  33. package/commands/feedback.js +61 -2
  34. package/commands/fleet-report.js +27 -7
  35. package/commands/fleet.js +98 -11
  36. package/commands/founder.js +60 -21
  37. package/commands/human-missions.js +79 -4
  38. package/commands/improve.js +54 -4
  39. package/commands/init.js +68 -92
  40. package/commands/install.js +40 -0
  41. package/commands/integrations.js +399 -30
  42. package/commands/interview.js +8 -0
  43. package/commands/land.js +48 -17
  44. package/commands/learn.js +29 -4
  45. package/commands/log.js +51 -0
  46. package/commands/member.js +68 -10
  47. package/commands/mission.js +339 -74
  48. package/commands/now.js +25 -6
  49. package/commands/orb.js +97 -2
  50. package/commands/pack.js +83 -9
  51. package/commands/playbook.js +381 -0
  52. package/commands/plugin.js +16 -0
  53. package/commands/radar.js +96 -40
  54. package/commands/recap.js +128 -21
  55. package/commands/release.js +102 -2
  56. package/commands/review.js +5 -10
  57. package/commands/revisions.js +55 -0
  58. package/commands/run-front.js +12 -4
  59. package/commands/scout.js +12 -1
  60. package/commands/search.js +123 -28
  61. package/commands/sign.js +2 -2
  62. package/commands/signup.js +12 -3
  63. package/commands/site-deploy.js +17 -4
  64. package/commands/site.js +2 -2
  65. package/commands/skill-eval.js +289 -0
  66. package/commands/skill.js +56 -6
  67. package/commands/social.js +603 -0
  68. package/commands/spaceship.js +69 -6
  69. package/commands/stream.js +8 -1
  70. package/commands/study.js +32 -19
  71. package/commands/sync.js +28 -22
  72. package/commands/task.js +418 -60
  73. package/commands/teach.js +72 -63
  74. package/commands/terminal.js +25 -9
  75. package/commands/topup.js +88 -0
  76. package/commands/truth.js +52 -6
  77. package/commands/usage.js +91 -0
  78. package/commands/verify.js +3 -2
  79. package/commands/version.js +3 -1
  80. package/commands/voice.js +5 -1
  81. package/commands/who.js +69 -11
  82. package/commands/wiki.js +7 -4
  83. package/commands/wish.js +76 -5
  84. package/commands/workflow.js +327 -145
  85. package/commands/worktree.js +1 -0
  86. package/commands/write.js +5 -0
  87. package/commands/x-search.js +340 -0
  88. package/commands/xp.js +5 -1
  89. package/commands/youtube.js +551 -33
  90. package/lib/account-bound.js +47 -0
  91. package/lib/auto-accept-certified.js +10 -2
  92. package/lib/auto-lessons.js +186 -0
  93. package/lib/autoland.js +4 -2
  94. package/lib/chat-log-scan.js +7 -4
  95. package/lib/cli-json.js +77 -0
  96. package/lib/cli-scope.js +26 -0
  97. package/lib/codex-flight.js +1 -0
  98. package/lib/conductor-artifacts.js +1 -1
  99. package/lib/context-gatherer.js +11 -0
  100. package/lib/developer-api.js +116 -0
  101. package/lib/engine-ask.js +11 -3
  102. package/lib/engine-registry.js +32 -7
  103. package/lib/feedback-triage.js +274 -0
  104. package/lib/first-minute.js +398 -0
  105. package/lib/fleet.js +115 -34
  106. package/lib/functional-owner.js +22 -0
  107. package/lib/known-commands.js +31 -5
  108. package/lib/member-scaffold.js +197 -0
  109. package/lib/mission-ledger-compact.js +127 -0
  110. package/lib/mission-root.js +4 -2
  111. package/lib/mission-runtime-loop.js +30 -2
  112. package/lib/next-moves.js +34 -34
  113. package/lib/noninteractive.js +88 -0
  114. package/lib/permission-grants.js +10 -1
  115. package/lib/policy-lessons.js +4 -1
  116. package/lib/revision-metric.js +279 -0
  117. package/lib/runner-command.js +5 -4
  118. package/lib/scratch-root.js +48 -0
  119. package/lib/skill-eval-gate.js +249 -0
  120. package/lib/task-db.js +22 -2
  121. package/lib/task-proof.js +75 -9
  122. package/lib/task-receipt.js +5 -1
  123. package/lib/verifier-quality.js +69 -0
  124. package/lib/wish-audit.js +2 -2
  125. package/lib/wish-delegate.js +10 -1
  126. package/lib/workspace-scaffold.js +270 -0
  127. package/package.json +4 -2
  128. package/scripts/det/checklist-score.js +191 -0
  129. package/scripts/det/ytsearch +31 -0
  130. package/scripts/outbound-artifact-gate.js +227 -0
  131. package/utils/update-check.js +15 -0
package/bin/atris.js CHANGED
@@ -41,7 +41,13 @@ const {
41
41
  autoUpdate,
42
42
  inspectInstallGitState,
43
43
  formatInstallGitWarning,
44
+ formatCliVersionLine,
44
45
  } = require('../utils/update-check');
46
+ const {
47
+ requireAccountBound,
48
+ refuseAccountGlobal,
49
+ } = require('../lib/account-bound');
50
+ const { isHelpToken, argsWantHelp } = require('../lib/noninteractive');
45
51
 
46
52
  // State detection for smart default
47
53
  const { detectWorkspaceState, loadContext } = require('../lib/state-detection');
@@ -52,6 +58,12 @@ const {
52
58
  isAtrisMetaQuestion,
53
59
  renderPrompt: renderContextGathererPrompt,
54
60
  } = require('../lib/context-gatherer');
61
+ const {
62
+ buildFirstMinute,
63
+ freshMinuteJson,
64
+ isBareAtrisFlag,
65
+ shouldAutoInitFresh,
66
+ } = require('../lib/first-minute');
55
67
 
56
68
  // Journal & config utilities (canonical modules)
57
69
  const { getLogPath, ensureLogDirectory, createLogFile } = require('../lib/file-ops');
@@ -75,7 +87,7 @@ const {
75
87
  DEFAULT_CLIENT_ID, DEFAULT_USER_AGENT,
76
88
  } = require('../utils/api');
77
89
  const missionRuntime = require('../lib/mission-runtime-loop');
78
- const { knownCommands, suggestCommand } = require('../lib/known-commands');
90
+ const { knownCommands, suggestCommand, suggestCommands } = require('../lib/known-commands');
79
91
  const { recordUsage } = require('../lib/usage');
80
92
 
81
93
  // Bind DI wrappers (utils/auth uses dependency injection for apiRequestJson)
@@ -153,6 +165,12 @@ function parseNaturalEntryArgs(args = []) {
153
165
  asJson = true;
154
166
  continue;
155
167
  }
168
+ if (value === '--yes' || value === '-y' || value === '--verbose') {
169
+ continue;
170
+ }
171
+ if (isHelpToken(value)) {
172
+ continue;
173
+ }
156
174
  const inlineFlag = NATURAL_VALUE_FLAGS.find((name) => value.startsWith(`${name}=`));
157
175
  if (inlineFlag) {
158
176
  const optionValue = value.slice(inlineFlag.length + 1);
@@ -262,7 +280,13 @@ const isBusinessSyncSafetyCommand = command === 'sync'
262
280
 
263
281
  // Auto-sync skills only for commands that modify workspace state. Help must
264
282
  // stay read-only, including global ~/.claude and ~/.codex skill directories.
265
- if (!helpRequested && !dryRunRequested && (['init', 'update', 'upgrade'].includes(command) || (command === 'sync' && !isBusinessSyncSafetyCommand))) {
283
+ // update/sync stay plan-only until --yes, so do not write skills on a bare update.
284
+ const updateWriteYes = updateArgs.includes('--yes') || updateArgs.includes('-y');
285
+ if (!helpRequested && !dryRunRequested && (
286
+ ['init', 'upgrade'].includes(command)
287
+ || (command === 'update' && updateWriteYes)
288
+ || (command === 'sync' && !isBusinessSyncSafetyCommand && updateWriteYes)
289
+ )) {
266
290
  try {
267
291
  const { syncSkills } = require('../commands/sync');
268
292
  const skillsUpdated = syncSkills({ silent: true });
@@ -446,40 +470,83 @@ function consoleCmd() {
446
470
  process.exit(result.status ?? 0);
447
471
  }
448
472
 
449
- function showHelp() {
473
+ function showStartHelp() {
474
+ console.log('');
475
+ console.log('Usage: atris start [options]');
476
+ console.log('');
477
+ console.log('Starts the durable mission loop for one useful self-improvement mission.');
478
+ console.log('');
479
+ console.log('Options:');
480
+ console.log(' --json Print the mission route without starting it.');
481
+ console.log(' --owner X Override mission owner.');
482
+ console.log(' --cadence X Override mission cadence.');
483
+ console.log('');
484
+ }
485
+
486
+ function showHelpShort() {
487
+ console.log('atris: you say what you want; atris builds it, checks it, and shows proof.');
488
+ console.log('');
489
+ console.log('Golden path:');
490
+ console.log(' atris init [--yes] [--minimal] scaffold this project');
491
+ console.log(' atris load context (MAP, tasks, journal)');
492
+ console.log(' atris task new "..." file work');
493
+ console.log(' atris task claim <id> --as <m> take ownership');
494
+ console.log(' atris task ready <id> --proof "..."');
495
+ console.log(' atris review validate');
496
+ console.log(' atris recap spoken recap of what landed');
497
+ console.log(' atris mission status active goals + next move');
498
+ console.log('');
499
+ console.log('More: atris help --all · machine list: atris help --json');
500
+ }
501
+
502
+ function helpCatalog() {
503
+ return [
504
+ { name: 'init', summary: 'scaffold atris/ in this project', json: false, interactive_risk: 'medium' },
505
+ { name: 'task', summary: 'local task plane: new, claim, ready, accept', json: true, interactive_risk: 'low' },
506
+ { name: 'review', summary: 'validate work and capture learnings', json: false, interactive_risk: 'low' },
507
+ { name: 'recap', summary: 'spoken recap of what your team did', json: true, interactive_risk: 'low' },
508
+ { name: 'mission', summary: 'durable goal + owner + verifier + tick', json: true, interactive_risk: 'medium' },
509
+ { name: 'log', summary: 'append an idea to today inbox (or --repl)', json: false, interactive_risk: 'medium' },
510
+ { name: 'wish', summary: 'headless inbox / mission intake', json: true, interactive_risk: 'low' },
511
+ { name: 'learn', summary: 'structured project learnings', json: false, interactive_risk: 'low' },
512
+ { name: 'activate', summary: 'load atris context for this session', json: false, interactive_risk: 'low' },
513
+ { name: 'status', summary: 'local work and completions', json: true, interactive_risk: 'low' },
514
+ { name: 'engine', summary: 'engine roster, ask, validate, dispatch', json: true, interactive_risk: 'low' },
515
+ { name: 'help', summary: 'this help (default short; --all full)', json: true, interactive_risk: 'none' },
516
+ ];
517
+ }
518
+
519
+ function showHelpAll() {
450
520
  console.log('');
451
521
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
452
522
  console.log('atris');
453
523
  console.log('you say what you want in plain words. atris builds it, checks it, and shows you proof.');
454
524
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
455
525
  console.log('');
456
- console.log('Quick Start:');
526
+ console.log('Quick start:');
457
527
  console.log('');
458
- console.log(' 1. atris Open a persistent AI computer for this workspace');
459
- console.log(' npx atris Same command after a local project install');
460
- console.log(' 2. Describe what you want run, built, researched, or validated');
461
- console.log(' 3. Atris acts with context, memory, tools, and a review loop');
528
+ console.log(' 1. atris load context (MAP, tasks, journal)');
529
+ console.log(' npx atris same after a local install');
530
+ console.log(' 2. say what you want built, checked, or researched');
531
+ console.log(' 3. atris files the work, then you review');
462
532
  console.log('');
463
533
  console.log('Common invocations:');
464
- console.log(' atris "<request>" Build one isolated change, verify it, and stop in Review');
534
+ console.log(' atris "<request>" one isolated change, then Review');
465
535
  console.log(' atris "<request>" --verify "<cmd>" --json');
466
- console.log(' Supply proof explicitly and return one JSON result');
467
- console.log(' atris init [--yes] Global install: initialize this project');
468
- console.log(' npx atris init [--yes] Local install: initialize this project');
536
+ console.log(' atris init [--yes] scaffold this project');
537
+ console.log(' npx atris init [--yes] same after a local install');
538
+ console.log(' atris mission status current mission + next move');
539
+ console.log(' atris task ready <id> --proof "..."');
469
540
  console.log(' atris computer');
470
- console.log(' atris ask "what you want" [--budget <usd>]');
471
- console.log(' atris mission Show the current mission');
472
- console.log(' atris approve Approve the step waiting for you');
473
- console.log(' atris stop Stop the current mission');
474
- console.log(' atris check <run-id> Show the checks for a mission');
475
- console.log(' atris ready --json Show which mission features are ready');
476
- console.log(' atris engine validate latest check the newest ask receipt with a cheap referee');
541
+ console.log(' atris engine validate latest');
542
+ console.log(' atris playbook workspace style rules and paired trials');
477
543
  console.log(' atris business init "My Company"');
478
544
  console.log(' atris run');
479
545
  console.log(' atris drill');
480
546
  console.log(' atris status');
481
547
  console.log(' atris soul');
482
548
  console.log(' atris fleet status');
549
+ console.log(' ask/stop/ready see atris mission and atris task ready');
483
550
  console.log('');
484
551
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
485
552
  console.log('');
@@ -490,7 +557,8 @@ function showHelp() {
490
557
  console.log('');
491
558
  console.log('Setup:');
492
559
  console.log(' setup - Guided first-time setup (login, pick business, pull)');
493
- console.log(' init - Initialize Atris in current project (--yes skips prompts)');
560
+ console.log(' init - Initialize Atris in current project (--yes skips prompts; --minimal for lean scaffold)');
561
+ console.log(' install - Install the atris/ brain if missing (alias: workspace install)');
494
562
  console.log(' update - Update local files to latest version');
495
563
  console.log(' upgrade - Install latest Atris from npm');
496
564
  console.log('');
@@ -505,7 +573,7 @@ function showHelp() {
505
573
  console.log(' spaceship - Bounded overnight runner that survives bad ticks and emails updates');
506
574
  console.log('');
507
575
  console.log('Context & tracking:');
508
- console.log(' log - Add ideas to inbox');
576
+ console.log(' log - Add ideas to inbox (`atris log "note"`; `log --repl` for the editor; see also `wish`)');
509
577
  console.log(' wish - Say one plain sentence, then Atris asks only for gaps or delegates it');
510
578
  console.log(' now - Show atris/now.md, the current operating truth');
511
579
  console.log(' goal - goal, distance, and what moved today (alias: wtf)');
@@ -518,13 +586,15 @@ function showHelp() {
518
586
  console.log(' team - One team view: members, roles, engine assignments (presence for live)');
519
587
  console.log(' watch - Turn one sentence into an always-on background watcher');
520
588
  console.log(' ctop - Show a process-first live agent CPU/memory view');
589
+ console.log(' doctor - Node/task/auth/workspace readiness (--json for agents)');
521
590
  console.log(' launchpad - Show the next action from local brain, task, mission, and proof state');
522
591
  console.log(' brief - Show the one-glance operator brief');
523
592
  console.log(' status - See local work and completions (`atris status <business>` for remote)');
524
- console.log(' recap - What your AI team did, in plain English (--share for paste-ready)');
593
+ console.log(' recap - What your AI team did, in spoken lines (--verbose for the old report)');
525
594
  console.log(' report - Weekly block: landings, journal completions, and Career XP');
526
595
  console.log(' xp - Show Career XP and contribution graph');
527
596
  console.log(' analytics - Show recent productivity from journals');
597
+ console.log(' revisions - show possible human revisions after atris-assisted landings');
528
598
  console.log(' search - Search workspace memory (atris search <keyword>)');
529
599
  console.log(' clean - Housekeeping (stale tasks, archive journals, broken refs)');
530
600
  console.log(' close - track open loops with deadlines and daily escalation');
@@ -542,14 +612,14 @@ function showHelp() {
542
612
  console.log(' atris autoland tick # second check runs, task lands');
543
613
  console.log(' mission - Goal + loop + member owner + verifier + receipt; --budget quick|long|deep sets bounded tiers');
544
614
  console.log(' decide - Answer open mission asks and send the decision to the next tick');
545
- console.log(' release - Tag release, bump version, create GitHub release, draft /launch');
615
+ console.log(' release - Tag release, bump version, create GitHub release, draft /launch; atris release preflight before a v* tag');
546
616
  console.log(' learn - Project learnings (patterns, pitfalls, preferences)');
547
617
  console.log(' study - On-demand learning feed: ingest topic, start server, open browser');
548
618
  console.log(' rainmaker - Relationship manager dashboard (atrisos-backend/scripts/rainmaker.py)');
549
619
  console.log(' meet - onboard a stranger in one sitting and print their /book link');
550
620
  console.log(' avail - Booking availability (/book/{username} weekly windows)');
551
621
  console.log(' brain - Compile MAP/TODO/wiki/state into a loadable agent brain');
552
- console.log(' lesson - Append a one-line lesson to atris/lessons.md (add --detector validates the falsifier at write time; ledger/revert audit every mutation; mine: distill receipts/episodes/scorecards into policy lessons)');
622
+ console.log(' lesson - atris lesson add <slug> <pass|fail> "<text>" (mine/sweep/resolve/revert/ledger; --detector validates the falsifier at write time)');
553
623
  console.log(' taste - Record the operator\'s keep, kill, and more creative verdicts');
554
624
  console.log(' teach - Turn a bad turn into a failing benchmark, then a permanent guard (red gate: no promotion without a proven failure)');
555
625
  console.log(' ingest - Local-first wiki ingest into atris/wiki/');
@@ -572,7 +642,8 @@ function showHelp() {
572
642
  console.log(' router - inspect ax lane outcomes and promote gated reflex overrides');
573
643
  console.log(' sign - Co-author trailer on every commit in an atris workspace (on/off/status)');
574
644
  console.log(' visualize - Generate a Slack/deck-ready visual from a prompt');
575
- console.log(' youtube - Free local notes, weekly digest, or 5-credit cloud process for YouTube videos');
645
+ console.log(' youtube - Free search + notes, weekly digest, 5-credit paid search, or 5-credit cloud process for YouTube videos');
646
+ console.log(' x-search - 5-credit X/Twitter search (tweet text + x.com links)');
576
647
  console.log('');
577
648
  console.log('Experiments:');
578
649
  console.log(' experiments init [slug] - Prepare atris/experiments/ or scaffold a pack');
@@ -640,12 +711,16 @@ function showHelp() {
640
711
  console.log(' agent - Select cloud agent, spawn worker requests, or run `agent doctor`');
641
712
  console.log(' chat - Chat with Atris 2 Fast in this workspace (--agent for cloud agent; or: atris chat scan)');
642
713
  console.log(' fast - Chat with Atris2 Fast');
643
- console.log(' login - Sign in or add another account');
714
+ console.log(' login - Sign in, add an account, or mint a scoped agent token (--agent)');
644
715
  console.log(' logout - Sign out of current account');
645
716
  console.log(' whoami - Show active account');
646
717
  console.log(' switch - Switch account globally (atris switch <name>)');
647
718
  console.log(' use - Set account for this terminal only (atris use <name>)');
648
719
  console.log(' accounts - Manage accounts (list, add, remove)');
720
+ console.log(' balance - Show credit balance in dollars');
721
+ console.log(' usage - Show developer API usage');
722
+ console.log(' api-key - Create, list, rotate, or revoke a developer API key');
723
+ console.log(' topup - Buy credits and print a Stripe checkout URL');
649
724
  console.log('');
650
725
  console.log('Integrations:');
651
726
  console.log(' github - github cli wrapper (doctor, auth, pr list/create/checks/view)');
@@ -653,7 +728,7 @@ function showHelp() {
653
728
  console.log(' supabase - supabase cli wrapper (doctor, auth, status/db/functions)');
654
729
  console.log(' linear - linear cli wrapper (doctor, auth, issue list/create/view/update)');
655
730
  console.log(' stripe - stripe cli wrapper (doctor, auth, listen/trigger/products)');
656
- console.log(' gmail - Email commands (inbox, read, archive)');
731
+ console.log(' gmail - email commands (connect, inbox, read, archive, accounts, use)');
657
732
  console.log(' calendar - Calendar commands (today, week)');
658
733
  console.log(' twitter - Twitter commands (post)');
659
734
  console.log(' slack - Slack commands (channels)');
@@ -666,6 +741,7 @@ function showHelp() {
666
741
  console.log(' skill list - Show all skills with compliance status');
667
742
  console.log(' skill audit [name] - Validate skill against Anthropic guide');
668
743
  console.log(' skill fix [name] - Auto-fix common compliance issues');
744
+ console.log(' skill eval <path> - Run an independent rubric review and write its receipt');
669
745
  console.log(' skill delete <name> - Delete a skill and its symlinks');
670
746
  console.log('');
671
747
  console.log('Team:');
@@ -682,47 +758,64 @@ function showHelp() {
682
758
  console.log('Feedback:');
683
759
  console.log(' feedback "msg" - Submit feedback');
684
760
  console.log(' feedback - List feedback queue');
761
+ console.log(' feedback triage <id> - Print a reproduction brief (--dispatch to run)');
685
762
  console.log(' feedback resolve <id> "<note>" - Mark resolved (admin)');
686
763
  console.log(' feedback close <id> - Close as wontfix (admin)');
687
764
  console.log(' feedback delete <id> - Delete feedback (admin)');
688
765
  console.log('');
689
766
  console.log('Other:');
690
767
  console.log(' version - Show Atris version');
691
- console.log(' help - Show this help');
768
+ console.log(' help - Short golden path (help --all for every command)');
692
769
  console.log('');
693
- console.log('💡 Tip: Just run "atris" to get started');
770
+ console.log('Tip: Just run "atris" to get started');
694
771
  console.log('');
695
772
  }
696
773
 
774
+ function showHelp(options = {}) {
775
+ const args = options.args || process.argv.slice(3);
776
+ const asJson = options.json === true || args.includes('--json');
777
+ const all = options.all === true || args.includes('--all');
778
+ if (asJson) {
779
+ console.log(JSON.stringify({ ok: true, commands: helpCatalog() }, null, 2));
780
+ return;
781
+ }
782
+ if (all) {
783
+ showHelpAll();
784
+ return;
785
+ }
786
+ showHelpShort();
787
+ }
788
+
697
789
  function showPlanHelp() {
698
790
  console.log('');
699
- console.log('Usage: atris plan [--execute] [--full]');
791
+ console.log('Usage: atris plan [--execute] [--prompt|--verbose|--full]');
700
792
  console.log('');
701
793
  console.log('Description:');
702
- console.log(' Activate the Navigator agent to plan work.');
703
- console.log(' Reads your journal Inbox, TODO.md, MAP.md, and features/, then prints a');
704
- console.log(' short, copy/pasteable prompt for your coding agent.');
794
+ console.log(' Same two spoken lines as bare atris.');
795
+ console.log(' --prompt prints the navigator copy/paste prompt.');
796
+ console.log(' Leftover words stay a request.');
705
797
  console.log('');
706
798
  console.log('Options:');
707
- console.log(' --execute Run in agent mode via Atris cloud (requires login + agent).');
708
- console.log(' --full Print full spec/context dumps (verbose copy/paste).');
709
- console.log(' --verbose Alias for --full.');
799
+ console.log(' --execute ACTION TAKEN: run in agent mode via Atris cloud (requires login + agent).');
800
+ console.log(' --prompt Print the navigator copy/paste prompt.');
801
+ console.log(' --verbose Alias for --prompt, plus full spec dumps.');
802
+ console.log(' --full Alias for --verbose.');
710
803
  console.log('');
711
804
  }
712
805
 
713
806
  function showDoHelp() {
714
807
  console.log('');
715
- console.log('Usage: atris do [--execute] [--full]');
808
+ console.log('Usage: atris do [--execute] [--prompt|--verbose|--full]');
716
809
  console.log('');
717
810
  console.log('Description:');
718
- console.log(' Activate the Executor agent to build tasks.');
719
- console.log(' Reads TODO.md and features/*/build.md, then prints step-by-step');
720
- console.log(' execution instructions (and, in agent mode, edits code + runs commands).');
811
+ console.log(' Same two spoken lines as bare atris.');
812
+ console.log(' --prompt prints the executor copy/paste prompt.');
721
813
  console.log('');
722
814
  console.log('Options:');
723
- console.log(' --execute Run in agent mode via Atris cloud (requires login + agent).');
724
- console.log(' --full Print full spec/context dumps (verbose copy/paste).');
725
- console.log(' --verbose Alias for --full.');
815
+ console.log(' --execute ACTION TAKEN: edit code + run commands via Atris cloud.');
816
+ console.log(' --prompt Print the executor copy/paste prompt.');
817
+ console.log(' --verbose Alias for --prompt, plus full spec dumps.');
818
+ console.log(' --full Alias for --verbose.');
726
819
  console.log('');
727
820
  }
728
821
 
@@ -731,8 +824,9 @@ function showReviewHelp() {
731
824
  console.log('Usage: atris review [--limit N|--all|--json] [--full|--execute]');
732
825
  console.log('');
733
826
  console.log('Description:');
734
- console.log(' Show the certified Review queue: proof-ready work waiting for');
735
- console.log(' human accept or revise. Human accept is the AgentXP gate.');
827
+ console.log(' Show the one finished thing waiting for your ok.');
828
+ console.log(' Certified work leads with atris task accept. Uncertified stays');
829
+ console.log(' still being checked. Use --json/--all for the full queue.');
736
830
  console.log(' Use --full/--verbose for the legacy Validator prompt.');
737
831
  console.log('');
738
832
  console.log('Options:');
@@ -773,6 +867,21 @@ function showAnalyticsHelp() {
773
867
  console.log('');
774
868
  }
775
869
 
870
+ function showRevisionsHelp() {
871
+ console.log('');
872
+ console.log('usage: atris revisions [--json]');
873
+ console.log('');
874
+ console.log('description:');
875
+ console.log(' count possible human revisions after atris-assisted landings.');
876
+ console.log(' uses same-file git follow-ups as a signal, not proof of intent.');
877
+ console.log(' saves one rolling seven-day summary row per utc day.');
878
+ console.log('');
879
+ console.log('options:');
880
+ console.log(' --json print the measured landings and revision signals as json.');
881
+ console.log(' --help, -h show this help.');
882
+ console.log('');
883
+ }
884
+
776
885
  function showActivateHelp() {
777
886
  console.log('');
778
887
  console.log('Usage: atris activate');
@@ -820,15 +929,18 @@ function showVerifyHelp() {
820
929
  function showUpdateHelp(commandName = 'update') {
821
930
  console.log('');
822
931
  const selfFlag = commandName === 'update' ? ' [--self]' : '';
823
- console.log(`Usage: atris ${commandName} [--all] [--dry-run] [--force]${selfFlag}`);
932
+ console.log(`Usage: atris ${commandName} [--all] [--dry-run] [--yes] [--force]${selfFlag}`);
824
933
  console.log('');
825
934
  console.log('Description:');
826
935
  console.log(' Sync Atris workspace files from the installed CLI templates.');
936
+ console.log(' Default is plan-only: print the file list and exit without writing.');
937
+ console.log(' Pass --yes to apply writes. Non-TTY sessions without --yes exit 2.');
827
938
  console.log('');
828
939
  console.log('Options:');
829
940
  console.log(' --all Update Atris files across projects under the current tree.');
830
941
  console.log(' --dry-run Preview update work without writing files.');
831
- console.log(' --force Overwrite existing template files where supported.');
942
+ console.log(' --yes, -y Apply the planned writes.');
943
+ console.log(' --force Overwrite existing template files where supported (with --yes).');
832
944
  if (commandName === 'update') {
833
945
  console.log(' --self Update the installed atris cli from npm (packaged installs only).');
834
946
  }
@@ -852,9 +964,11 @@ function showUpgradeHelp() {
852
964
  function showReleaseHelp() {
853
965
  console.log('');
854
966
  console.log('Usage: atris release [--dry-run]');
967
+ console.log(' atris release preflight');
855
968
  console.log('');
856
969
  console.log('Description:');
857
970
  console.log(' Draft or publish a release from local git history.');
971
+ console.log(' preflight checks master, a clean tree, origin/master, the version tag, and the full test suite with CI=true.');
858
972
  console.log('');
859
973
  console.log('Options:');
860
974
  console.log(' --dry-run Print the planned release without committing, tagging, or pushing.');
@@ -864,9 +978,9 @@ function showReleaseHelp() {
864
978
 
865
979
  function showAuthHelp(commandName) {
866
980
  const usage = {
867
- login: 'Usage: atris login [--token <token>] [--force]',
981
+ login: 'Usage: atris login [--token <token>] [--force] [--agent]',
868
982
  logout: 'Usage: atris logout',
869
- whoami: 'Usage: atris whoami',
983
+ whoami: 'Usage: atris whoami [--json]',
870
984
  switch: 'Usage: atris switch [account] [--global]',
871
985
  use: 'Usage: atris use [account]',
872
986
  accounts: 'Usage: atris accounts [add|remove <account>|remove --all]',
@@ -877,6 +991,7 @@ function showAuthHelp(commandName) {
877
991
  console.log('Description:');
878
992
  if (commandName === 'login') {
879
993
  console.log(' Sign in with browser OAuth or a pasted API token.');
994
+ console.log(' --agent mints a scoped agent token from stored credentials. No browser.');
880
995
  } else if (commandName === 'logout') {
881
996
  console.log(' Sign out of the current Atris account.');
882
997
  } else if (commandName === 'whoami') {
@@ -891,8 +1006,11 @@ function showAuthHelp(commandName) {
891
1006
  console.log('');
892
1007
  console.log('Options:');
893
1008
  if (commandName === 'login') {
894
- console.log(' --token <token> Save an API token without prompting.');
895
- console.log(' --force, -f Re-run login even if credentials already exist.');
1009
+ console.log(' --token <token> Save an API token without prompting.');
1010
+ console.log(' --force, -f Re-run login even if credentials already exist.');
1011
+ console.log(' --agent Mint a scoped agent token from the stored login.');
1012
+ console.log(' --scopes <list> Comma-separated scopes (default: x-search,youtube).');
1013
+ console.log(' --daily-credit-cap <n> Daily credit cap (default: 50).');
896
1014
  } else if (commandName === 'switch') {
897
1015
  console.log(' --global, -g Switch the account for all terminals.');
898
1016
  }
@@ -1002,7 +1120,7 @@ function showAutopilotHelp() {
1002
1120
  }
1003
1121
 
1004
1122
  if (command === 'help' || command === '--help' || command === '-h') {
1005
- showHelp();
1123
+ showHelp({ args: process.argv.slice(3) });
1006
1124
  process.exit(0);
1007
1125
  }
1008
1126
 
@@ -1043,26 +1161,20 @@ if (isSpecFile(command)) {
1043
1161
 
1044
1162
  // --version flag (works anywhere: atris --version, atris -v)
1045
1163
  if (command === '--version' || command === '-v' || process.argv.includes('--version')) {
1046
- console.log(`atris v${CLI_VERSION}`);
1164
+ console.log(formatCliVersionLine(CLI_VERSION, path.join(__dirname, '..')));
1047
1165
  process.exit(0);
1048
1166
  }
1049
1167
 
1050
1168
  // If no command OR command is not recognized, treat as natural language
1051
1169
  // Voice-friendly aliases — natural language → command mapping
1052
1170
  // Solves speech-to-text issues (inspired by gstack v0.14.6 voice-triggers)
1053
- const START_MISSION_OBJECTIVE = 'self improve goal after goal: pick one useful bounded mission from current Atris state, run proof, and continue only with real next work';
1054
-
1171
+ // One-word voice aliases like start/go/audit/deploy must not spawn runners.
1172
+ // Unmatched single verbs exit 2 with did-you-mean (see below). Keep phrases
1173
+ // that name a real command without overnight side effects.
1055
1174
  const voiceTriggers = {
1056
- 'start': '_start',
1057
- 'start now': '_start',
1058
- 'go': '_start',
1059
- 'keep going': '_start',
1060
- 'keepgoing': '_start',
1061
- 'keep-going': '_start',
1062
1175
  'review my code': 'code-review',
1063
1176
  'check my code': 'code-review',
1064
1177
  'run a review': 'code-review',
1065
- 'audit': 'code-review',
1066
1178
  'create a business': 'business',
1067
1179
  'start a business': 'business',
1068
1180
  'new business': 'business',
@@ -1072,7 +1184,6 @@ const voiceTriggers = {
1072
1184
  'what is going on': 'radar',
1073
1185
  'run tests': 'verify',
1074
1186
  'check health': 'status',
1075
- 'deploy': 'business',
1076
1187
  'show my businesses': 'business',
1077
1188
  'pull latest': 'pull',
1078
1189
  'push changes': 'push',
@@ -1080,6 +1191,17 @@ const voiceTriggers = {
1080
1191
  'what did i learn': 'learn',
1081
1192
  };
1082
1193
 
1194
+ // -h/--help/help on argv[1] (including voice words like start) is usage
1195
+ // + exit 2 before NL and before _start. Never file a task titled a flag.
1196
+ if (command && !knownCommands.includes(command) && argsWantHelp(process.argv.slice(2))) {
1197
+ if (String(command).toLowerCase() === 'start') showStartHelp();
1198
+ else {
1199
+ showHelpShort();
1200
+ console.log('Usage: atris help');
1201
+ }
1202
+ process.exit(2);
1203
+ }
1204
+
1083
1205
  if (!command || !knownCommands.includes(command)) {
1084
1206
  // Check voice triggers before falling through to natural language
1085
1207
  const fullInput = process.argv.slice(2).join(' ').toLowerCase().trim();
@@ -1099,11 +1221,31 @@ if (!command || !knownCommands.includes(command)) {
1099
1221
  if (!command || !knownCommands.includes(command)) {
1100
1222
  const rawNaturalArgs = process.argv.slice(2);
1101
1223
  const natural = parseNaturalEntryArgs(rawNaturalArgs);
1224
+ if (command && isBareAtrisFlag(command)) {
1225
+ command = '';
1226
+ }
1102
1227
  const userInput = natural.input;
1103
- const directSingleWordNatural = !natural.multiword
1104
- && SINGLE_WORD_NATURAL_INTENTS.has(userInput.toLowerCase());
1105
1228
 
1106
- if (natural.asJson && !natural.multiword && !directSingleWordNatural) {
1229
+ // An unmatched first token that looks like a command (or a typo of one)
1230
+ // is not a request to create work, even when extra args follow
1231
+ // (`atris taks list`). A lone verb (`atris build`) is also unknown:
1232
+ // never mint a task from argv[1]. Quoted sentences (`atris "build
1233
+ // the site"`), multiword phrases (`atris build a thing`), task new,
1234
+ // and wish still create work.
1235
+ const firstToken = String((natural.positionals && natural.positionals[0]) || command || '').trim();
1236
+ const firstLooksLikeCommand = Boolean(firstToken) && !/\s/.test(firstToken);
1237
+ const firstIsWorkVerb = SINGLE_WORD_NATURAL_INTENTS.has(firstToken.toLowerCase());
1238
+ // `atris test` is the documented first-minute alias, not an unknown command
1239
+ // and not a new first task. Extra words (`atris test the login`) still
1240
+ // create work once MAP is real.
1241
+ const firstMinuteAlias = firstToken.toLowerCase() === 'test' && !natural.multiword;
1242
+ const unknownCommandToken = Boolean(command)
1243
+ && firstLooksLikeCommand
1244
+ && !knownCommands.includes(firstToken)
1245
+ && !firstMinuteAlias
1246
+ && (!natural.multiword || !firstIsWorkVerb);
1247
+
1248
+ if (unknownCommandToken && natural.asJson) {
1107
1249
  console.log(JSON.stringify({
1108
1250
  ok: false,
1109
1251
  error: command ? `unknown command: ${command}` : 'unknown command',
@@ -1114,18 +1256,26 @@ if (!command || !knownCommands.includes(command)) {
1114
1256
  process.exit(2);
1115
1257
  }
1116
1258
 
1117
- // Warn if this looks like a mistyped single-word command (no spaces)
1118
- if (command && !natural.multiword && !directSingleWordNatural) {
1119
- console.log(`⚠ Unknown command: "${command}". Run "atris help" for available commands.`);
1120
- const suggestion = suggestCommand(command);
1121
- if (suggestion) {
1122
- console.log(` Did you mean "atris ${suggestion}"?`);
1259
+ if (unknownCommandToken) {
1260
+ const suggestions = suggestCommands(command);
1261
+ console.error(`Unknown command: "${command}". Run "atris help" for available commands.`);
1262
+ if (suggestions.length) {
1263
+ console.error(`Did you mean ${suggestions.map((name) => `"atris ${name}"`).join(' or ')}?`);
1264
+ } else {
1265
+ console.error('To create work from a sentence, run: atris "<request>" (quoted), or atris task new / atris wish.');
1123
1266
  }
1124
- console.log(' Treating as natural language input...\n');
1267
+ process.exit(2);
1125
1268
  }
1126
1269
 
1127
1270
  // Launch interactive entry (the "Performance")
1128
- interactiveEntry(userInput, { oneLap: true, asJson: natural.asJson, engine: natural.engine, verifier: natural.verifier, optionError: natural.error })
1271
+ interactiveEntry(firstMinuteAlias ? '' : userInput, {
1272
+ oneLap: true,
1273
+ asJson: natural.asJson,
1274
+ engine: natural.engine,
1275
+ verifier: natural.verifier,
1276
+ optionError: natural.error,
1277
+ firstMinuteAlias,
1278
+ })
1129
1279
  .then((code) => process.exit(Number.isInteger(code) ? code : 0))
1130
1280
  .catch((error) => {
1131
1281
  console.error(`✗ Error: ${error.message || error}`);
@@ -1149,7 +1299,12 @@ function printAtrisOverview() {
1149
1299
  }
1150
1300
 
1151
1301
  function useInteractiveAtrisUi() {
1152
- return Boolean(process.stdin.isTTY && process.stdout.isTTY && !process.env.ATRIS_NO_INTERACTIVE);
1302
+ return Boolean(
1303
+ process.stdin.isTTY
1304
+ && process.stdout.isTTY
1305
+ && !process.env.ATRIS_NO_INTERACTIVE
1306
+ && process.env.ATRIS_NONINTERACTIVE !== '1'
1307
+ );
1153
1308
  }
1154
1309
 
1155
1310
  function initNonInteractiveFlag() {
@@ -1189,21 +1344,44 @@ function printFirstUseNext() {
1189
1344
  console.log(`Then: ${firstUseCommand()}`);
1190
1345
  }
1191
1346
 
1192
- function printStarterTaskNext(starter) {
1193
- console.log('next setup: open atris/MAP.md, then claim the starter task.');
1194
- if (starter && starter.display_id) {
1195
- console.log(`Next: atris task claim ${starter.display_id} --as ${localOwnerName()}`);
1196
- return;
1197
- }
1198
- console.log('Next: atris task next --as ' + localOwnerName());
1347
+ function runMinimalInit() {
1348
+ const script = process.argv[1] || path.join(__dirname, 'atris.js');
1349
+ const result = spawnSync(process.execPath, [script, 'init', '--minimal', '--yes'], {
1350
+ cwd: process.cwd(),
1351
+ env: process.env,
1352
+ encoding: 'utf8',
1353
+ stdio: 'inherit',
1354
+ });
1355
+ return Number.isInteger(result.status) ? result.status : 1;
1356
+ }
1357
+
1358
+ function printFirstMinuteScreen(options) {
1359
+ const screen = buildFirstMinute(options);
1360
+ console.log('');
1361
+ console.log(screen.text);
1362
+ return screen;
1199
1363
  }
1200
1364
 
1201
1365
  async function interactiveEntry(userInput, options = {}) {
1202
1366
  const workspaceDir = process.cwd();
1203
1367
  const state = detectWorkspaceState(workspaceDir);
1368
+
1369
+ // Fresh folder is the first-minute product: one next command, no menu.
1370
+ if (state.state === 'fresh') {
1371
+ if (options.asJson) {
1372
+ console.log(JSON.stringify(freshMinuteJson(), null, 2));
1373
+ return 2;
1374
+ }
1375
+ if (shouldAutoInitFresh(process.argv.slice(2))) {
1376
+ return runMinimalInit();
1377
+ }
1378
+ printFirstMinuteScreen({ root: workspaceDir, fresh: true });
1379
+ return 0;
1380
+ }
1381
+
1204
1382
  const context = loadContext(workspaceDir);
1205
1383
 
1206
- if (options.asJson && !String(userInput || '').trim()) {
1384
+ if (options.asJson && !String(userInput || '').trim() && !options.firstMinuteAlias) {
1207
1385
  console.log(JSON.stringify({
1208
1386
  schema: 'atris.one_lap.v1',
1209
1387
  ok: false,
@@ -1229,25 +1407,6 @@ async function interactiveEntry(userInput, options = {}) {
1229
1407
  return;
1230
1408
  }
1231
1409
 
1232
- // Fresh install - offer init
1233
- if (state.state === 'fresh') {
1234
- if (options.asJson) {
1235
- console.log(JSON.stringify({
1236
- schema: 'atris.one_lap.v1',
1237
- ok: false,
1238
- status: 'stuck',
1239
- reason: 'this workspace is not initialized',
1240
- next_action: 'atris init --yes',
1241
- }, null, 2));
1242
- return 2;
1243
- }
1244
- console.log('\nNo atris/ folder found.');
1245
- console.log('');
1246
- console.log('Next: atris init');
1247
- console.log('Local project install instead? Run: npx atris init');
1248
- return;
1249
- }
1250
-
1251
1410
  // Ensure today's journal exists (so "atris" always has somewhere to write/read)
1252
1411
  try {
1253
1412
  ensureLogDirectory();
@@ -1302,6 +1461,29 @@ async function interactiveEntry(userInput, options = {}) {
1302
1461
  return 2;
1303
1462
  }
1304
1463
 
1464
+ // Placeholder or missing MAP is already a first-minute job. Do not send the
1465
+ // operator to a rewrite lobby, and do not treat `test` as a new first task.
1466
+ if (mapStatus !== 'ready') {
1467
+ const screenOptions = {
1468
+ root: workspaceDir,
1469
+ context,
1470
+ missions: activeMissions,
1471
+ };
1472
+ if (options.asJson) {
1473
+ const screen = buildFirstMinute(screenOptions);
1474
+ console.log(JSON.stringify({
1475
+ schema: 'atris.one_lap.v1',
1476
+ ok: false,
1477
+ status: 'stuck',
1478
+ reason: 'the generate-map task is already waiting',
1479
+ next_action: screen.nextCommand,
1480
+ }, null, 2));
1481
+ return 2;
1482
+ }
1483
+ printFirstMinuteScreen(screenOptions);
1484
+ return 0;
1485
+ }
1486
+
1305
1487
  if (userInput && mapStatus === 'ready' && !gatherContext && options.oneLap !== false) {
1306
1488
  return require('../commands/one-lap').runOneLap(userInput, {
1307
1489
  root: workspaceDir,
@@ -1316,75 +1498,17 @@ async function interactiveEntry(userInput, options = {}) {
1316
1498
  schema: 'atris.one_lap.v1',
1317
1499
  ok: false,
1318
1500
  status: 'stuck',
1319
- reason: mapStatus !== 'ready' ? 'the workspace map is not ready' : 'first-contact context is required',
1320
- next_action: mapStatus !== 'ready' ? 'atris init --yes' : 'atris "<first direction>"',
1501
+ reason: 'first-contact context is required',
1502
+ next_action: 'atris "<first direction>"',
1321
1503
  }, null, 2));
1322
1504
  return 2;
1323
1505
  }
1324
- // Mission needs a tick when: it has a verifier configured AND that verifier
1325
- // hasn't passed yet. Planning-state missions count too — first tick is what
1326
- // moves them to running.
1327
- const needsTickMission = activeMissions.find(
1328
- (m) => m.verifier && !m.verifier_passed
1329
- );
1330
-
1331
- // Build status line
1332
- const parts = [];
1333
- if (wipCount > 0) {
1334
- parts.push(`work in progress: ${wipCount}`);
1335
- }
1336
- if (liveMissionsCount > 0) {
1337
- parts.push(`missions: ${liveMissionsCount}`);
1338
- }
1339
- if (inboxCount > 0) {
1340
- parts.push(`inbox: ${inboxCount}`);
1341
- }
1342
- if (backlogCount > 0) {
1343
- parts.push(`backlog: ${backlogCount}`);
1344
- }
1345
- if (completedTasksCount > 0) {
1346
- parts.push(`done: ${completedTasksCount}`);
1347
- }
1348
- const statusLine = parts.length > 0 ? parts.join(' | ') : 'clean slate';
1506
+ const hasRequest = Boolean(String(userInput || '').trim());
1349
1507
 
1350
- console.log('');
1351
- const contextRow = (label, value) => ` ${label.padEnd(9)}${value}`;
1352
- console.log(contextRow('context', 'loaded'));
1353
- console.log(contextRow('status', statusLine));
1354
-
1355
- if (gatherContext) {
1356
- const hotAnswer = String(userInput || '').trim();
1357
- if (hotAnswer) {
1358
- const answer = hotAnswer;
1359
- if (isAtrisMetaQuestion(answer)) {
1360
- printAtrisOverview();
1361
- return;
1362
- }
1363
- const profile = saveContextProfile(workspaceDir, answer, { source: 'hot_start' });
1364
- const starter = createStarterTask(workspaceDir, answer);
1365
- console.log('');
1366
- console.log('Got it. I saved your first direction.');
1367
- console.log(`Focus: ${profile.first_answer}`);
1368
- if (starter && starter.display_id) {
1369
- console.log(`First task: ${starter.display_id} — ${starter.title}`);
1370
- } else if (starter && starter.title) {
1371
- console.log(`First task: ${starter.title}`);
1372
- }
1373
- if (mapStatus !== 'ready') {
1374
- printStarterTaskNext(starter);
1375
- return;
1376
- }
1377
- await planCmd(answer);
1378
- return;
1379
- }
1380
- if (shouldSkipContextGatherer()) {
1381
- console.log('');
1382
- if (process.argv.includes('--verbose')) {
1383
- console.log('context gatherer skipped (non-interactive).');
1384
- }
1385
- printFirstUseNext();
1386
- return;
1387
- } else {
1508
+ // Bare `atris` is the first-minute screen: one win, one next command.
1509
+ // Interactive first-contact still asks once when the folder has no work yet.
1510
+ if (!hasRequest) {
1511
+ if (gatherContext && !shouldSkipContextGatherer()) {
1388
1512
  const answer = await askContextGatherer(workspaceDir);
1389
1513
  if (isAtrisMetaQuestion(answer)) {
1390
1514
  printAtrisOverview();
@@ -1406,105 +1530,39 @@ async function interactiveEntry(userInput, options = {}) {
1406
1530
  } else if (starter && starter.title) {
1407
1531
  console.log(`First task: ${starter.title}`);
1408
1532
  }
1409
- if (mapStatus !== 'ready') {
1410
- printStarterTaskNext(starter);
1411
- return;
1412
- }
1413
1533
  await planCmd(answer);
1414
1534
  return;
1415
1535
  }
1536
+ printFirstMinuteScreen({
1537
+ root: workspaceDir,
1538
+ context,
1539
+ missions: activeMissions,
1540
+ });
1541
+ return 0;
1416
1542
  }
1417
1543
 
1418
- if (mapStatus !== 'ready') {
1419
- printMapBootstrap({ userInput });
1420
- return;
1421
- }
1422
-
1423
- // Hot start - user provided input directly
1424
- if (userInput) {
1425
- console.log(`\n> ${userInput}`);
1426
- await planCmd(userInput);
1427
- return;
1428
- }
1429
-
1430
- // Surface live missions so the operator sees durable goals alongside dev WIP.
1431
- if (liveMissionsCount > 0) {
1432
- console.log('\nLive missions:');
1433
- for (const m of activeMissions.slice(0, 5)) {
1434
- const tickGate = m.verifier && !m.verifier_passed ? ' [needs tick]' : '';
1435
- const obj = m.objective.length > 70 ? `${m.objective.slice(0, 67)}...` : m.objective;
1436
- console.log(`- [${m.owner}] ${obj} (${m.status})${tickGate}`);
1437
- }
1438
- }
1439
-
1440
- // Cold start auto-advance.
1441
- // ORDER MATTERS: missions outrank pipeline state because a mission's verifier
1442
- // is the contract that gates the Stop hook. Closing it unblocks everything else.
1443
- if (needsTickMission) {
1444
- console.log(`\nNext: atris mission tick (${needsTickMission.owner} mission has unverified verifier)`);
1445
- console.log(`Run: atris mission tick ${needsTickMission.id} --verify --complete-on-pass`);
1446
- return;
1447
- }
1448
-
1449
- if (completedTasksCount > 0) {
1450
- const preview = context.completedTasks.slice(0, 3).map((t) => (t.length > 70 ? `${t.slice(0, 67)}...` : t));
1451
- if (preview.length > 0) {
1452
- console.log('\nCompleted (history):');
1453
- preview.forEach((t) => console.log(`- ${t}`));
1454
- console.log('Completed tasks are history, not pending review.');
1455
- }
1456
- }
1457
-
1458
- if (wipCount > 0 || backlogCount > 0) {
1459
- const featurePreview = Array.isArray(context.inProgressFeatures) ? context.inProgressFeatures : [];
1460
- const inProgressPreview = context.inProgressTasks.slice(0, 2).map((t) => (t.length > 70 ? `${t.slice(0, 67)}...` : t));
1461
- const backlogPreview = context.backlogTasks.slice(0, 2).map((t) => (t.length > 70 ? `${t.slice(0, 67)}...` : t));
1462
- if (featurePreview.length > 0) {
1463
- console.log(`\nIn-progress features: ${featurePreview.join(', ')}`);
1464
- }
1465
- if (inProgressPreview.length > 0) {
1466
- console.log('\nIn Progress (preview):');
1467
- inProgressPreview.forEach((t) => console.log(`- ${t}`));
1468
- }
1469
- if (backlogPreview.length > 0) {
1470
- console.log('\nBacklog (preview):');
1471
- backlogPreview.forEach((t) => console.log(`- ${t}`));
1544
+ if (gatherContext) {
1545
+ const answer = String(userInput || '').trim();
1546
+ if (isAtrisMetaQuestion(answer)) {
1547
+ printAtrisOverview();
1548
+ return;
1472
1549
  }
1473
- console.log('\nNext: atris do (work ready to execute)');
1474
- await doCmd();
1475
- return;
1476
- }
1477
-
1478
- if (inboxCount > 0) {
1479
- const preview = context.inboxItems.slice(0, 3).map((t) => (t.length > 70 ? `${t.slice(0, 67)}...` : t));
1480
- if (preview.length > 0) {
1481
- console.log('\nInbox (preview):');
1482
- preview.forEach((t) => console.log(`- ${t}`));
1550
+ const profile = saveContextProfile(workspaceDir, answer, { source: 'hot_start' });
1551
+ const starter = createStarterTask(workspaceDir, answer);
1552
+ console.log('');
1553
+ console.log('Got it. I saved your first direction.');
1554
+ console.log(`Focus: ${profile.first_answer}`);
1555
+ if (starter && starter.display_id) {
1556
+ console.log(`First task: ${starter.display_id} ${starter.title}`);
1557
+ } else if (starter && starter.title) {
1558
+ console.log(`First task: ${starter.title}`);
1483
1559
  }
1484
- console.log('\nNext: atris plan (Inbox has ideas)');
1485
- await planCmd();
1486
- return;
1487
- }
1488
-
1489
- if (completedTasksCount > 0) {
1490
- console.log('Next: atris plan (new work)');
1491
- return;
1492
- }
1493
-
1494
- // No obvious next step - prompt for input
1495
- const rl = readline.createInterface({
1496
- input: process.stdin,
1497
- output: process.stdout
1498
- });
1499
-
1500
- const request = await new Promise(r => rl.question('\nWhat do you want to build?\n> ', r));
1501
- rl.close();
1502
-
1503
- if (!request.trim()) {
1560
+ await planCmd(answer);
1504
1561
  return;
1505
1562
  }
1506
1563
 
1507
- await planCmd(request);
1564
+ console.log(`\n> ${userInput}`);
1565
+ await planCmd(userInput);
1508
1566
  }
1509
1567
 
1510
1568
  async function askContextGatherer(workspaceDir) {
@@ -1518,30 +1576,6 @@ async function askContextGatherer(workspaceDir) {
1518
1576
  return answer;
1519
1577
  }
1520
1578
 
1521
- function printMapBootstrap({ userInput, prefix = 'Bootstrap required' } = {}) {
1522
- console.log('');
1523
- console.log('┌─────────────────────────────────────────────────────────────┐');
1524
- console.log(`│ ${String(prefix).toUpperCase().padEnd(60)}│`);
1525
- console.log('└─────────────────────────────────────────────────────────────┘');
1526
- console.log('');
1527
- console.log('Atris needs a real `atris/MAP.md` so future steps are grounded in the workspace.');
1528
- console.log('');
1529
- console.log('For an agent:');
1530
- console.log('─────────────────────────────────────────────────────────────');
1531
- console.log('Read `atris/atris.md`, then generate a complete `atris/MAP.md` for this repo.');
1532
- console.log('Rules: include file:line refs, keep it grep-friendly, do NOT change code.');
1533
- if (userInput) {
1534
- console.log('');
1535
- console.log('After MAP is generated, continue with:');
1536
- console.log(`- ${userInput}`);
1537
- } else {
1538
- console.log('');
1539
- console.log('Then rerun: atris');
1540
- }
1541
- console.log('─────────────────────────────────────────────────────────────');
1542
- console.log('');
1543
- }
1544
-
1545
1579
  // Boot status: plain rows, honest numbers, one next action.
1546
1580
  // The banner only renders in a real terminal; when a hook or agent captures
1547
1581
  // this output it stays compact so it costs almost nothing in context.
@@ -1787,40 +1821,10 @@ if (command === 'init') {
1787
1821
  console.error(`✗ Error: ${error.message || error}`);
1788
1822
  process.exit(1);
1789
1823
  });
1790
- } else if (command === '_start') {
1791
- const args = process.argv.slice(3);
1792
- if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1793
- console.log('');
1794
- console.log('Usage: atris start [options]');
1795
- console.log('');
1796
- console.log('Starts the durable mission loop for one useful self-improvement mission.');
1797
- console.log('');
1798
- console.log('Options:');
1799
- console.log(' --json Print the mission route without starting it.');
1800
- console.log(' --owner X Override mission owner.');
1801
- console.log(' --cadence X Override mission cadence.');
1802
- console.log('');
1803
- process.exit(0);
1804
- }
1805
-
1806
- if (args.includes('--json')) {
1807
- console.log(JSON.stringify({
1808
- ok: true,
1809
- action: 'start_mission_run',
1810
- route: `atris mission run "${START_MISSION_OBJECTIVE}"`,
1811
- reason: 'casual_launch',
1812
- objective: START_MISSION_OBJECTIVE,
1813
- expected_loop: 'mission_run',
1814
- }, null, 2));
1815
- process.exit(0);
1816
- }
1817
-
1818
- Promise.resolve(require('../commands/mission').missionCommand(['run', START_MISSION_OBJECTIVE, ...args]))
1819
- .then(() => process.exit(process.exitCode || 0))
1820
- .catch((error) => {
1821
- console.error(`✗ Start failed: ${error.message || error}`);
1822
- process.exit(1);
1823
- });
1824
+ } else if (command === 'install' || command === 'workspace') {
1825
+ const { installCommand } = require('../commands/install');
1826
+ const code = installCommand(process.argv.slice(3), { verb: command });
1827
+ process.exit(typeof code === 'number' ? code : 0);
1824
1828
  } else if (command === 'task') {
1825
1829
  // SQLite-backed task plane. ~/.atris/tasks.db, gitignored, per-workspace.
1826
1830
  Promise.resolve(require('../commands/task').run(process.argv.slice(3)))
@@ -1916,9 +1920,20 @@ if (command === 'init') {
1916
1920
  .then((code) => process.exit(code || 0))
1917
1921
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1918
1922
  } else if (command === 'land') {
1919
- Promise.resolve(require('../commands/land').landCommand(process.argv.slice(3)))
1923
+ // landCommand can throw synchronously on empty repos; wrap so callers get a
1924
+ // one-line error instead of an uncaught stack from Promise.resolve(fn()).
1925
+ Promise.resolve()
1926
+ .then(() => require('../commands/land').landCommand(process.argv.slice(3)))
1920
1927
  .then((code) => process.exit(code || 0))
1921
- .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1928
+ .catch((err) => {
1929
+ const msg = String(err && err.message || err || '');
1930
+ if (/no master\/main|no commits|not a git/i.test(msg)) {
1931
+ console.error(msg);
1932
+ } else {
1933
+ console.error(`land: ${msg}`);
1934
+ }
1935
+ process.exit(1);
1936
+ });
1922
1937
  } else if (command === 'caretaker') {
1923
1938
  Promise.resolve(require('../commands/caretaker').caretakerCommand(process.argv.slice(3)))
1924
1939
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
@@ -1956,7 +1971,7 @@ if (command === 'init') {
1956
1971
  } else if (command === 'truth') {
1957
1972
  // Truth: one table rolling up mission state, tasks, feature proof receipts, and loop heartbeats.
1958
1973
  Promise.resolve(require('../commands/truth').truthCommand(process.argv.slice(3)))
1959
- .then((code) => process.exit(code || 0))
1974
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1960
1975
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1961
1976
  } else if (command === 'codex-goal') {
1962
1977
  Promise.resolve(require('../commands/codex-goal').codexGoalCommand(process.argv.slice(3)))
@@ -1978,12 +1993,20 @@ if (command === 'init') {
1978
1993
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
1979
1994
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1980
1995
  } else if (command === 'rainmaker') {
1981
- const code = require('../commands/rainmaker').rainmakerCommand(process.argv.slice(3));
1982
- process.exit(typeof code === 'number' ? code : 0);
1996
+ {
1997
+ const gate = requireAccountBound(process.argv.slice(3));
1998
+ if (!gate.ok) process.exit(refuseAccountGlobal());
1999
+ const code = require('../commands/rainmaker').rainmakerCommand(gate.args);
2000
+ process.exit(typeof code === 'number' ? code : 0);
2001
+ }
1983
2002
  } else if (command === 'avail') {
1984
- Promise.resolve(require('../commands/avail').availCommand(process.argv.slice(3)))
1985
- .then((code) => process.exit(typeof code === 'number' ? code : 0))
1986
- .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2003
+ {
2004
+ const gate = requireAccountBound(process.argv.slice(3));
2005
+ if (!gate.ok) process.exit(refuseAccountGlobal());
2006
+ Promise.resolve(require('../commands/avail').availCommand(gate.args))
2007
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2008
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2009
+ }
1987
2010
  } else if (command === 'meet') {
1988
2011
  Promise.resolve(require('../commands/meet').meetCommand(process.argv.slice(3)))
1989
2012
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
@@ -1991,7 +2014,7 @@ if (command === 'init') {
1991
2014
  } else if (command === 'brain') {
1992
2015
  Promise.resolve()
1993
2016
  .then(() => require('../commands/brain').brainCommand(process.argv.slice(3)))
1994
- .then(() => process.exit(0))
2017
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1995
2018
  .catch((err) => {
1996
2019
  const message = err.message || String(err);
1997
2020
  if (process.argv.slice(3).includes('--json')) {
@@ -2002,9 +2025,17 @@ if (command === 'init') {
2002
2025
  process.exit(1);
2003
2026
  });
2004
2027
  } else if (command === 'agent') {
2005
- agentAtris().then(() => process.exit(0)).catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2028
+ {
2029
+ const gate = requireAccountBound(process.argv.slice(3));
2030
+ if (!gate.ok) process.exit(refuseAccountGlobal());
2031
+ // Re-slice argv so agentAtris sees args without --account.
2032
+ process.argv = [process.argv[0], process.argv[1], 'agent', ...gate.args];
2033
+ agentAtris().then(() => process.exit(0)).catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2034
+ }
2006
2035
  } else if (command === 'log') {
2007
- const subcommand = process.argv[3];
2036
+ const logArgs = process.argv.slice(3);
2037
+ const subcommand = logArgs[0];
2038
+ const positional = logArgs.filter((arg) => !String(arg).startsWith('-'));
2008
2039
  if (subcommand === 'sync') {
2009
2040
  require('../commands/log-sync').logSyncAtris()
2010
2041
  .then(() => process.exit(0))
@@ -2013,15 +2044,22 @@ if (command === 'init') {
2013
2044
  process.exit(1);
2014
2045
  });
2015
2046
  } else if (subcommand === '--help' || subcommand === '-h' || subcommand === 'help') {
2016
- console.log('Usage: atris log [business-slug | sync | help]');
2047
+ console.log('Usage: atris log ["note"] | --repl | sync | <business-slug> | help');
2017
2048
  console.log('');
2018
- console.log(' atris log Open today\'s journal REPL (write to ## Inbox)');
2019
- console.log(' atris log <slug> Show business log history');
2049
+ console.log(' atris log "an idea" Append a note to today\'s ## Inbox');
2050
+ console.log(' atris log --repl Open today\'s journal REPL');
2051
+ console.log(' atris log Open the REPL (needs a terminal)');
2020
2052
  console.log(' atris log sync Sync the local journal to cloud');
2053
+ console.log(' atris log <slug> Show business log history (single slug token)');
2054
+ console.log(' atris wish "..." Headless inbox write; add --json --no-mission for agents');
2021
2055
  process.exit(0);
2022
- } else if (subcommand && !subcommand.startsWith('-')) {
2023
- // Business log: atris log <business-slug>
2024
- require('../commands/context-sync').businessLog(subcommand)
2056
+ } else if (
2057
+ positional.length === 1
2058
+ && !logArgs.includes('--repl')
2059
+ && /^[a-z0-9][a-z0-9_-]{0,63}$/i.test(positional[0])
2060
+ ) {
2061
+ // Business log: atris log <business-slug> (single slug token only)
2062
+ require('../commands/context-sync').businessLog(positional[0])
2025
2063
  .then(() => process.exit(0))
2026
2064
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2027
2065
  } else {
@@ -2090,13 +2128,15 @@ if (command === 'init') {
2090
2128
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2091
2129
  } else if (process.argv.includes('--all')) {
2092
2130
  const dryRun = process.argv.includes('--dry-run');
2093
- const force = process.argv.includes('--force') || process.argv.includes('--yes') || process.argv.includes('-y');
2094
- Promise.resolve(syncAllCmd({ dryRun, force }))
2131
+ const yes = process.argv.includes('--yes') || process.argv.includes('-y');
2132
+ const force = process.argv.includes('--force') || yes;
2133
+ Promise.resolve(syncAllCmd({ dryRun, force, yes }))
2095
2134
  .then(() => process.exit(0))
2096
2135
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2097
2136
  } else {
2098
2137
  syncCmd({
2099
2138
  dryRun: args.includes('--dry-run'),
2139
+ yes: args.includes('--yes') || args.includes('-y'),
2100
2140
  force: args.includes('--force') || args.includes('--yes') || args.includes('-y'),
2101
2141
  });
2102
2142
  }
@@ -2140,6 +2180,14 @@ if (command === 'init') {
2140
2180
  showServeHelp();
2141
2181
  process.exit(0);
2142
2182
  }
2183
+ const { refuseHeadlessUnless } = require('../lib/noninteractive');
2184
+ if (refuseHeadlessUnless(serveArgs, {
2185
+ allowYes: false,
2186
+ allowOnce: true,
2187
+ usage: 'Usage: atris serve [--agent <id>] [--allow-bash] [--once <op_id>]\nHeadless serve needs --once <op_id>.',
2188
+ })) {
2189
+ process.exit(2);
2190
+ }
2143
2191
  const serveOptions = {};
2144
2192
  for (let i = 0; i < serveArgs.length; i++) {
2145
2193
  if (serveArgs[i] === '--agent' && serveArgs[i + 1]) {
@@ -2147,6 +2195,9 @@ if (command === 'init') {
2147
2195
  i++;
2148
2196
  } else if (serveArgs[i] === '--allow-bash') {
2149
2197
  serveOptions.allowBash = true;
2198
+ } else if (serveArgs[i] === '--once' && serveArgs[i + 1]) {
2199
+ serveOptions.once = serveArgs[i + 1];
2200
+ i++;
2150
2201
  }
2151
2202
  }
2152
2203
  require('../commands/serve').serveAtris(serveOptions)
@@ -2155,7 +2206,7 @@ if (command === 'init') {
2155
2206
  process.exit(1);
2156
2207
  });
2157
2208
  } else if (command === 'version') {
2158
- require('../commands/version').showVersion();
2209
+ require('../commands/version').showVersion(process.argv.slice(3));
2159
2210
  } else if (command === 'login') {
2160
2211
  const args = process.argv.slice(3);
2161
2212
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
@@ -2197,7 +2248,23 @@ if (command === 'init') {
2197
2248
  showAuthHelp('accounts');
2198
2249
  process.exit(0);
2199
2250
  }
2200
- require('../commands/auth').accountsCmd();
2251
+ require('../commands/auth').accountsCmd(args);
2252
+ } else if (command === 'balance') {
2253
+ Promise.resolve(require('../commands/balance').balanceCommand(process.argv.slice(3)))
2254
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2255
+ .catch((err) => { console.error(String(err.message || err).replace(/\s+/g, ' ')); process.exit(1); });
2256
+ } else if (command === 'usage') {
2257
+ Promise.resolve(require('../commands/usage').usageCommand(process.argv.slice(3)))
2258
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2259
+ .catch((err) => { console.error(String(err.message || err).replace(/\s+/g, ' ')); process.exit(1); });
2260
+ } else if (command === 'api-key') {
2261
+ Promise.resolve(require('../commands/api-key').apiKeyCommand(process.argv.slice(3)))
2262
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2263
+ .catch((err) => { console.error(String(err.message || err).replace(/\s+/g, ' ')); process.exit(1); });
2264
+ } else if (command === 'topup') {
2265
+ Promise.resolve(require('../commands/topup').topupCommand(process.argv.slice(3)))
2266
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2267
+ .catch((err) => { console.error(String(err.message || err).replace(/\s+/g, ' ')); process.exit(1); });
2201
2268
  } else if (command === '_resolve') {
2202
2269
  // Hidden: resolve a profile name query → print exact profile name
2203
2270
  require('../commands/auth').resolveProfile();
@@ -2220,6 +2287,10 @@ if (command === 'init') {
2220
2287
  require('../commands/youtube').youtubeCommand(process.argv.slice(3))
2221
2288
  .then(() => exitWhenFlushed(0))
2222
2289
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); exitWhenFlushed(1); });
2290
+ } else if (command === 'x-search') {
2291
+ require('../commands/x-search').xSearchCommand(process.argv.slice(3))
2292
+ .then((code) => exitWhenFlushed(Number.isInteger(code) ? code : 0))
2293
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); exitWhenFlushed(1); });
2223
2294
  } else if (command === 'run') {
2224
2295
  const args = process.argv.slice(3);
2225
2296
  if (args[0] === 'logs') {
@@ -2536,6 +2607,14 @@ if (command === 'init') {
2536
2607
  process.exit(0);
2537
2608
  }
2538
2609
  require('../commands/analytics').analyticsAtris();
2610
+ } else if (command === 'revisions') {
2611
+ const args = process.argv.slice(3);
2612
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
2613
+ showRevisionsHelp();
2614
+ process.exit(0);
2615
+ }
2616
+ const code = require('../commands/revisions').revisionsCommand(args);
2617
+ process.exit(code);
2539
2618
  } else if (command === 'clean') {
2540
2619
  const args = process.argv.slice(3);
2541
2620
  if (args.includes('--help') || args.includes('-h')) {
@@ -2551,6 +2630,10 @@ if (command === 'init') {
2551
2630
  console.error(err.message || err);
2552
2631
  process.exit(1);
2553
2632
  });
2633
+ } else if (command === 'doctor') {
2634
+ Promise.resolve(require('../commands/doctor').doctorCommand(process.argv.slice(3)))
2635
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2636
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2554
2637
  } else if (command === 'verify') {
2555
2638
  const args = process.argv.slice(3);
2556
2639
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
@@ -2584,14 +2667,19 @@ if (command === 'init') {
2584
2667
  const code = require('../commands/verify').verifyRubric(slug, section);
2585
2668
  process.exit(code);
2586
2669
  }
2587
- const taskId = process.argv[3] || null;
2588
- require('../commands/verify').verifyAtris(taskId);
2670
+ const taskId = process.argv[3] && !String(process.argv[3]).startsWith('--') ? process.argv[3] : null;
2671
+ const code = require('../commands/verify').verifyAtris(taskId);
2672
+ process.exit(typeof code === 'number' ? code : 0);
2589
2673
  } else if (command === 'release') {
2590
2674
  const args = process.argv.slice(3);
2591
2675
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
2592
2676
  showReleaseHelp();
2593
2677
  process.exit(0);
2594
2678
  }
2679
+ if (args[0] === 'preflight') {
2680
+ const code = require('../commands/release').releasePreflight();
2681
+ process.exit(typeof code === 'number' ? code : 1);
2682
+ }
2595
2683
  const dryRun = process.argv.includes('--dry-run');
2596
2684
  require('../commands/release').releaseAtris({ dryRun })
2597
2685
  .then(() => process.exit(0))
@@ -2647,12 +2735,23 @@ if (command === 'init') {
2647
2735
  const status = require('../commands/stripe').stripeCommand(process.argv.slice(3));
2648
2736
  process.exit(status);
2649
2737
  } else if (command === 'gmail') {
2650
- const { gmailCommand } = require('../commands/integrations');
2651
- const subcommand = process.argv[3];
2652
- const args = process.argv.slice(4);
2653
- gmailCommand(subcommand, ...args)
2654
- .then(() => process.exit(0))
2655
- .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
2738
+ {
2739
+ const raw = process.argv.slice(3);
2740
+ const { gmailCommand, extractGmailMailboxAccount } = require('../commands/integrations');
2741
+ const mailbox = extractGmailMailboxAccount(raw);
2742
+ const gateInput = mailbox.mailboxAccountId ? ['--account', ...mailbox.args] : mailbox.args;
2743
+ const gate = requireAccountBound(gateInput);
2744
+ if (!gate.ok) {
2745
+ process.exit(refuseAccountGlobal());
2746
+ return;
2747
+ }
2748
+ const subcommand = gate.args[0];
2749
+ const args = gate.args.slice(1);
2750
+ if (mailbox.mailboxAccountId) args.push('--account', mailbox.mailboxAccountId);
2751
+ gmailCommand(subcommand, ...args)
2752
+ .then(() => process.exit(process.exitCode || 0))
2753
+ .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
2754
+ }
2656
2755
  } else if (command === 'calendar') {
2657
2756
  const { calendarCommand } = require('../commands/integrations');
2658
2757
  const subcommand = process.argv[3];
@@ -2661,19 +2760,30 @@ if (command === 'init') {
2661
2760
  .then(() => process.exit(0))
2662
2761
  .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
2663
2762
  } else if (command === 'twitter') {
2664
- const { twitterCommand } = require('../commands/integrations');
2665
- const subcommand = process.argv[3];
2666
- const args = process.argv.slice(4);
2667
- twitterCommand(subcommand, ...args)
2668
- .then(() => process.exit(0))
2669
- .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
2763
+ {
2764
+ const raw = process.argv.slice(3);
2765
+ const helpOnly = raw.length === 0 || raw.includes('--help') || raw.includes('-h') || raw[0] === 'help';
2766
+ const gate = helpOnly ? { ok: true, args: raw } : requireAccountBound(raw);
2767
+ if (!gate.ok) process.exit(refuseAccountGlobal());
2768
+ const { twitterCommand } = require('../commands/integrations');
2769
+ const subcommand = gate.args[0];
2770
+ const args = gate.args.slice(1);
2771
+ twitterCommand(subcommand, ...args)
2772
+ .then(() => process.exit(0))
2773
+ .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
2774
+ }
2670
2775
  } else if (command === 'slack') {
2671
- const { slackCommand } = require('../commands/integrations');
2672
- const subcommand = process.argv[3];
2673
- const args = process.argv.slice(4);
2674
- slackCommand(subcommand, ...args)
2675
- .then(() => process.exit(0))
2676
- .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
2776
+ {
2777
+ const raw = process.argv.slice(3);
2778
+ const gate = requireAccountBound(raw);
2779
+ if (!gate.ok) process.exit(refuseAccountGlobal());
2780
+ const { slackCommand } = require('../commands/integrations');
2781
+ const subcommand = gate.args[0];
2782
+ const args = gate.args.slice(1);
2783
+ slackCommand(subcommand, ...args)
2784
+ .then(() => process.exit(0))
2785
+ .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
2786
+ }
2677
2787
  } else if (command === 'imessage') {
2678
2788
  const { imessageCommand } = require('../commands/integrations');
2679
2789
  const subcommand = process.argv[3];
@@ -2688,8 +2798,8 @@ if (command === 'init') {
2688
2798
  process.exit(0);
2689
2799
  }
2690
2800
  const { integrationsStatus } = require('../commands/integrations');
2691
- integrationsStatus()
2692
- .then(() => process.exit(0))
2801
+ integrationsStatus(args)
2802
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2693
2803
  .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
2694
2804
  } else if (command === 'apps') {
2695
2805
  // Keep APP.md app-pack operations independent from the heavier workspace boot
@@ -2714,11 +2824,13 @@ if (command === 'init') {
2714
2824
  } else if (command === 'teach') {
2715
2825
  const subcommand = process.argv[3];
2716
2826
  const args = process.argv.slice(4);
2717
- require('../commands/teach')(subcommand, ...args);
2827
+ const teachCode = require('../commands/teach')(subcommand, ...args);
2828
+ process.exit(typeof teachCode === 'number' ? teachCode : 0);
2718
2829
  } else if (command === 'skill') {
2719
2830
  const subcommand = process.argv[3];
2720
2831
  const args = process.argv.slice(4);
2721
- require('../commands/skill').skillCommand(subcommand, ...args);
2832
+ const code = require('../commands/skill').skillCommand(subcommand, ...args);
2833
+ if (Number.isInteger(code) && code !== 0) process.exitCode = code;
2722
2834
  } else if (command === 'member') {
2723
2835
  const subcommand = process.argv[3];
2724
2836
  const args = process.argv.slice(4);
@@ -2756,12 +2868,14 @@ if (command === 'init') {
2756
2868
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2757
2869
  } else if (command === 'x') {
2758
2870
  // Fast Agent SDK execution - like "atris x echo hello" or "atris x ls -la"
2759
- const userInput = process.argv.slice(3).join(' ').trim();
2760
- if (!userInput) {
2871
+ const xArgs = process.argv.slice(3);
2872
+ const userInput = xArgs.join(' ').trim();
2873
+ const helpOnly = xArgs.length === 1 && (xArgs[0] === '--help' || xArgs[0] === '-h' || xArgs[0] === 'help' || xArgs[0] === '-?');
2874
+ if (!userInput || helpOnly) {
2761
2875
  console.log('Usage: atris x <command>');
2762
2876
  console.log('Example: atris x echo "hello world"');
2763
2877
  console.log('Example: atris x ls -la');
2764
- process.exit(1);
2878
+ process.exit(helpOnly ? 2 : 1);
2765
2879
  }
2766
2880
  require('../commands/workflow').executeAgentSDKFast(userInput);
2767
2881
  } else if (command === 'computer') {
@@ -2786,11 +2900,21 @@ if (command === 'init') {
2786
2900
  .then(() => process.exit(0))
2787
2901
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2788
2902
  } else if (command === 'business') {
2789
- const subcommand = process.argv[3];
2790
- const args = process.argv.slice(4);
2791
- require('../commands/business').businessCommand(subcommand, ...args)
2792
- .then(() => process.exit(process.exitCode || 0))
2793
- .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2903
+ {
2904
+ const raw = process.argv.slice(3);
2905
+ const helpOnly = raw.length === 0
2906
+ || raw.includes('--help')
2907
+ || raw.includes('-h')
2908
+ || raw[0] === 'help'
2909
+ || raw[0] === 'quickstart';
2910
+ const gate = helpOnly ? { ok: true, args: raw } : requireAccountBound(raw);
2911
+ if (!gate.ok) process.exit(refuseAccountGlobal());
2912
+ const subcommand = gate.args[0];
2913
+ const args = gate.args.slice(1);
2914
+ require('../commands/business').businessCommand(subcommand, ...args)
2915
+ .then(() => process.exit(process.exitCode || 0))
2916
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2917
+ }
2794
2918
  } else if (command === 'soul') {
2795
2919
  const args = process.argv.slice(3);
2796
2920
  require('../commands/soul').soul(args)
@@ -2875,6 +2999,11 @@ if (command === 'init') {
2875
2999
  Promise.resolve(require('../commands/engine').engineCommand(engineArgs))
2876
3000
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
2877
3001
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3002
+ } else if (command === 'playbook') {
3003
+ Promise.resolve()
3004
+ .then(() => require('../commands/playbook').playbookCommand(process.argv.slice(3)))
3005
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
3006
+ .catch((err) => { console.error(`playbook could not continue: ${err.message || err}`); process.exit(1); });
2878
3007
  } else if (command === 'sign') {
2879
3008
  // Sign: prepare-commit-msg hook that credits Atris as co-author on commits in atris workspaces.
2880
3009
  try { process.exit(require('../commands/sign').signCommand(process.argv[3]) || 0); }
@@ -2943,9 +3072,17 @@ if (command === 'init') {
2943
3072
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2944
3073
  } else if (command === 'site') {
2945
3074
  // Site: beautiful static site from a folder of markdown, in the anti-slop design system.
2946
- Promise.resolve(require('../commands/site').run(process.argv.slice(3)))
2947
- .then((code) => process.exit(typeof code === 'number' ? code : 0))
2948
- .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3075
+ {
3076
+ const raw = process.argv.slice(3);
3077
+ const deployWrite = raw[0] === 'deploy'
3078
+ && !argsWantHelp(raw)
3079
+ && !raw.includes('--dry-run');
3080
+ const gate = deployWrite ? requireAccountBound(raw) : { ok: true, args: raw };
3081
+ if (!gate.ok) process.exit(refuseAccountGlobal());
3082
+ Promise.resolve(require('../commands/site').run(gate.args))
3083
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
3084
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3085
+ }
2949
3086
  } else if (command === 'theme') {
2950
3087
  // Theme: brand themes (.atris/theme.json) for the whole design system (deck/html/site).
2951
3088
  Promise.resolve(require('../commands/theme').run(process.argv.slice(3)))
@@ -3004,11 +3141,64 @@ if (command === 'init') {
3004
3141
  .then(() => process.exit(0))
3005
3142
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3006
3143
  } else if (command === 'feedback') {
3007
- require('../commands/feedback').feedbackCommand()
3144
+ {
3145
+ const gate = requireAccountBound(process.argv.slice(3));
3146
+ if (!gate.ok) process.exit(refuseAccountGlobal());
3147
+ process.argv = [process.argv[0], process.argv[1], 'feedback', ...gate.args];
3148
+ require('../commands/feedback').feedbackCommand()
3149
+ .then(() => process.exit(0))
3150
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3151
+ }
3152
+ } else if (command === 'errors') {
3153
+ {
3154
+ const gate = requireAccountBound(process.argv.slice(3));
3155
+ if (!gate.ok) process.exit(refuseAccountGlobal());
3156
+ // Keep --account so errors can default to --local inside bound workspaces.
3157
+ const errorArgs = gate.account ? ['--account', ...gate.args] : gate.args;
3158
+ process.argv = [process.argv[0], process.argv[1], 'errors', ...errorArgs];
3159
+ require('../commands/errors').errorsCommand()
3160
+ .then(() => process.exit(0))
3161
+ .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
3162
+ }
3163
+ } else if (command === 'social') {
3164
+ require('../commands/social').socialCommand()
3008
3165
  .then(() => process.exit(0))
3009
3166
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3010
- } else if (command === 'errors') {
3011
- require('../commands/errors').errorsCommand()
3167
+ } else if (command === 'people') {
3168
+ require('../commands/social').peopleCommand()
3169
+ .then(() => process.exit(0))
3170
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3171
+ } else if (command === 'follow') {
3172
+ require('../commands/social').followCommand()
3173
+ .then(() => process.exit(0))
3174
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3175
+ } else if (command === 'unfollow') {
3176
+ require('../commands/social').unfollowCommand()
3177
+ .then(() => process.exit(0))
3178
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3179
+ } else if (command === 'friends') {
3180
+ require('../commands/social').friendsCommand()
3181
+ .then(() => process.exit(0))
3182
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3183
+ } else if (command === 'msg') {
3184
+ require('../commands/social').msgCommand()
3185
+ .then(() => process.exit(0))
3186
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3187
+ } else if (command === 'inbox') {
3188
+ {
3189
+ const gate = requireAccountBound(process.argv.slice(3));
3190
+ if (!gate.ok) process.exit(refuseAccountGlobal());
3191
+ process.argv = [process.argv[0], process.argv[1], 'inbox', ...gate.args];
3192
+ require('../commands/social').inboxCommand()
3193
+ .then(() => process.exit(0))
3194
+ .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
3195
+ }
3196
+ } else if (command === 'join') {
3197
+ require('../commands/social').joinCommand()
3198
+ .then(() => process.exit(0))
3199
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3200
+ } else if (command === 'invite') {
3201
+ require('../commands/social').inviteCommand()
3012
3202
  .then(() => process.exit(0))
3013
3203
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
3014
3204
  } else {
@@ -3140,7 +3330,7 @@ function inspectAgentCliWiring() {
3140
3330
  },
3141
3331
  ];
3142
3332
 
3143
- const binaries = ['atris', 'ax', 'claude', 'codex', 'cursor-agent', 'devin', 'droid'].map((name) => ({
3333
+ const binaries = ['atris', 'ax', 'claude', 'codex', 'cursor-agent', 'devin', 'agy'].map((name) => ({
3144
3334
  name,
3145
3335
  path: commandOnPath(name),
3146
3336
  }));
@@ -3319,7 +3509,7 @@ async function chatAtris() {
3319
3509
  for (const arg of rawArgs) {
3320
3510
  if (arg === '--agent') {
3321
3511
  agentLane = true;
3322
- } else if (arg === '--print' || arg === '--headless') {
3512
+ } else if (arg === '--print' || arg === '--headless' || arg === '--rapid' || arg === '--auto-approve') {
3323
3513
  fastLaneFlags.push(arg);
3324
3514
  } else {
3325
3515
  messageArgs.push(arg);
@@ -3332,9 +3522,12 @@ async function chatAtris() {
3332
3522
  console.log('Usage: atris chat ["message"]');
3333
3523
  console.log('');
3334
3524
  console.log(' Chat with Atris 2 Fast in this workspace: tools attached, same turn as `ax --fast`.');
3525
+ console.log(' Pass --rapid to use Atris Rapid instead.');
3335
3526
  console.log(' Requires `atris login`.');
3336
3527
  console.log('');
3337
3528
  console.log(' atris chat Interactive chat (ax --fast --chat)');
3529
+ console.log(' atris chat --rapid Interactive Rapid chat (ax --rapid --chat)');
3530
+ console.log(' atris chat --rapid --auto-approve Rapid chat that auto-approves safe git push');
3338
3531
  console.log(' atris chat "what now?" One-shot message (ax --fast)');
3339
3532
  console.log(' atris chat --print "..." Headless JSON result (ax --fast --print)');
3340
3533
  console.log(' atris chat --agent [...] Legacy cloud-agent lane (needs `atris agent`)');
@@ -3361,9 +3554,16 @@ async function chatAtris() {
3361
3554
  if (!agentLane) {
3362
3555
  try {
3363
3556
  const axPath = path.join(__dirname, '..', 'ax');
3364
- const axArgs = message || fastLaneFlags.length ? ['--fast', ...fastLaneFlags, message].filter(Boolean) : ['--fast', '--chat'];
3557
+ const lane = fastLaneFlags.includes('--rapid') ? '--rapid' : '--fast';
3558
+ const extraFlags = fastLaneFlags.filter((flag) => flag !== '--rapid');
3559
+ const axArgs = message
3560
+ ? [lane, ...extraFlags, message].filter(Boolean)
3561
+ : [lane, ...extraFlags, '--chat'];
3365
3562
  const run = spawnSync(process.execPath, [axPath, ...axArgs], { stdio: 'inherit' });
3366
- process.exit(run.status || 0);
3563
+ // spawnSync reports spawn failure via run.error (status stays null), and
3564
+ // a signal-killed ax also leaves status null; neither is a success.
3565
+ if (!run.error) process.exit(run.status ?? 1);
3566
+ // ax unavailable: fall through to the agent lane.
3367
3567
  } catch {
3368
3568
  // ax unavailable: fall through to the agent lane.
3369
3569
  }
@@ -3632,7 +3832,9 @@ async function atrisFastChat() {
3632
3832
  const axModule = require('../ax');
3633
3833
  if (axModule.resolveRoute(message) === 'local') {
3634
3834
  const run = spawnSync(process.execPath, [path.join(__dirname, '..', 'ax'), '--fast', message], { stdio: 'inherit' });
3635
- process.exit(run.status || 0);
3835
+ // Same rule as the chat lane: spawn failure or a signal kill leaves
3836
+ // status null and must not read as success.
3837
+ if (!run.error) process.exit(run.status ?? 1);
3636
3838
  }
3637
3839
  } catch {
3638
3840
  // ax unavailable: fall through to the plain cloud one-shot.