atris 3.15.14 → 3.15.22

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 (93) hide show
  1. package/AGENTS.md +84 -8
  2. package/README.md +5 -1
  3. package/atris/AGENTS.md +46 -1
  4. package/atris/CLAUDE.md +36 -1
  5. package/atris/GEMINI.md +14 -1
  6. package/atris/atris.md +12 -1
  7. package/atris/atrisDev.md +3 -2
  8. package/atris/context/README.md +11 -0
  9. package/atris/features/company-brain-sync/validate.md +5 -5
  10. package/atris/learnings.jsonl +1 -0
  11. package/atris/policies/atris-design.md +2 -0
  12. package/atris/skills/aeo/SKILL.md +2 -2
  13. package/atris/skills/atris/SKILL.md +15 -62
  14. package/atris/skills/design/SKILL.md +2 -0
  15. package/atris/skills/imessage/SKILL.md +19 -2
  16. package/atris/skills/loop/SKILL.md +6 -5
  17. package/atris/skills/magic-inbox/SKILL.md +1 -1
  18. package/atris/team/_template/MEMBER.md +23 -1
  19. package/atris/team/brainstormer/START_HERE.md +6 -0
  20. package/atris/team/executor/MEMBER.md +13 -0
  21. package/atris/team/executor/START_HERE.md +6 -0
  22. package/atris/team/launcher/START_HERE.md +6 -0
  23. package/atris/team/mission-lead/MEMBER.md +39 -0
  24. package/atris/team/mission-lead/MISSION.md +33 -0
  25. package/atris/team/mission-lead/START_HERE.md +6 -0
  26. package/atris/team/navigator/MEMBER.md +11 -0
  27. package/atris/team/navigator/START_HERE.md +6 -0
  28. package/atris/team/opus-overnight/MEMBER.md +39 -0
  29. package/atris/team/opus-overnight/MISSION.md +61 -0
  30. package/atris/team/opus-overnight/START_HERE.md +6 -0
  31. package/atris/team/opus-overnight/STEERING.md +35 -0
  32. package/atris/team/researcher/START_HERE.md +6 -0
  33. package/atris/team/validator/MEMBER.md +26 -6
  34. package/atris/team/validator/START_HERE.md +6 -0
  35. package/atris/wiki/concepts/agent-activation-contract.md +79 -0
  36. package/atris/wiki/concepts/workspace-initialization-contract.md +73 -0
  37. package/atris/wiki/index.md +27 -0
  38. package/atris/wiki/sources/atris-labs-2026-05-10.txt +17 -0
  39. package/atris/wiki/sources/atris-labs-goals-2026-05-10.txt +15 -0
  40. package/atris/wiki/sources/atrisos-generative-ui-product-surface-2026-05-10.txt +10 -0
  41. package/atris/wiki/sources/jack-dorsey-2026-05-10.txt +12 -0
  42. package/atris.md +49 -13
  43. package/bin/atris.js +660 -22
  44. package/commands/activate.js +12 -3
  45. package/commands/aeo.js +1 -1
  46. package/commands/align.js +10 -10
  47. package/commands/analytics.js +9 -4
  48. package/commands/app.js +2 -0
  49. package/commands/apps.js +276 -0
  50. package/commands/auth.js +1 -1
  51. package/commands/autopilot.js +74 -5
  52. package/commands/brain.js +536 -61
  53. package/commands/brainstorm.js +12 -12
  54. package/commands/business-sync.js +142 -24
  55. package/commands/clean.js +9 -6
  56. package/commands/codex-goal.js +311 -0
  57. package/commands/errors.js +11 -1
  58. package/commands/feedback.js +55 -17
  59. package/commands/fork.js +2 -2
  60. package/commands/gm.js +376 -0
  61. package/commands/init.js +80 -3
  62. package/commands/integrations.js +524 -0
  63. package/commands/learn.js +25 -16
  64. package/commands/lesson.js +41 -0
  65. package/commands/lifecycle.js +2 -2
  66. package/commands/member.js +2416 -9
  67. package/commands/mission.js +1776 -0
  68. package/commands/now.js +48 -7
  69. package/commands/play.js +425 -0
  70. package/commands/publish.js +2 -1
  71. package/commands/pull.js +72 -29
  72. package/commands/push.js +163 -18
  73. package/commands/review.js +51 -13
  74. package/commands/skill.js +2 -2
  75. package/commands/soul.js +19 -13
  76. package/commands/status.js +6 -1
  77. package/commands/sync.js +5 -4
  78. package/commands/task.js +1041 -147
  79. package/commands/terminal.js +5 -5
  80. package/commands/verify.js +7 -5
  81. package/commands/visualize.js +7 -0
  82. package/commands/wiki.js +53 -16
  83. package/commands/workflow.js +298 -54
  84. package/commands/workspace-clean.js +1 -1
  85. package/commands/worktree.js +468 -0
  86. package/commands/xp.js +1608 -0
  87. package/lib/manifest.js +34 -4
  88. package/lib/scorecard.js +3 -2
  89. package/lib/task-db.js +408 -27
  90. package/lib/todo-fallback.js +28 -2
  91. package/lib/todo.js +5 -3
  92. package/package.json +23 -2
  93. package/utils/update-check.js +51 -1
package/bin/atris.js CHANGED
@@ -26,7 +26,13 @@ try {
26
26
  }
27
27
 
28
28
  // Update check utility
29
- const { checkForUpdates, showUpdateNotification, autoUpdate } = require('../utils/update-check');
29
+ const {
30
+ checkForUpdates,
31
+ showUpdateNotification,
32
+ autoUpdate,
33
+ inspectInstallGitState,
34
+ formatInstallGitWarning,
35
+ } = require('../utils/update-check');
30
36
 
31
37
  // State detection for smart default
32
38
  const { detectWorkspaceState, loadContext } = require('../lib/state-detection');
@@ -62,10 +68,18 @@ const fetchMyAgents = (token) => _fetchMyAgents(token, apiRequestJson);
62
68
  const displayAccountSummary = () => _displayAccountSummary(apiRequestJson);
63
69
 
64
70
  // Run update check in background (non-blocking)
65
- // Skip for 'version' and 'update' commands to avoid redundant messages
71
+ // Skip for 'version', 'update', and help commands to avoid redundant messages or help side effects.
66
72
  let updateCheckPromise = null;
67
- const skipUpdateCheck = Boolean(process.env.ATRIS_SKIP_UPDATE_CHECK || process.env.NO_UPDATE_NOTIFIER);
68
- if (!skipUpdateCheck && (!process.argv[2] || (process.argv[2] && !['version', 'update', 'help'].includes(process.argv[2])))) {
73
+ const updateCommand = process.argv[2];
74
+ const updateArgs = process.argv.slice(3);
75
+ const helpRequested = updateCommand === 'help'
76
+ || updateCommand === '--help'
77
+ || updateCommand === '-h'
78
+ || updateArgs.includes('--help')
79
+ || updateArgs.includes('-h')
80
+ || updateArgs[0] === 'help';
81
+ const skipUpdateCheck = Boolean(process.env.ATRIS_SKIP_UPDATE_CHECK || process.env.NO_UPDATE_NOTIFIER || helpRequested);
82
+ if (!skipUpdateCheck && (!updateCommand || (updateCommand && !['version', 'update'].includes(updateCommand)))) {
69
83
  updateCheckPromise = checkForUpdates()
70
84
  .then((updateInfo) => {
71
85
  // Show notification if update available (after command completes)
@@ -103,6 +117,15 @@ const isBusinessSyncSafetyCommand = command === 'sync'
103
117
  || firstCommandArg === 'resolve'
104
118
  );
105
119
 
120
+ // Keep APP.md app-pack operations independent from the heavier workspace boot
121
+ // path so `atris apps --json` stays machine-readable for agents.
122
+ if (command === 'apps') {
123
+ const subcommand = process.argv[3];
124
+ const args = process.argv.slice(4);
125
+ require('../commands/apps').appsCommand(subcommand, ...args);
126
+ process.exit(0);
127
+ }
128
+
106
129
  // Auto-sync skills only for commands that modify workspace state
107
130
  if (['init', 'update', 'upgrade'].includes(command) || (command === 'sync' && !isBusinessSyncSafetyCommand)) {
108
131
  try {
@@ -116,10 +139,68 @@ if (['init', 'update', 'upgrade'].includes(command) || (command === 'sync' && !i
116
139
  }
117
140
  }
118
141
 
142
+ /**
143
+ * Load active missions from .atris/state/missions.jsonl (append-only event log).
144
+ * Walks lines in reverse, deduping by mission id, returns missions whose latest
145
+ * record has status in {ready, running, planning} sorted newest-first.
146
+ *
147
+ * Each mission: { id, owner, objective, status, verifier, verifier_passed, next_action, lane }.
148
+ *
149
+ * Returns [] if file missing or malformed — never throws.
150
+ */
151
+ function loadActiveMissions(workspaceDir) {
152
+ try {
153
+ const file = path.join(workspaceDir, '.atris', 'state', 'missions.jsonl');
154
+ if (!fs.existsSync(file)) return [];
155
+ const lines = fs.readFileSync(file, 'utf8').split('\n').filter(Boolean);
156
+ const seen = new Map(); // id -> mission record
157
+ for (let i = lines.length - 1; i >= 0; i--) {
158
+ let rec;
159
+ try { rec = JSON.parse(lines[i]); } catch { continue; }
160
+ const id = rec.id || rec.mission_id;
161
+ if (!id || seen.has(id)) continue;
162
+ seen.set(id, rec);
163
+ }
164
+ const live = [];
165
+ for (const m of seen.values()) {
166
+ const status = m.status;
167
+ if (!['ready', 'running', 'planning'].includes(status)) continue;
168
+ live.push({
169
+ id: m.id || m.mission_id,
170
+ owner: m.owner || '?',
171
+ objective: m.objective || '',
172
+ status,
173
+ verifier: m.verifier || null,
174
+ verifier_passed: (m.verifier_result && m.verifier_result.passed) === true,
175
+ next_action: m.next_action || '',
176
+ lane: m.lane || null,
177
+ });
178
+ }
179
+ // Most recently started first (rough — relies on insertion order from reversed walk)
180
+ return live;
181
+ } catch {
182
+ return [];
183
+ }
184
+ }
185
+
186
+ function showSearchHelp() {
187
+ console.log('Usage: atris search <keyword>');
188
+ console.log('Example: atris search auth');
189
+ }
190
+
119
191
  function searchJournal(keyword) {
120
192
  if (!keyword) {
121
- console.log('Usage: atris search <keyword>');
122
- console.log('Example: atris search auth');
193
+ showSearchHelp();
194
+ process.exit(1);
195
+ }
196
+
197
+ if (keyword === '--help' || keyword === '-h') {
198
+ showSearchHelp();
199
+ process.exit(0);
200
+ }
201
+
202
+ if (process.argv.slice(4).includes('--help') || process.argv.slice(4).includes('-h')) {
203
+ showSearchHelp();
123
204
  process.exit(1);
124
205
  }
125
206
 
@@ -253,14 +334,17 @@ function showHelp() {
253
334
  console.log(' now - Show atris/now.md, the current operating truth');
254
335
  console.log(' activate - Load Atris context');
255
336
  console.log(' status - See local work and completions (`atris status <business>` for remote)');
337
+ console.log(' xp - Show Career XP and contribution graph');
256
338
  console.log(' analytics - Show recent productivity from journals');
257
339
  console.log(' search - Search journal history (atris search <keyword>)');
258
340
  console.log(' clean - Housekeeping (stale tasks, archive journals, broken refs)');
259
341
  console.log(' verify - Validate work is done (tests, MAP.md, changes)');
260
342
  console.log(' task - Local agent task plane (atomic claims, TODO import)');
343
+ console.log(' mission - Goal + loop + member owner + verifier + receipt');
261
344
  console.log(' release - Tag release, bump version, create GitHub release, draft /launch');
262
345
  console.log(' learn - Project learnings (patterns, pitfalls, preferences)');
263
346
  console.log(' brain - Compile MAP/TODO/wiki/state into a loadable agent brain');
347
+ console.log(' lesson - Append a one-line lesson to atris/lessons.md');
264
348
  console.log(' ingest - Local-first wiki ingest into atris/wiki/');
265
349
  console.log(' query - Local-first wiki query against atris/wiki/');
266
350
  console.log(' lint - Local-first wiki lint for atris/wiki/');
@@ -269,6 +353,7 @@ function showHelp() {
269
353
  console.log('Optional helpers:');
270
354
  console.log(' brainstorm - Explore ideas conversationally before planning');
271
355
  console.log(' autopilot - Guided loop that can clarify TODOs and run plan → do → review');
356
+ console.log(' worktree - Isolated Git worktrees plus guarded ship/merge for parallel agents');
272
357
  console.log(' visualize - Generate a Slack/deck-ready visual from a prompt');
273
358
  console.log('');
274
359
  console.log('Experiments:');
@@ -319,7 +404,7 @@ function showHelp() {
319
404
  console.log(' console - Start/attach always-on coding console (tmux daemon)');
320
405
  console.log(' soul - Show, snapshot, or fork workspace identity');
321
406
  console.log(' fleet - Inspect local fleet status');
322
- console.log(' agent - Select which Atris agent to use');
407
+ console.log(' agent - Select cloud agent, or run `agent doctor` for local CLI wiring');
323
408
  console.log(' chat - Chat with the selected Atris agent');
324
409
  console.log(' login - Sign in or add another account');
325
410
  console.log(' logout - Sign out of current account');
@@ -333,7 +418,7 @@ function showHelp() {
333
418
  console.log(' calendar - Calendar commands (today, week)');
334
419
  console.log(' twitter - Twitter commands (post)');
335
420
  console.log(' slack - Slack commands (channels)');
336
- console.log(' imessage - Local Mac iMessage commands (doctor, recent)');
421
+ console.log(' imessage - Local Mac iMessage commands (doctor, lookup, recent, send)');
337
422
  console.log(' integrations - Show integration status');
338
423
  console.log('');
339
424
  console.log('Skills:');
@@ -418,6 +503,216 @@ function showReviewHelp() {
418
503
  console.log('');
419
504
  }
420
505
 
506
+ function showStatusHelp() {
507
+ console.log('');
508
+ console.log('Usage: atris status [--quick] [--json] [--verbose]');
509
+ console.log('');
510
+ console.log('Description:');
511
+ console.log(' Show the local Atris workspace task, inbox, completion, lesson, and team status.');
512
+ console.log('');
513
+ console.log('Options:');
514
+ console.log(' --quick, -q Print one compact status line.');
515
+ console.log(' --json Print machine-readable workspace status.');
516
+ console.log(' --verbose, -v Print the legacy visual task board.');
517
+ console.log(' --help, -h Show this help.');
518
+ console.log('');
519
+ }
520
+
521
+ function showAnalyticsHelp() {
522
+ console.log('');
523
+ console.log('Usage: atris analytics');
524
+ console.log('');
525
+ console.log('Description:');
526
+ console.log(' Summarize local journal completions, inbox trend, and activity patterns.');
527
+ console.log('');
528
+ console.log('Options:');
529
+ console.log(' --help, -h Show this help.');
530
+ console.log('');
531
+ }
532
+
533
+ function showActivateHelp() {
534
+ console.log('');
535
+ console.log('Usage: atris activate');
536
+ console.log('');
537
+ console.log('Description:');
538
+ console.log(' Load workspace context, recent completions, TODO, MAP, journal, and wiki status.');
539
+ console.log('');
540
+ console.log('Options:');
541
+ console.log(' --help, -h Show this help.');
542
+ console.log('');
543
+ }
544
+
545
+ function showNextHelp(commandName = 'next') {
546
+ console.log('');
547
+ console.log(`Usage: atris ${commandName} [request]`);
548
+ console.log('');
549
+ console.log('Description:');
550
+ console.log(' Auto-advance to the next workflow step, or route a request through the Atris entry.');
551
+ console.log('');
552
+ console.log('Options:');
553
+ console.log(' --help, -h Show this help.');
554
+ console.log('');
555
+ }
556
+
557
+ function showVerifyHelp() {
558
+ console.log('');
559
+ console.log('Usage: atris verify [task]');
560
+ console.log('Usage: atris verify <feature-slug> --section <name>');
561
+ console.log('');
562
+ console.log('Description:');
563
+ console.log(' Validate workspace health, a specific task, or a feature rubric section.');
564
+ console.log('');
565
+ console.log('Options:');
566
+ console.log(' --section <name> Run a fenced bash check from atris/features/<slug>/validate.md.');
567
+ console.log(' --help, -h Show this help.');
568
+ console.log('');
569
+ }
570
+
571
+ function showUpdateHelp(commandName = 'update') {
572
+ console.log('');
573
+ console.log(`Usage: atris ${commandName} [--all] [--dry-run] [--force]`);
574
+ console.log('');
575
+ console.log('Description:');
576
+ console.log(' Sync Atris workspace files from the installed CLI templates.');
577
+ console.log('');
578
+ console.log('Options:');
579
+ console.log(' --all Update Atris files across projects under the current tree.');
580
+ console.log(' --dry-run Preview update work without writing files.');
581
+ console.log(' --force Overwrite existing template files where supported.');
582
+ console.log(' --help, -h Show this help.');
583
+ console.log('');
584
+ }
585
+
586
+ function showUpgradeHelp() {
587
+ console.log('');
588
+ console.log('Usage: atris upgrade');
589
+ console.log('');
590
+ console.log('Description:');
591
+ console.log(' Check npm for the latest Atris CLI and install it globally if newer.');
592
+ console.log('');
593
+ console.log('Options:');
594
+ console.log(' --help, -h Show this help.');
595
+ console.log('');
596
+ }
597
+
598
+ function showReleaseHelp() {
599
+ console.log('');
600
+ console.log('Usage: atris release [--dry-run]');
601
+ console.log('');
602
+ console.log('Description:');
603
+ console.log(' Draft or publish a release from local git history.');
604
+ console.log('');
605
+ console.log('Options:');
606
+ console.log(' --dry-run Print the planned release without committing, tagging, or pushing.');
607
+ console.log(' --help, -h Show this help.');
608
+ console.log('');
609
+ }
610
+
611
+ function showAuthHelp(commandName) {
612
+ const usage = {
613
+ login: 'Usage: atris login [--token <token>] [--force]',
614
+ logout: 'Usage: atris logout',
615
+ whoami: 'Usage: atris whoami',
616
+ switch: 'Usage: atris switch [account] [--global]',
617
+ use: 'Usage: atris use [account]',
618
+ accounts: 'Usage: atris accounts [add|remove <account>|remove --all]',
619
+ }[commandName] || 'Usage: atris login|logout|whoami';
620
+ console.log('');
621
+ console.log(usage);
622
+ console.log('');
623
+ console.log('Description:');
624
+ if (commandName === 'login') {
625
+ console.log(' Sign in with browser OAuth or a pasted API token.');
626
+ } else if (commandName === 'logout') {
627
+ console.log(' Sign out of the current Atris account.');
628
+ } else if (commandName === 'whoami') {
629
+ console.log(' Show the active Atris account.');
630
+ } else if (commandName === 'switch') {
631
+ console.log(' Switch the active account globally or for this terminal session.');
632
+ } else if (commandName === 'use') {
633
+ console.log(' Print an ATRIS_PROFILE export for per-terminal account use.');
634
+ } else if (commandName === 'accounts') {
635
+ console.log(' List, add, or remove saved Atris accounts.');
636
+ }
637
+ console.log('');
638
+ console.log('Options:');
639
+ if (commandName === 'login') {
640
+ console.log(' --token <token> Save an API token without prompting.');
641
+ console.log(' --force, -f Re-run login even if credentials already exist.');
642
+ } else if (commandName === 'switch') {
643
+ console.log(' --global, -g Switch the account for all terminals.');
644
+ }
645
+ console.log(' --help, -h Show this help.');
646
+ console.log('');
647
+ }
648
+
649
+ function showIntegrationsHelp() {
650
+ console.log('');
651
+ console.log('Usage: atris integrations');
652
+ console.log('');
653
+ console.log('Description:');
654
+ console.log(' Show connection status for Gmail, Calendar, Slack, Twitter, GitHub, and iMessage.');
655
+ console.log('');
656
+ console.log('Options:');
657
+ console.log(' --help, -h Show this help.');
658
+ console.log('');
659
+ }
660
+
661
+ function showSetupHelp() {
662
+ console.log('');
663
+ console.log('Usage: atris setup');
664
+ console.log('');
665
+ console.log('Description:');
666
+ console.log(' Guided first-time setup: checks Node.js, login, businesses, and pull.');
667
+ console.log('');
668
+ console.log('Options:');
669
+ console.log(' --help, -h Show this help.');
670
+ console.log('');
671
+ }
672
+
673
+ function showServeHelp() {
674
+ console.log('');
675
+ console.log('Usage: atris serve [--agent <agent_id>] [--allow-bash]');
676
+ console.log('');
677
+ console.log('Description:');
678
+ console.log(' Start the local AI Computer bridge for the current directory.');
679
+ console.log('');
680
+ console.log('Options:');
681
+ console.log(' --agent <agent_id> Bind the session to a specific cloud agent.');
682
+ console.log(' --allow-bash Allow remote bash operations in this directory.');
683
+ console.log(' --help, -h Show this help.');
684
+ console.log('');
685
+ }
686
+
687
+ function showLoopHelp() {
688
+ console.log('');
689
+ console.log('Usage: atris loop [--dry-run] [--json] [--limit=N]');
690
+ console.log('');
691
+ console.log('Description:');
692
+ console.log(' Inspect wiki upkeep state and optionally refresh wiki status/log files.');
693
+ console.log('');
694
+ console.log('Options:');
695
+ console.log(' --dry-run Preview wiki loop state without writing files.');
696
+ console.log(' --json Print the loop report as JSON.');
697
+ console.log(' --limit=N Limit suggested source count.');
698
+ console.log(' --help, -h Show this help.');
699
+ console.log('');
700
+ }
701
+
702
+ function showCleanHelp() {
703
+ console.log('');
704
+ console.log('Usage: atris clean [--dry-run]');
705
+ console.log('');
706
+ console.log('Description:');
707
+ console.log(' Check workspace housekeeping: stale tasks, MAP.md refs, old journals,');
708
+ console.log(' empty TODO sections, and stale wiki pages.');
709
+ console.log('');
710
+ console.log('Options:');
711
+ console.log(' --dry-run, -n Preview cleanup without changing files.');
712
+ console.log(' --help, -h Show this help.');
713
+ console.log('');
714
+ }
715
+
421
716
  function showAutopilotHelp() {
422
717
  console.log('');
423
718
  console.log('Usage: atris autopilot [description] [options]');
@@ -457,11 +752,22 @@ const { planAtris: planCmd, doAtris: doCmd, reviewAtris: reviewCmd } = require('
457
752
 
458
753
  // All other commands are lazy-loaded inline (require() only when invoked)
459
754
 
755
+ if (command === '2' && ['fast', 'pro'].includes(String(firstCommandArg || '').toLowerCase())) {
756
+ const userInput = process.argv.slice(2).join(' ');
757
+ planCmd(userInput)
758
+ .then(() => process.exit(0))
759
+ .catch((error) => {
760
+ console.error(`✗ Atris 2 failed: ${error.message || error}`);
761
+ process.exit(1);
762
+ });
763
+ return;
764
+ }
765
+
460
766
  // Check if this is a known command or natural language input
461
767
  const knownCommands = ['init', 'log', 'now', 'status', 'analytics', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', 'plan', 'do', 'review', 'release',
462
- 'activate', '_activate', 'agent', 'chat', 'console', 'login', 'logout', 'whoami', 'switch', 'use', 'accounts', '_resolve', '_profile-email', '_switch-session', 'shell-init', 'update', 'upgrade', 'version', 'help', 'next', 'atris',
463
- 'clean', 'verify', 'search', 'skill', 'member', 'app', 'learn', 'plugin', 'experiments', 'receipt', 'proof', 'openclaw', 'pull', 'push', 'live', 'align', 'terminal', 'computer', 'diff', 'business', 'sync',
464
- 'ingest', 'query', 'lint', 'loop', 'task', 'aeo',
768
+ 'activate', '_activate', 'agent', 'chat', 'console', 'serve', 'login', 'logout', 'whoami', 'switch', 'use', 'accounts', '_resolve', '_profile-email', '_switch-session', 'shell-init', 'update', 'upgrade', 'version', 'help', 'next', 'atris',
769
+ 'clean', 'verify', 'search', 'skill', 'member', 'codex-goal', 'app', 'apps', 'learn', 'lesson', 'plugin', 'experiments', 'receipt', 'proof', 'openclaw', 'pull', 'push', 'live', 'align', 'terminal', 'computer', 'diff', 'business', 'sync',
770
+ 'ingest', 'query', 'lint', 'loop', 'task', 'mission', 'worktree', 'aeo', 'xp', 'play', 'gm', 'x',
465
771
  'gmail', 'calendar', 'twitter', 'slack', 'imessage', 'integrations', 'setup', 'clean-workspace', 'cw',
466
772
  'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet'];
467
773
 
@@ -524,6 +830,17 @@ if (!command || !knownCommands.includes(command)) {
524
830
  if (!command || !knownCommands.includes(command)) {
525
831
  const userInput = process.argv.slice(2).join(' ');
526
832
 
833
+ if (process.argv.includes('--json')) {
834
+ console.log(JSON.stringify({
835
+ ok: false,
836
+ error: command ? `unknown command: ${command}` : 'unknown command',
837
+ command: command || null,
838
+ input: userInput,
839
+ usage: 'atris help',
840
+ }, null, 2));
841
+ process.exit(2);
842
+ }
843
+
527
844
  // Warn if this looks like a mistyped single-word command (no spaces)
528
845
  if (command && !userInput.includes(' ')) {
529
846
  console.log(`⚠ Unknown command: "${command}". Run "atris help" for available commands.`);
@@ -570,12 +887,27 @@ async function interactiveEntry(userInput) {
570
887
  ? context.inProgressFeaturesCount
571
888
  : (context.inProgressFeatures || []).length;
572
889
 
890
+ // Pull active missions (durable goals) — these outrank dev-pipeline state
891
+ // because a mission with an unverified verifier is a Keshav-attributable
892
+ // commitment that hasn't been closed yet.
893
+ const activeMissions = loadActiveMissions(workspaceDir);
894
+ const liveMissionsCount = activeMissions.length;
895
+ // Mission needs a tick when: it has a verifier configured AND that verifier
896
+ // hasn't passed yet. Planning-state missions count too — first tick is what
897
+ // moves them to running.
898
+ const needsTickMission = activeMissions.find(
899
+ (m) => m.verifier && !m.verifier_passed
900
+ );
901
+
573
902
  // Build status line
574
903
  const parts = [];
575
904
  const wipCount = inProgressTasksCount + inProgressFeaturesCount;
576
905
  if (wipCount > 0) {
577
906
  parts.push(`WIP: ${wipCount}`);
578
907
  }
908
+ if (liveMissionsCount > 0) {
909
+ parts.push(`Missions: ${liveMissionsCount}`);
910
+ }
579
911
  if (inboxCount > 0) {
580
912
  parts.push(`Inbox: ${inboxCount}`);
581
913
  }
@@ -627,16 +959,32 @@ async function interactiveEntry(userInput) {
627
959
  return;
628
960
  }
629
961
 
630
- // Cold start - auto-advance based on current workspace state
962
+ // Surface live missions so the operator sees durable goals alongside dev WIP.
963
+ if (liveMissionsCount > 0) {
964
+ console.log('\nLive missions:');
965
+ for (const m of activeMissions.slice(0, 5)) {
966
+ const tickGate = m.verifier && !m.verifier_passed ? ' [needs tick]' : '';
967
+ const obj = m.objective.length > 70 ? `${m.objective.slice(0, 67)}...` : m.objective;
968
+ console.log(`- [${m.owner}] ${obj} (${m.status})${tickGate}`);
969
+ }
970
+ }
971
+
972
+ // Cold start auto-advance.
973
+ // ORDER MATTERS: missions outrank pipeline state because a mission's verifier
974
+ // is the contract that gates the Stop hook. Closing it unblocks everything else.
975
+ if (needsTickMission) {
976
+ console.log(`\nNext: atris mission tick (${needsTickMission.owner} mission has unverified verifier)`);
977
+ console.log(`Run: atris mission tick ${needsTickMission.id} --verify --complete-on-pass`);
978
+ return;
979
+ }
980
+
631
981
  if (completedTasksCount > 0) {
632
982
  const preview = context.completedTasks.slice(0, 3).map((t) => (t.length > 70 ? `${t.slice(0, 67)}...` : t));
633
983
  if (preview.length > 0) {
634
- console.log('\nCompleted (preview):');
984
+ console.log('\nCompleted (history):');
635
985
  preview.forEach((t) => console.log(`- ${t}`));
986
+ console.log('Completed tasks are history, not pending review.');
636
987
  }
637
- console.log('\nNext: atris review (pending validation)');
638
- await reviewCmd();
639
- return;
640
988
  }
641
989
 
642
990
  if (wipCount > 0 || backlogCount > 0) {
@@ -670,6 +1018,11 @@ async function interactiveEntry(userInput) {
670
1018
  return;
671
1019
  }
672
1020
 
1021
+ if (completedTasksCount > 0) {
1022
+ console.log('Next: atris plan (new work)');
1023
+ return;
1024
+ }
1025
+
673
1026
  // No obvious next step - prompt for input
674
1027
  const rl = readline.createInterface({
675
1028
  input: process.stdin,
@@ -799,6 +1152,13 @@ function showWelcomeVisualization() {
799
1152
  }
800
1153
 
801
1154
  if (command === 'init') {
1155
+ // Help flag must short-circuit before initCmd() (which scaffolds files)
1156
+ // and before interactiveEntry (which loads workspace context).
1157
+ const initArg = process.argv[3];
1158
+ if (initArg === '-h' || initArg === '--help' || initArg === 'help') {
1159
+ initCmd();
1160
+ process.exit(0);
1161
+ }
802
1162
  initCmd();
803
1163
  // Flow directly into interactive prompt
804
1164
  interactiveEntry()
@@ -812,15 +1172,36 @@ if (command === 'init') {
812
1172
  Promise.resolve(require('../commands/task').run(process.argv.slice(3)))
813
1173
  .then(() => process.exit(0))
814
1174
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1175
+ } else if (command === 'mission') {
1176
+ Promise.resolve(require('../commands/mission').missionCommand(process.argv.slice(3)))
1177
+ .then(() => process.exit(0))
1178
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1179
+ } else if (command === 'worktree') {
1180
+ Promise.resolve(require('../commands/worktree').worktreeCommand(process.argv.slice(3)))
1181
+ .then((code) => process.exit(code || 0))
1182
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1183
+ } else if (command === 'codex-goal') {
1184
+ Promise.resolve(require('../commands/codex-goal').codexGoalCommand(process.argv.slice(3)))
1185
+ .then(() => process.exit(process.exitCode || 0))
1186
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
815
1187
  } else if (command === 'aeo') {
816
1188
  // AEO: AI Engine Optimization — credit-metered citation drafting against the customer workspace.
817
1189
  Promise.resolve(require('../commands/aeo').run(process.argv.slice(3)))
818
1190
  .then(() => process.exit(0))
819
1191
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
820
1192
  } else if (command === 'brain') {
821
- Promise.resolve(require('../commands/brain').brainCommand(process.argv.slice(3)))
1193
+ Promise.resolve()
1194
+ .then(() => require('../commands/brain').brainCommand(process.argv.slice(3)))
822
1195
  .then(() => process.exit(0))
823
- .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1196
+ .catch((err) => {
1197
+ const message = err.message || String(err);
1198
+ if (process.argv.slice(3).includes('--json')) {
1199
+ console.log(JSON.stringify({ ok: false, error: message }, null, 2));
1200
+ } else {
1201
+ console.error(`\n✗ Error: ${message}`);
1202
+ }
1203
+ process.exit(1);
1204
+ });
824
1205
  } else if (command === 'agent') {
825
1206
  agentAtris().then(() => process.exit(0)).catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
826
1207
  } else if (command === 'log') {
@@ -832,7 +1213,14 @@ if (command === 'init') {
832
1213
  console.error(`✗ Log sync failed: ${error.message || error}`);
833
1214
  process.exit(1);
834
1215
  });
835
- } else if (subcommand && subcommand !== '--help' && !subcommand.startsWith('-')) {
1216
+ } else if (subcommand === '--help' || subcommand === '-h' || subcommand === 'help') {
1217
+ console.log('Usage: atris log [business-slug | sync | help]');
1218
+ console.log('');
1219
+ console.log(' atris log Open today\'s journal REPL (write to ## Inbox)');
1220
+ console.log(' atris log <slug> Show business log history');
1221
+ console.log(' atris log sync Sync the local journal to cloud');
1222
+ process.exit(0);
1223
+ } else if (subcommand && !subcommand.startsWith('-')) {
836
1224
  // Business log: atris log <business-slug>
837
1225
  require('../commands/context-sync').businessLog(subcommand)
838
1226
  .then(() => process.exit(0))
@@ -841,10 +1229,21 @@ if (command === 'init') {
841
1229
  logCmd();
842
1230
  }
843
1231
  } else if (command === 'now') {
844
- require('../commands/now').nowAtris(process.argv.slice(3));
1232
+ const args = process.argv.slice(3);
1233
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1234
+ require('../commands/now').nowAtris(args);
1235
+ process.exit(0);
1236
+ }
1237
+ require('../commands/now').nowAtris(args);
845
1238
  } else if (command === 'activate') {
1239
+ const args = process.argv.slice(3);
1240
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1241
+ showActivateHelp();
1242
+ process.exit(0);
1243
+ }
846
1244
  activateCmd();
847
1245
  } else if (command === 'update' || command === 'sync') {
1246
+ const args = process.argv.slice(3);
848
1247
  const firstSyncArg = process.argv[3];
849
1248
  const isBusinessSync = command === 'sync'
850
1249
  && (
@@ -853,6 +1252,10 @@ if (command === 'init') {
853
1252
  || (firstSyncArg && !firstSyncArg.startsWith('-'))
854
1253
  )
855
1254
  && firstSyncArg !== 'all';
1255
+ if ((args.includes('--help') || args.includes('-h') || args[0] === 'help') && !isBusinessSync) {
1256
+ showUpdateHelp(command);
1257
+ process.exit(0);
1258
+ }
856
1259
  if (isBusinessSync) {
857
1260
  Promise.resolve(require('../commands/business-sync').businessSync(process.argv.slice(3)))
858
1261
  .then(() => process.exit(0))
@@ -867,6 +1270,11 @@ if (command === 'init') {
867
1270
  syncCmd();
868
1271
  }
869
1272
  } else if (command === 'upgrade') {
1273
+ const args = process.argv.slice(3);
1274
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1275
+ showUpgradeHelp();
1276
+ process.exit(0);
1277
+ }
870
1278
  upgradeAtris().then(() => process.exit(0)).catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
871
1279
  } else if (command === 'chat') {
872
1280
  chatAtris()
@@ -881,6 +1289,10 @@ if (command === 'init') {
881
1289
  // Start the local AI Computer bridge — make this directory addressable
882
1290
  // by cloud agents via the Atris API
883
1291
  const serveArgs = process.argv.slice(3);
1292
+ if (serveArgs.includes('--help') || serveArgs.includes('-h') || serveArgs[0] === 'help') {
1293
+ showServeHelp();
1294
+ process.exit(0);
1295
+ }
884
1296
  const serveOptions = {};
885
1297
  for (let i = 0; i < serveArgs.length; i++) {
886
1298
  if (serveArgs[i] === '--agent' && serveArgs[i + 1]) {
@@ -898,16 +1310,46 @@ if (command === 'init') {
898
1310
  } else if (command === 'version') {
899
1311
  require('../commands/version').showVersion();
900
1312
  } else if (command === 'login') {
1313
+ const args = process.argv.slice(3);
1314
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1315
+ showAuthHelp('login');
1316
+ process.exit(0);
1317
+ }
901
1318
  require('../commands/auth').loginAtris();
902
1319
  } else if (command === 'logout') {
1320
+ const args = process.argv.slice(3);
1321
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1322
+ showAuthHelp('logout');
1323
+ process.exit(0);
1324
+ }
903
1325
  require('../commands/auth').logoutAtris();
904
1326
  } else if (command === 'whoami') {
1327
+ const args = process.argv.slice(3);
1328
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1329
+ showAuthHelp('whoami');
1330
+ process.exit(0);
1331
+ }
905
1332
  require('../commands/auth').whoamiAtris();
906
1333
  } else if (command === 'switch') {
1334
+ const args = process.argv.slice(3);
1335
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1336
+ showAuthHelp('switch');
1337
+ process.exit(0);
1338
+ }
907
1339
  require('../commands/auth').switchAccount();
908
1340
  } else if (command === 'use') {
1341
+ const args = process.argv.slice(3);
1342
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1343
+ showAuthHelp('use');
1344
+ process.exit(0);
1345
+ }
909
1346
  require('../commands/auth').useAccount();
910
1347
  } else if (command === 'accounts') {
1348
+ const args = process.argv.slice(3);
1349
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1350
+ showAuthHelp('accounts');
1351
+ process.exit(0);
1352
+ }
911
1353
  require('../commands/auth').accountsCmd();
912
1354
  } else if (command === '_resolve') {
913
1355
  // Hidden: resolve a profile name query → print exact profile name
@@ -929,7 +1371,7 @@ if (command === 'init') {
929
1371
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
930
1372
  } else if (command === 'run') {
931
1373
  const args = process.argv.slice(3);
932
- if (args.includes('--help') || args.includes('-h')) {
1374
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
933
1375
  console.log('');
934
1376
  console.log('Usage: atris run [options]');
935
1377
  console.log('');
@@ -965,7 +1407,7 @@ if (command === 'init') {
965
1407
  });
966
1408
  } else if (command === 'autopilot') {
967
1409
  const args = process.argv.slice(3);
968
- if (args.includes('--help') || args.includes('-h')) {
1410
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
969
1411
  showAutopilotHelp();
970
1412
  process.exit(0);
971
1413
  }
@@ -1008,6 +1450,10 @@ if (command === 'init') {
1008
1450
  });
1009
1451
  } else if (command === 'next' || command === 'atris') {
1010
1452
  const rawArgs = process.argv.slice(3);
1453
+ if (rawArgs.includes('--help') || rawArgs.includes('-h') || rawArgs[0] === 'help') {
1454
+ showNextHelp(command);
1455
+ process.exit(0);
1456
+ }
1011
1457
  const userInput = rawArgs.filter((arg) => !arg.startsWith('-')).join(' ').trim();
1012
1458
  interactiveEntry(userInput || null)
1013
1459
  .then(() => process.exit(0))
@@ -1052,6 +1498,11 @@ if (command === 'init') {
1052
1498
  process.exit(1);
1053
1499
  });
1054
1500
  } else if (command === 'status') {
1501
+ const args = process.argv.slice(3);
1502
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1503
+ showStatusHelp();
1504
+ process.exit(0);
1505
+ }
1055
1506
  let subcommand = process.argv[3];
1056
1507
  if (subcommand && !subcommand.startsWith('-')) {
1057
1508
  require('../commands/context-sync').businessStatus(subcommand)
@@ -1064,11 +1515,26 @@ if (command === 'init') {
1064
1515
  statusCmd(isQuick, isJson, verbose);
1065
1516
  }
1066
1517
  } else if (command === 'analytics') {
1518
+ const args = process.argv.slice(3);
1519
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1520
+ showAnalyticsHelp();
1521
+ process.exit(0);
1522
+ }
1067
1523
  require('../commands/analytics').analyticsAtris();
1068
1524
  } else if (command === 'clean') {
1525
+ const args = process.argv.slice(3);
1526
+ if (args.includes('--help') || args.includes('-h')) {
1527
+ showCleanHelp();
1528
+ process.exit(0);
1529
+ }
1069
1530
  const dryRun = process.argv.includes('--dry-run') || process.argv.includes('-n');
1070
1531
  require('../commands/clean').cleanAtris({ dryRun });
1071
1532
  } else if (command === 'verify') {
1533
+ const args = process.argv.slice(3);
1534
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1535
+ showVerifyHelp();
1536
+ process.exit(0);
1537
+ }
1072
1538
  const sectionIdx = process.argv.indexOf('--section');
1073
1539
  if (sectionIdx > 0 && process.argv[sectionIdx + 1]) {
1074
1540
  const slug = process.argv[3] && !process.argv[3].startsWith('--') ? process.argv[3] : null;
@@ -1079,6 +1545,11 @@ if (command === 'init') {
1079
1545
  const taskId = process.argv[3] || null;
1080
1546
  require('../commands/verify').verifyAtris(taskId);
1081
1547
  } else if (command === 'release') {
1548
+ const args = process.argv.slice(3);
1549
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1550
+ showReleaseHelp();
1551
+ process.exit(0);
1552
+ }
1082
1553
  const dryRun = process.argv.includes('--dry-run');
1083
1554
  require('../commands/release').releaseAtris({ dryRun })
1084
1555
  .then(() => process.exit(0))
@@ -1086,6 +1557,18 @@ if (command === 'init') {
1086
1557
  } else if (command === 'search') {
1087
1558
  const keyword = process.argv.slice(3).join(' ');
1088
1559
  searchJournal(keyword);
1560
+ } else if (command === 'xp') {
1561
+ require('../commands/xp').xpCommand(...process.argv.slice(3))
1562
+ .then(() => process.exit(0))
1563
+ .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
1564
+ } else if (command === 'play') {
1565
+ require('../commands/play').playCommand(...process.argv.slice(3))
1566
+ .then(() => process.exit(0))
1567
+ .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
1568
+ } else if (command === 'gm') {
1569
+ require('../commands/gm').gmCommand(...process.argv.slice(3))
1570
+ .then(() => process.exit(0))
1571
+ .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
1089
1572
  } else if (command === 'gmail') {
1090
1573
  const { gmailCommand } = require('../commands/integrations');
1091
1574
  const subcommand = process.argv[3];
@@ -1122,6 +1605,11 @@ if (command === 'init') {
1122
1605
  .then(() => process.exit(0))
1123
1606
  .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
1124
1607
  } else if (command === 'integrations') {
1608
+ const args = process.argv.slice(3);
1609
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1610
+ showIntegrationsHelp();
1611
+ process.exit(0);
1612
+ }
1125
1613
  const { integrationsStatus } = require('../commands/integrations');
1126
1614
  integrationsStatus()
1127
1615
  .then(() => process.exit(0))
@@ -1130,6 +1618,10 @@ if (command === 'init') {
1130
1618
  const subcommand = process.argv[3];
1131
1619
  const args = process.argv.slice(4);
1132
1620
  require('../commands/learn')(subcommand, ...args);
1621
+ } else if (command === 'lesson') {
1622
+ const subcommand = process.argv[3];
1623
+ const args = process.argv.slice(4);
1624
+ require('../commands/lesson')(subcommand, ...args);
1133
1625
  } else if (command === 'skill') {
1134
1626
  const subcommand = process.argv[3];
1135
1627
  const args = process.argv.slice(4);
@@ -1162,13 +1654,28 @@ if (command === 'init') {
1162
1654
  require('../commands/terminal').terminalAtris()
1163
1655
  .then(() => process.exit(0))
1164
1656
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1657
+ } else if (command === 'x') {
1658
+ // Fast Agent SDK execution - like "atris x echo hello" or "atris x ls -la"
1659
+ const userInput = process.argv.slice(3).join(' ').trim();
1660
+ if (!userInput) {
1661
+ console.log('Usage: atris x <command>');
1662
+ console.log('Example: atris x echo "hello world"');
1663
+ console.log('Example: atris x ls -la');
1664
+ process.exit(1);
1665
+ }
1666
+ require('../commands/workflow').executeAgentSDKFast(userInput);
1165
1667
  } else if (command === 'computer') {
1166
1668
  require('../commands/computer').runComputer()
1167
1669
  .then(() => process.exit(0))
1168
1670
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1169
1671
  } else if (command === 'diff') {
1170
1672
  let diffSlug = process.argv[3];
1673
+ if (diffSlug === '-h' || diffSlug === '--help') {
1674
+ console.log('Usage: atris diff [business] [path]');
1675
+ process.exit(0);
1676
+ }
1171
1677
  if (!diffSlug || diffSlug.startsWith('-')) {
1678
+ diffSlug = undefined;
1172
1679
  const bizFile = require('path').join(process.cwd(), '.atris', 'business.json');
1173
1680
  if (require('fs').existsSync(bizFile)) {
1174
1681
  try { diffSlug = JSON.parse(require('fs').readFileSync(bizFile, 'utf8')).slug; } catch {}
@@ -1210,6 +1717,10 @@ if (command === 'init') {
1210
1717
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1211
1718
  } else if (command === 'loop') {
1212
1719
  const args = process.argv.slice(3);
1720
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1721
+ showLoopHelp();
1722
+ process.exit(0);
1723
+ }
1213
1724
  require('../commands/loop').loopAtris(args)
1214
1725
  .then(() => process.exit(0))
1215
1726
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
@@ -1231,6 +1742,11 @@ if (command === 'init') {
1231
1742
  const args = process.argv.slice(4);
1232
1743
  require('../commands/proof').proofCommand(subcommand, ...args);
1233
1744
  } else if (command === 'setup') {
1745
+ const args = process.argv.slice(3);
1746
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1747
+ showSetupHelp();
1748
+ process.exit(0);
1749
+ }
1234
1750
  require('../commands/setup').setupAtris()
1235
1751
  .then(() => process.exit(0))
1236
1752
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
@@ -1276,6 +1792,11 @@ async function upgradeAtris() {
1276
1792
  console.log('');
1277
1793
  console.log(`Current version: ${CLI_VERSION}`);
1278
1794
  console.log('');
1795
+ const installWarning = formatInstallGitWarning(inspectInstallGitState(path.join(__dirname, '..')));
1796
+ if (installWarning) {
1797
+ console.log(installWarning);
1798
+ console.log('');
1799
+ }
1279
1800
  console.log('Checking for updates...');
1280
1801
 
1281
1802
  // Force check npm for latest version
@@ -1331,7 +1852,112 @@ function showVersion() {
1331
1852
  // Agent Selection
1332
1853
  // ============================================
1333
1854
 
1855
+ function fileContains(relPath, pattern) {
1856
+ try {
1857
+ const fullPath = path.join(process.cwd(), relPath);
1858
+ if (!fs.existsSync(fullPath)) return false;
1859
+ const text = fs.readFileSync(fullPath, 'utf8');
1860
+ return pattern instanceof RegExp ? pattern.test(text) : text.includes(pattern);
1861
+ } catch {
1862
+ return false;
1863
+ }
1864
+ }
1865
+
1866
+ function commandOnPath(name) {
1867
+ const result = spawnSync('which', [name], { encoding: 'utf8', timeout: 1000 });
1868
+ return result.status === 0 ? result.stdout.trim() : null;
1869
+ }
1870
+
1871
+ function inspectAgentCliWiring() {
1872
+ const checks = [
1873
+ {
1874
+ id: 'atris-core',
1875
+ label: 'Atris core',
1876
+ ok: fs.existsSync(path.join(process.cwd(), 'atris', 'MAP.md'))
1877
+ && fs.existsSync(path.join(process.cwd(), 'atris', 'TODO.md')),
1878
+ fix: 'Run `atris init` from the workspace root.',
1879
+ },
1880
+ {
1881
+ id: 'codex',
1882
+ label: 'Codex / OpenAI agents',
1883
+ ok: fileContains('AGENTS.md', /atris\/MAP\.md|atris atris\.md|atris task/),
1884
+ fix: 'Add AGENTS.md with Atris boot, MAP, and task instructions.',
1885
+ },
1886
+ {
1887
+ id: 'claude',
1888
+ label: 'Claude Code',
1889
+ ok: fileContains('.claude/commands/atris.md', /atris|AGENTS\.md/)
1890
+ || fileContains('.claude/settings.json', /atris atris\.md|atris\/skills/)
1891
+ || fileContains('CLAUDE.md', /Atris|atris/),
1892
+ fix: 'Run `atris init` to create .claude commands/settings.',
1893
+ },
1894
+ {
1895
+ id: 'cursor',
1896
+ label: 'Cursor',
1897
+ ok: fileContains('.cursor/rules/atris.mdc', /atris\/MAP\.md|AGENTS\.md|atris task/)
1898
+ || fileContains('.cursorrules', /atris\/MAP\.md|AGENTS\.md|atris task/)
1899
+ || fileContains('.cursor/commands/atris.md', /atris\/MAP\.md|atris\.md/),
1900
+ fix: 'Run `atris init` to create Cursor rules, or add .cursor/commands/atris.md.',
1901
+ },
1902
+ {
1903
+ id: 'devin',
1904
+ label: 'Devin',
1905
+ ok: fileContains('.devin/config.local.json', /Exec\(atris\)/),
1906
+ fix: 'Run `atris init` or add .devin/config.local.json allowing Exec(atris).',
1907
+ },
1908
+ ];
1909
+
1910
+ const binaries = ['atris', 'claude', 'codex', 'cursor-agent', 'devin'].map((name) => ({
1911
+ name,
1912
+ path: commandOnPath(name),
1913
+ }));
1914
+ return { checks, binaries };
1915
+ }
1916
+
1917
+ function agentDoctor() {
1918
+ const args = process.argv.slice(4);
1919
+ const json = args.includes('--json');
1920
+ const { checks, binaries } = inspectAgentCliWiring();
1921
+ const ok = checks.every((check) => check.ok);
1922
+ const payload = { ok, action: 'agent_doctor', checks, binaries };
1923
+
1924
+ if (json) {
1925
+ console.log(JSON.stringify(payload, null, 2));
1926
+ process.exit(ok ? 0 : 1);
1927
+ }
1928
+
1929
+ console.log('Atris agent CLI doctor');
1930
+ console.log('');
1931
+ for (const check of checks) {
1932
+ console.log(`${check.ok ? '✓' : '✗'} ${check.label}`);
1933
+ if (!check.ok) console.log(` fix: ${check.fix}`);
1934
+ }
1935
+ console.log('');
1936
+ console.log('Local binaries');
1937
+ for (const binary of binaries) {
1938
+ console.log(`${binary.path ? '✓' : '·'} ${binary.name}${binary.path ? ` -> ${binary.path}` : ' not on PATH'}`);
1939
+ }
1940
+ process.exit(ok ? 0 : 1);
1941
+ }
1942
+
1334
1943
  async function agentAtris() {
1944
+ // Respect -h / --help / help before any auth/state work
1945
+ const firstArg = process.argv[3];
1946
+ if (firstArg === '-h' || firstArg === '--help' || firstArg === 'help') {
1947
+ console.log('Usage: atris agent [doctor]');
1948
+ console.log('');
1949
+ console.log(' Pick which cloud agent to chat with from this workspace.');
1950
+ console.log(' Run `atris agent doctor` to verify local AI CLIs can see Atris context.');
1951
+ console.log(' Requires `atris login` first.');
1952
+ console.log('');
1953
+ console.log(' After selecting, use: atris chat ["message"]');
1954
+ process.exit(0);
1955
+ }
1956
+
1957
+ if (firstArg === 'doctor') {
1958
+ agentDoctor();
1959
+ }
1960
+
1335
1961
  const targetDir = path.join(process.cwd(), 'atris');
1336
1962
 
1337
1963
  // Check if atris/ folder exists
@@ -1423,6 +2049,18 @@ async function chatAtris() {
1423
2049
  // Get message from command line args
1424
2050
  const message = process.argv.slice(3).join(' ').trim();
1425
2051
 
2052
+ // Respect -h / --help before any auth/state checks
2053
+ if (message === '-h' || message === '--help' || message === 'help') {
2054
+ console.log('Usage: atris chat ["message"]');
2055
+ console.log('');
2056
+ console.log(' Open an interactive session with the selected agent, or send a one-shot message.');
2057
+ console.log(' Requires `atris login` and `atris agent` to be run first.');
2058
+ console.log('');
2059
+ console.log(' atris chat Interactive REPL with selected agent');
2060
+ console.log(' atris chat "what now?" One-shot message');
2061
+ process.exit(0);
2062
+ }
2063
+
1426
2064
  // Check atris/ exists
1427
2065
  const targetDir = path.join(process.cwd(), 'atris');
1428
2066
  if (!fs.existsSync(targetDir)) {