atris 3.30.12 → 3.32.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 (64) hide show
  1. package/AGENTS.md +16 -3
  2. package/FOR_AGENTS.md +81 -0
  3. package/README.md +6 -4
  4. package/atris/CLAUDE.md +8 -0
  5. package/atris/atris.md +51 -19
  6. package/atris/skills/README.md +1 -0
  7. package/atris/skills/blocks/SKILL.md +134 -0
  8. package/atris/skills/clawhub/philosophy-of-work/SKILL.md +56 -0
  9. package/atris/skills/youtube/SKILL.md +31 -11
  10. package/atris.md +15 -0
  11. package/ax +189 -7
  12. package/bin/atris.js +273 -225
  13. package/commands/autoland.js +379 -0
  14. package/commands/autopilot-front.js +273 -0
  15. package/commands/autopilot.js +94 -4
  16. package/commands/business.js +1 -1
  17. package/commands/clean.js +72 -9
  18. package/commands/codex-goal.js +72 -22
  19. package/commands/computer.js +48 -3
  20. package/commands/gm.js +1 -1
  21. package/commands/harvest.js +179 -0
  22. package/commands/init.js +22 -1
  23. package/commands/land.js +442 -0
  24. package/commands/loop-front.js +122 -4
  25. package/commands/member.js +551 -19
  26. package/commands/mission.js +3674 -278
  27. package/commands/play.js +1 -1
  28. package/commands/pulse.js +65 -7
  29. package/commands/run-front.js +144 -0
  30. package/commands/run.js +10 -7
  31. package/commands/sign.js +90 -0
  32. package/commands/strings.js +301 -0
  33. package/commands/task.js +782 -108
  34. package/commands/truth.js +171 -0
  35. package/commands/xp.js +32 -8
  36. package/commands/youtube.js +72 -5
  37. package/decks/README.md +6 -12
  38. package/lib/auto-accept-certified.js +10 -0
  39. package/lib/autoland.js +391 -0
  40. package/lib/context-gatherer.js +0 -8
  41. package/lib/mission-artifact.js +504 -0
  42. package/lib/mission-room.js +846 -0
  43. package/lib/next-moves.js +212 -6
  44. package/lib/operator-next.js +7 -0
  45. package/lib/pulse.js +78 -4
  46. package/lib/runner-command.js +51 -20
  47. package/lib/runs-prune.js +242 -0
  48. package/lib/task-db.js +19 -2
  49. package/lib/task-proof.js +1 -1
  50. package/package.json +4 -4
  51. package/decks/atris-seed-pitch-v3.json +0 -118
  52. package/decks/atris-seed-pitch-v4-skeleton.json +0 -106
  53. package/decks/atris-seed-pitch-v5.json +0 -109
  54. package/decks/atris-seed-pitch-v6.json +0 -137
  55. package/decks/atris-seed-pitch-v7.json +0 -133
  56. package/decks/mark-pincus-narrative.json +0 -102
  57. package/decks/mark-pincus-sourcery.json +0 -94
  58. package/decks/yash-applied-compute-detailed.json +0 -150
  59. package/decks/yash-applied-compute-generalist.json +0 -82
  60. package/decks/yash-applied-compute-narrative.json +0 -54
  61. package/lib/ax-chat-input.js +0 -164
  62. package/lib/ax-goal.js +0 -307
  63. package/lib/ax-prefs.js +0 -63
  64. package/lib/ax-shimmer.js +0 -63
package/bin/atris.js CHANGED
@@ -128,7 +128,16 @@ function isOptionValue(args, index, optionNames) {
128
128
 
129
129
  function applyRunnerFlags(args) {
130
130
  const runnerProfile = readOptionArg(args, '--runner-profile');
131
- if (runnerProfile) process.env.ATRIS_RUNNER_PROFILE = runnerProfile;
131
+ if (runnerProfile) {
132
+ // Fail fast at the CLI boundary: an unknown profile otherwise stays silent
133
+ // until a heartbeat spawn resolves it mid-loop (silent overnight outage).
134
+ const { RUNNER_PROFILES, RUNNER_PROFILE_NAMES } = require('../lib/runner-command');
135
+ if (!Object.prototype.hasOwnProperty.call(RUNNER_PROFILES, runnerProfile)) {
136
+ console.error(`Unknown --runner-profile "${runnerProfile}". Known profiles: ${RUNNER_PROFILE_NAMES.join(', ')}.`);
137
+ process.exit(1);
138
+ }
139
+ process.env.ATRIS_RUNNER_PROFILE = runnerProfile;
140
+ }
132
141
  const runnerBin = readOptionArg(args, '--runner-bin');
133
142
  if (runnerBin) {
134
143
  process.env.ATRIS_RUNNER_BIN = runnerBin;
@@ -449,7 +458,7 @@ function showHelp() {
449
458
  console.log(' plan - Create build spec with visualization');
450
459
  console.log(' do - Execute tasks');
451
460
  console.log(' review - Validate work (tests, safety checks, docs)');
452
- console.log(' run - Auto-chain plan→do→review (autonomous loop, auto-pushes)');
461
+ console.log(' run - One bounded mission pursuit: start or resume, tick, complete');
453
462
  console.log(' run logs - Browse glass run logs (phase reasoning persisted to disk)');
454
463
  console.log(' run search - Search phase reasoning across all run logs');
455
464
  console.log(' pulse - Durable overnight self-improvement heartbeat (OS cron, install/status/tick)');
@@ -468,6 +477,7 @@ function showHelp() {
468
477
  console.log(' analytics - Show recent productivity from journals');
469
478
  console.log(' search - Search journal history (atris search <keyword>)');
470
479
  console.log(' clean - Housekeeping (stale tasks, archive journals, broken refs)');
480
+ console.log(' harvest - Find bugs and next actions from receipts, run logs, and thinking');
471
481
  console.log(' verify - Validate work is done (tests, MAP.md, changes)');
472
482
  console.log(' task - Local agent task plane (atomic claims, TODO import)');
473
483
  console.log(' mission - Goal + loop + member owner + verifier + receipt');
@@ -482,11 +492,14 @@ function showHelp() {
482
492
  console.log('');
483
493
  console.log('Optional helpers:');
484
494
  console.log(' brainstorm - Explore ideas conversationally before planning');
485
- console.log(' autopilot - Guided loop that can clarify TODOs and run plan → do → review');
495
+ console.log(' autopilot - Keep the workspace moving: mission/member loop until you stop it');
486
496
  console.log(' improve - Run one paid RL tick (POST /api/improve, deducts credits)');
487
497
  console.log(' worktree - Isolated Git worktrees plus guarded ship/merge for parallel agents');
498
+ console.log(' land - The landing: what is actually done vs still in the air; --reap backs up + clears overdue');
499
+ console.log(' autoland - Approve the policy once; certified work lands itself, you keep irreversible calls');
500
+ console.log(' sign - Co-author trailer on every commit in an atris workspace (on/off/status)');
488
501
  console.log(' visualize - Generate a Slack/deck-ready visual from a prompt');
489
- console.log(' youtube - Process YouTube videos with Gemini native video analysis');
502
+ console.log(' youtube - Process YouTube videos with timestamped transcript-first analysis');
490
503
  console.log('');
491
504
  console.log('Experiments:');
492
505
  console.log(' experiments init [slug] - Prepare atris/experiments/ or scaffold a pack');
@@ -544,7 +557,7 @@ function showHelp() {
544
557
  console.log(' soul - Show, snapshot, or fork workspace identity');
545
558
  console.log(' fleet - Inspect local fleet status');
546
559
  console.log(' agent - Select cloud agent, spawn worker requests, or run `agent doctor`');
547
- console.log(' chat - Chat with the selected Atris agent (or: atris chat scan)');
560
+ console.log(' chat - Chat with Atris 2 Fast in this workspace (--agent for cloud agent; or: atris chat scan)');
548
561
  console.log(' fast - Chat with Atris2 Fast');
549
562
  console.log(' login - Sign in or add another account');
550
563
  console.log(' logout - Sign out of current account');
@@ -831,22 +844,27 @@ function showServeHelp() {
831
844
 
832
845
  function showLoopHelp() {
833
846
  console.log('');
834
- console.log('Usage: atris loop [--dry-run] [--json] [--limit=N]');
847
+ console.log('Usage: atris loop [add|start|status|report|stop|wiki] [options]');
835
848
  console.log('');
836
849
  console.log('Description:');
837
- console.log(' Inspect wiki upkeep state and optionally refresh wiki status/log files.');
850
+ console.log(' One front door for the self-improvement loop. It reads ROADMAP.md,');
851
+ console.log(' runs bounded proof-backed work, and keeps wiki upkeep under loop wiki.');
838
852
  console.log('');
839
- console.log('Options:');
840
- console.log(' --dry-run Preview wiki loop state without writing files.');
841
- console.log(' --json Print the loop report as JSON.');
842
- console.log(' --limit=N Limit suggested source count.');
843
- console.log(' --help, -h Show this help.');
853
+ console.log('Commands:');
854
+ console.log(' atris loop add "<task>" Put a bounded task into the loop queue.');
855
+ console.log(' atris loop start [--once] Run the local loop.');
856
+ console.log(' atris loop start --overnight Install the durable heartbeat.');
857
+ console.log(' atris loop status [--json] Show heartbeat, local runs, and next moves.');
858
+ console.log(' atris loop report [--json] Show proof of handled and queued loop work.');
859
+ console.log(' atris loop stop Remove the durable heartbeat.');
860
+ console.log(' atris loop wiki Run the wiki upkeep loop.');
861
+ console.log(' --help, -h Show this help.');
844
862
  console.log('');
845
863
  }
846
864
 
847
865
  function showCleanHelp() {
848
866
  console.log('');
849
- console.log('Usage: atris clean [--dry-run]');
867
+ console.log('Usage: atris clean [--dry-run] [--json]');
850
868
  console.log('');
851
869
  console.log('Description:');
852
870
  console.log(' Check workspace housekeeping: stale tasks, MAP.md refs, old journals,');
@@ -854,6 +872,7 @@ function showCleanHelp() {
854
872
  console.log('');
855
873
  console.log('Options:');
856
874
  console.log(' --dry-run, -n Preview cleanup without changing files.');
875
+ console.log(' --json Print machine-readable cleanup results.');
857
876
  console.log(' --help, -h Show this help.');
858
877
  console.log('');
859
878
  }
@@ -876,7 +895,7 @@ function showAutopilotHelp() {
876
895
  console.log(' --runner-bin PATH Runner binary for this run');
877
896
  console.log(' --runner-template CMD Runner command template for this run');
878
897
  console.log(' --runner-model MODEL Runner model for this run');
879
- console.log(' --runner-profile NAME Runner profile for this run (e.g. atris-fast)');
898
+ console.log(` --runner-profile NAME Runner profile for this run (one of: ${require('../lib/runner-command').RUNNER_PROFILE_NAMES.join(', ')})`);
880
899
  console.log('');
881
900
  console.log('Examples:');
882
901
  console.log(' atris autopilot # Suggest from existing work');
@@ -913,12 +932,12 @@ if (command === '2' && ['fast', 'pro'].includes(String(firstCommandArg || '').to
913
932
  }
914
933
 
915
934
  // Check if this is a known command or natural language input
916
- const knownCommands = ['init', 'log', 'now', 'radar', 'ctop', 'launchpad', 'status', 'analytics', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', 'plan', 'do', 'review', 'release',
935
+ const knownCommands = ['init', 'log', 'now', 'radar', 'ctop', 'launchpad', 'status', 'analytics', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', '_start', 'plan', 'do', 'review', 'release',
917
936
  'activate', '_activate', 'agent', 'chat', 'fast', 'ax', 'console', 'serve', 'login', 'logout', 'whoami', 'switch', 'use', 'accounts', '_resolve', '_profile-email', '_switch-session', 'shell-init', 'update', 'upgrade', 'version', 'help', 'next', 'atris',
918
- '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', 'youtube',
919
- 'ingest', 'query', 'lint', 'loop', 'pulse', 'task', 'mission', 'probe', 'worktree', 'aeo', 'slop', 'security-review', 'secure', 'deck', 'site', 'theme', 'card', 'reel', 'improve', 'xp', 'play', 'gm', 'x', 'recap', 'signup', 'clarity', 'moves',
937
+ 'clean', 'harvest', 'verify', 'search', 'skill', 'member', 'codex-goal', 'app', 'apps', 'learn', 'lesson', 'plugin', 'experiments', 'receipt', 'proof', 'openclaw', 'pull', 'push', 'live', 'align', 'terminal', 'computer', 'diff', 'business', 'sync', 'youtube',
938
+ 'ingest', 'query', 'lint', 'loop', 'pulse', 'task', 'mission', 'probe', 'worktree', 'land', 'autoland', 'aeo', 'slop', 'strings', 'security-review', 'secure', 'deck', 'site', 'theme', 'card', 'reel', 'improve', 'xp', 'play', 'gm', 'x', 'recap', 'signup', 'clarity', 'moves',
920
939
  'gmail', 'calendar', 'twitter', 'slack', 'imessage', 'integrations', 'setup', 'clean-workspace', 'cw',
921
- 'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'compile', 'spaceship'];
940
+ 'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'compile', 'spaceship', 'truth', 'sign'];
922
941
 
923
942
  // Check if command is an atris.md spec file - triggers welcome visualization
924
943
  function isSpecFile(cmd) {
@@ -940,7 +959,15 @@ if (command === '--version' || command === '-v' || process.argv.includes('--vers
940
959
  // If no command OR command is not recognized, treat as natural language
941
960
  // Voice-friendly aliases — natural language → command mapping
942
961
  // Solves speech-to-text issues (inspired by gstack v0.14.6 voice-triggers)
962
+ 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';
963
+
943
964
  const voiceTriggers = {
965
+ 'start': '_start',
966
+ 'start now': '_start',
967
+ 'go': '_start',
968
+ 'keep going': '_start',
969
+ 'keepgoing': '_start',
970
+ 'keep-going': '_start',
944
971
  'review my code': 'code-review',
945
972
  'check my code': 'code-review',
946
973
  'run a review': 'code-review',
@@ -965,7 +992,12 @@ const voiceTriggers = {
965
992
  if (!command || !knownCommands.includes(command)) {
966
993
  // Check voice triggers before falling through to natural language
967
994
  const fullInput = process.argv.slice(2).join(' ').toLowerCase().trim();
968
- const triggered = voiceTriggers[fullInput];
995
+ const fullInputWithoutFlags = process.argv.slice(2)
996
+ .filter((arg, index, args) => !String(arg).startsWith('-') && !isOptionValue(args, index, RUNNER_FLAG_NAMES))
997
+ .join(' ')
998
+ .toLowerCase()
999
+ .trim();
1000
+ const triggered = voiceTriggers[fullInput] || voiceTriggers[fullInputWithoutFlags];
969
1001
  if (triggered) {
970
1002
  command = triggered;
971
1003
  // Re-check — if it's now a known command, fall through to dispatch
@@ -1355,6 +1387,12 @@ function showWelcomeVisualization() {
1355
1387
  : `${tasksInReview} waiting`;
1356
1388
  console.log(` │ ⏳ Review: ${reviewText.padEnd(26)}│`);
1357
1389
  }
1390
+ let landInfo = null;
1391
+ try { landInfo = require('../commands/land').landSummary(process.cwd()); } catch (err) { landInfo = null; }
1392
+ if (landInfo && landInfo.branches > 0) {
1393
+ const landText = `${landInfo.branches} in the air, ${landInfo.due} overdue`;
1394
+ console.log(` │ 🛬 Land: ${landText.padEnd(26)}│`);
1395
+ }
1358
1396
  console.log(` │ 📝 Journal: ${(journalEntries + ' entries today').padEnd(26)}│`);
1359
1397
  if (endgameState.slug !== 'unset' && endgameState.horizon) {
1360
1398
  const endgameLine = endgameState.slug + ' — ' + endgameState.horizon;
@@ -1377,7 +1415,13 @@ function showWelcomeVisualization() {
1377
1415
  if (tasksCertified > 0) {
1378
1416
  console.log(` Ready. ${tasksCertified} certified await accept — run 'atris task reviews'.`);
1379
1417
  } else {
1380
- console.log(` Ready. Run 'atris plan' to start.`);
1418
+ let landHint = null;
1419
+ try { landHint = require('../commands/land').landSummary(process.cwd()); } catch (err) { landHint = null; }
1420
+ if (landHint && landHint.due > 0) {
1421
+ console.log(` Ready. ${landHint.due} overdue in the landing — run 'atris land --reap'.`);
1422
+ } else {
1423
+ console.log(` Ready. Run 'atris plan' to start.`);
1424
+ }
1381
1425
  }
1382
1426
  console.log('');
1383
1427
  }
@@ -1398,6 +1442,40 @@ if (command === 'init') {
1398
1442
  console.error(`✗ Error: ${error.message || error}`);
1399
1443
  process.exit(1);
1400
1444
  });
1445
+ } else if (command === '_start') {
1446
+ const args = process.argv.slice(3);
1447
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1448
+ console.log('');
1449
+ console.log('Usage: atris start [options]');
1450
+ console.log('');
1451
+ console.log('Starts the durable mission loop for one useful self-improvement mission.');
1452
+ console.log('');
1453
+ console.log('Options:');
1454
+ console.log(' --json Print the mission route without starting it.');
1455
+ console.log(' --owner X Override mission owner.');
1456
+ console.log(' --cadence X Override mission cadence.');
1457
+ console.log('');
1458
+ process.exit(0);
1459
+ }
1460
+
1461
+ if (args.includes('--json')) {
1462
+ console.log(JSON.stringify({
1463
+ ok: true,
1464
+ action: 'start_mission_run',
1465
+ route: `atris mission run "${START_MISSION_OBJECTIVE}"`,
1466
+ reason: 'casual_launch',
1467
+ objective: START_MISSION_OBJECTIVE,
1468
+ expected_loop: 'mission_run',
1469
+ }, null, 2));
1470
+ process.exit(0);
1471
+ }
1472
+
1473
+ Promise.resolve(require('../commands/mission').missionCommand(['run', START_MISSION_OBJECTIVE, ...args]))
1474
+ .then(() => process.exit(process.exitCode || 0))
1475
+ .catch((error) => {
1476
+ console.error(`✗ Start failed: ${error.message || error}`);
1477
+ process.exit(1);
1478
+ });
1401
1479
  } else if (command === 'task') {
1402
1480
  // SQLite-backed task plane. ~/.atris/tasks.db, gitignored, per-workspace.
1403
1481
  Promise.resolve(require('../commands/task').run(process.argv.slice(3)))
@@ -1421,11 +1499,24 @@ if (command === 'init') {
1421
1499
  Promise.resolve(require('../commands/worktree').worktreeCommand(process.argv.slice(3)))
1422
1500
  .then((code) => process.exit(code || 0))
1423
1501
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1502
+ } else if (command === 'autoland') {
1503
+ Promise.resolve(require('../commands/autoland').autolandCommand(process.argv.slice(3)))
1504
+ .then((code) => process.exit(code || 0))
1505
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1506
+ } else if (command === 'land') {
1507
+ Promise.resolve(require('../commands/land').landCommand(process.argv.slice(3)))
1508
+ .then((code) => process.exit(code || 0))
1509
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1424
1510
  } else if (command === 'radar' || command === 'ctop') {
1425
1511
  const radarArgs = command === 'ctop' ? ['--agents', ...process.argv.slice(3)] : process.argv.slice(3);
1426
1512
  Promise.resolve(require('../commands/radar').radarCommand(radarArgs))
1427
1513
  .then((code) => process.exit(code || 0))
1428
1514
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1515
+ } else if (command === 'truth') {
1516
+ // Truth: one table rolling up mission state, tasks, feature proof receipts, and loop heartbeats.
1517
+ Promise.resolve(require('../commands/truth').truthCommand(process.argv.slice(3)))
1518
+ .then((code) => process.exit(code || 0))
1519
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1429
1520
  } else if (command === 'codex-goal') {
1430
1521
  Promise.resolve(require('../commands/codex-goal').codexGoalCommand(process.argv.slice(3)))
1431
1522
  .then(() => process.exit(process.exitCode || 0))
@@ -1750,23 +1841,26 @@ if (command === 'init') {
1750
1841
  }
1751
1842
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1752
1843
  console.log('');
1753
- console.log('Usage: atris run [options]');
1844
+ console.log('Usage: atris run ["objective"] [options]');
1754
1845
  console.log('');
1755
- console.log('Auto-chain plan do review cycles autonomously.');
1756
- console.log('Reads inbox ideas, creates tasks, builds them, validates, repeats.');
1846
+ console.log('One bounded mission pursuit: start a mission from the objective');
1847
+ console.log('(or resume the most logical runnable mission), tick it through the');
1848
+ console.log('mission runtime, complete on pass, then exit.');
1849
+ console.log('For an ongoing loop, use: atris autopilot');
1757
1850
  console.log('');
1758
1851
  console.log('Options:');
1759
- console.log(' --cycles=N Max cycles (default: 5)');
1760
- console.log(' --once Single plan→do→review cycle');
1761
- console.log(' --verbose Show configured runner output');
1762
- console.log(' --dry-run Preview without executing');
1763
- console.log(' --timeout=N Phase timeout in seconds (default: 600)');
1764
- console.log(' --runner-bin PATH Runner binary for this run');
1765
- console.log(' --runner-template CMD Runner command template for this run');
1766
- console.log(' --runner-model MODEL Runner model for this run');
1767
- console.log(' --runner-profile NAME Runner profile for this run (e.g. atris-fast)');
1768
- console.log(' --push Auto-push after each cycle (default: true)');
1769
- console.log(' --no-push Skip auto-push after each cycle');
1852
+ console.log(' --owner NAME Mission owner (default: mission-lead)');
1853
+ console.log(' --minutes N | --hours N Time budget for the pursuit');
1854
+ console.log(' --max-ticks N Override the tick budget');
1855
+ console.log(' --max-wall N Override the wall clock in seconds');
1856
+ console.log(' --cadence C Mission cadence (e.g. 15m)');
1857
+ console.log(' --no-complete Skip auto-complete after a passing run');
1858
+ console.log(' --legacy Old plan→do→review loop (claude -p cycles)');
1859
+ console.log('');
1860
+ console.log('Legacy options (with --legacy):');
1861
+ console.log(' --cycles=N --once --verbose --dry-run --timeout=N --no-push');
1862
+ console.log(' --runner-bin PATH / --runner-template CMD / --runner-model MODEL');
1863
+ console.log(` --runner-profile NAME Runner profile for this run (one of: ${require('../lib/runner-command').RUNNER_PROFILE_NAMES.join(', ')})`);
1770
1864
  console.log('');
1771
1865
  console.log('Subcommands:');
1772
1866
  console.log(' atris run logs [--tail N] [--cat FILE] [--json] Browse glass run logs');
@@ -1779,62 +1873,81 @@ if (command === 'init') {
1779
1873
  process.exit(0);
1780
1874
  }
1781
1875
 
1782
- const verbose = args.includes('--verbose') || args.includes('-v');
1783
- const dryRun = args.includes('--dry-run');
1784
- const once = args.includes('--once');
1785
- const push = !args.includes('--no-push');
1786
1876
  applyRunnerFlags(args);
1787
- const cyclesArg = args.find(a => a.startsWith('--cycles='));
1788
- const maxCycles = cyclesArg ? parseInt(cyclesArg.split('=')[1]) : 5;
1789
- const timeoutArg = args.find(a => a.startsWith('--timeout='));
1790
- const timeout = timeoutArg ? parseInt(timeoutArg.split('=')[1]) * 1000 : undefined;
1791
1877
 
1792
- require('../commands/run').runAtris({ maxCycles, verbose, dryRun, once, push, timeout })
1793
- .then(() => process.exit(0))
1794
- .catch((error) => {
1795
- console.error(`\u2717 Run failed: ${error.message || error}`);
1796
- process.exit(1);
1797
- });
1878
+ if (args.includes('--legacy')) {
1879
+ const legacyArgs = args.filter(a => a !== '--legacy');
1880
+ const verbose = legacyArgs.includes('--verbose') || legacyArgs.includes('-v');
1881
+ const dryRun = legacyArgs.includes('--dry-run');
1882
+ const once = legacyArgs.includes('--once');
1883
+ const push = !legacyArgs.includes('--no-push');
1884
+ const cyclesArg = legacyArgs.find(a => a.startsWith('--cycles='));
1885
+ const maxCycles = cyclesArg ? parseInt(cyclesArg.split('=')[1]) : 5;
1886
+ const timeoutArg = legacyArgs.find(a => a.startsWith('--timeout='));
1887
+ const timeout = timeoutArg ? parseInt(timeoutArg.split('=')[1]) * 1000 : undefined;
1888
+
1889
+ require('../commands/run').runAtris({ maxCycles, verbose, dryRun, once, push, timeout })
1890
+ .then(() => process.exit(0))
1891
+ .catch((error) => {
1892
+ console.error(`\u2717 Run failed: ${error.message || error}`);
1893
+ process.exit(1);
1894
+ });
1895
+ } else {
1896
+ require('../commands/run-front').runMissionFront(args)
1897
+ .then((code) => process.exit(code || 0))
1898
+ .catch((error) => {
1899
+ console.error(`\u2717 Run failed: ${error.message || error}`);
1900
+ process.exit(1);
1901
+ });
1902
+ }
1798
1903
  } else if (command === 'launchpad') {
1799
1904
  const code = require('../commands/launchpad').launchpadCommand(process.argv.slice(3));
1800
1905
  process.exit(code);
1801
1906
  } else if (command === 'autopilot') {
1802
1907
  const args = process.argv.slice(3);
1803
- if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1804
- showAutopilotHelp();
1805
- process.exit(0);
1806
- }
1807
-
1808
- // Parse options
1809
- const verbose = args.includes('--verbose') || args.includes('-v');
1810
- const dryRun = args.includes('--dry-run');
1811
- const auto = args.includes('--auto');
1812
1908
  applyRunnerFlags(args);
1813
- const maxIterationsArg = args.find(a => a.startsWith('--iterations='));
1814
- const maxIterations = maxIterationsArg ? parseInt(maxIterationsArg.split('=')[1]) : undefined;
1815
- const durationArg = args.find(a => a.startsWith('--duration='));
1816
- const duration = durationArg ? durationArg.split('=')[1] : null;
1817
-
1818
- // Get description (non-flag args)
1819
- const description = args.filter((a, i) => !a.startsWith('-') && !isOptionValue(args, i, RUNNER_FLAG_NAMES)).join(' ').trim() || null;
1820
-
1821
- const options = {
1822
- ...(maxIterations !== undefined && { maxIterations }),
1823
- verbose,
1824
- dryRun,
1825
- auto,
1826
- duration
1827
- };
1828
1909
 
1829
- let promise;
1830
- promise = require('../commands/autopilot').autopilotAtris(description, options);
1910
+ if (args.includes('--legacy')) {
1911
+ const legacyArgs = args.filter(a => a !== '--legacy');
1912
+ if (legacyArgs.includes('--help') || legacyArgs.includes('-h') || legacyArgs[0] === 'help') {
1913
+ showAutopilotHelp();
1914
+ process.exit(0);
1915
+ }
1831
1916
 
1832
- promise
1833
- .then(() => process.exit(0))
1834
- .catch((error) => {
1835
- console.error(`✗ Autopilot failed: ${error.message || error}`);
1836
- process.exit(1);
1837
- });
1917
+ // Parse options
1918
+ const verbose = legacyArgs.includes('--verbose') || legacyArgs.includes('-v');
1919
+ const dryRun = legacyArgs.includes('--dry-run');
1920
+ const auto = legacyArgs.includes('--auto');
1921
+ const maxIterationsArg = legacyArgs.find(a => a.startsWith('--iterations='));
1922
+ const maxIterations = maxIterationsArg ? parseInt(maxIterationsArg.split('=')[1]) : undefined;
1923
+ const durationArg = legacyArgs.find(a => a.startsWith('--duration='));
1924
+ const duration = durationArg ? durationArg.split('=')[1] : null;
1925
+
1926
+ // Get description (non-flag args)
1927
+ const description = legacyArgs.filter((a, i) => !a.startsWith('-') && !isOptionValue(legacyArgs, i, RUNNER_FLAG_NAMES)).join(' ').trim() || null;
1928
+
1929
+ const options = {
1930
+ ...(maxIterations !== undefined && { maxIterations }),
1931
+ verbose,
1932
+ dryRun,
1933
+ auto,
1934
+ duration
1935
+ };
1936
+
1937
+ require('../commands/autopilot').autopilotAtris(description, options)
1938
+ .then(() => process.exit(0))
1939
+ .catch((error) => {
1940
+ console.error(`✗ Autopilot failed: ${error.message || error}`);
1941
+ process.exit(1);
1942
+ });
1943
+ } else {
1944
+ Promise.resolve(require('../commands/autopilot-front').autopilotFront(args))
1945
+ .then((code) => process.exit(code || 0))
1946
+ .catch((error) => {
1947
+ console.error(`✗ Autopilot failed: ${error.message || error}`);
1948
+ process.exit(1);
1949
+ });
1950
+ }
1838
1951
  } else if (command === 'brainstorm') {
1839
1952
  require('../commands/brainstorm').brainstormAtris()
1840
1953
  .then(() => process.exit(0))
@@ -1922,7 +2035,14 @@ if (command === 'init') {
1922
2035
  process.exit(0);
1923
2036
  }
1924
2037
  const dryRun = process.argv.includes('--dry-run') || process.argv.includes('-n');
1925
- require('../commands/clean').cleanAtris({ dryRun });
2038
+ const json = process.argv.includes('--json');
2039
+ require('../commands/clean').cleanAtris({ dryRun, json });
2040
+ } else if (command === 'harvest') {
2041
+ Promise.resolve(require('../commands/harvest').harvestCommand(process.argv.slice(3)))
2042
+ .catch((err) => {
2043
+ console.error(err.message || err);
2044
+ process.exit(1);
2045
+ });
1926
2046
  } else if (command === 'verify') {
1927
2047
  const args = process.argv.slice(3);
1928
2048
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
@@ -2130,8 +2250,8 @@ if (command === 'init') {
2130
2250
  showLoopHelp();
2131
2251
  process.exit(0);
2132
2252
  }
2133
- require('../commands/loop').loopAtris(args)
2134
- .then(() => process.exit(0))
2253
+ Promise.resolve(require('../commands/loop-front').loopFront(args))
2254
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2135
2255
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2136
2256
  } else if (command === 'clean-workspace' || command === 'cw') {
2137
2257
  const { cleanWorkspace } = require('../commands/workspace-clean');
@@ -2152,11 +2272,21 @@ if (command === 'init') {
2152
2272
  Promise.resolve(require('../commands/compile').compileCommand(subcommand, ...args))
2153
2273
  .then(() => process.exit(process.exitCode || 0))
2154
2274
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2275
+ } else if (command === 'sign') {
2276
+ // Sign: prepare-commit-msg hook that credits Atris as co-author on commits in atris workspaces.
2277
+ try { process.exit(require('../commands/sign').signCommand(process.argv[3]) || 0); }
2278
+ catch (err) { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); }
2155
2279
  } else if (command === 'slop') {
2156
2280
  // Slop: deterministic frontend-slop detector (no LLM). Exit 1 = slop found, for CI + the autopilot gate.
2157
2281
  Promise.resolve(require('../commands/slop').slopCommand(process.argv.slice(3)))
2158
2282
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
2159
2283
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2284
+ } else if (command === 'strings') {
2285
+ // Strings: content design system from live code (no LLM). Extracts UI strings, flags variants,
2286
+ // enforces preferred terms at the gate. Exit 1 = variants/banned terms found, for CI + the gate.
2287
+ Promise.resolve(require('../commands/strings').stringsCommand(process.argv.slice(3)))
2288
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2289
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2160
2290
  } else if (command === 'security-review' || command === 'secure') {
2161
2291
  // Security review: deterministic secrets/PII/code-risk scan (no LLM). Exit 1 = HIGH finding,
2162
2292
  // for the autopilot/mission/CI gate + a SOC 2 evidence artifact via --json.
@@ -2410,13 +2540,12 @@ async function agentAtris() {
2410
2540
  // Respect -h / --help / help before any auth/state work
2411
2541
  const firstArg = process.argv[3];
2412
2542
  if (firstArg === '-h' || firstArg === '--help' || firstArg === 'help') {
2413
- console.log('Usage: atris agent [doctor|dogfood|spawn|spawns|spawn-status]');
2543
+ console.log('Usage: atris agent [doctor|spawn|spawns|spawn-status]');
2414
2544
  console.log('');
2415
2545
  console.log(' Pick which cloud agent to chat with from this workspace.');
2416
2546
  console.log(' Run `atris agent spawn <role> --task "..."` to create a worker request.');
2417
2547
  console.log(' Run `atris agent spawns` to list worker requests.');
2418
2548
  console.log(' Run `atris agent doctor` to verify local AI CLIs can see Atris context.');
2419
- console.log(' Run `atris agent dogfood --live` to smoke-test Devin/Droid with GLM 5.2.');
2420
2549
  console.log(' Requires `atris login` first.');
2421
2550
  console.log('');
2422
2551
  console.log(' After selecting, use: atris chat ["message"]');
@@ -2427,7 +2556,19 @@ async function agentAtris() {
2427
2556
  agentDoctor();
2428
2557
  }
2429
2558
  if (firstArg === 'dogfood') {
2430
- const result = require('../commands/agent-spawn').agentDogfoodCommand(process.argv.slice(4));
2559
+ // Internal diagnostic, gated off the public CLI. Operators use `atris agent doctor`.
2560
+ if (!process.env.ATRIS_INTERNAL_AGENT_DOGFOOD) {
2561
+ console.error('atris agent dogfood is an internal diagnostic and is not part of the public CLI.');
2562
+ console.error('Run `atris agent doctor` to verify local AI CLIs can see Atris context.');
2563
+ process.exit(1);
2564
+ }
2565
+ const dogfoodArgs = process.argv.slice(4);
2566
+ if (dogfoodArgs.includes('--help') || dogfoodArgs.includes('-h') || dogfoodArgs[0] === 'help') {
2567
+ console.log('Internal usage: atris agent dogfood [--live]');
2568
+ console.log(' Smoke-test Devin/Droid with GLM 5.2. Gated behind ATRIS_INTERNAL_AGENT_DOGFOOD.');
2569
+ process.exit(0);
2570
+ }
2571
+ const result = require('../commands/agent-spawn').agentDogfoodCommand(dogfoodArgs);
2431
2572
  process.exit(result.ok ? 0 : 1);
2432
2573
  }
2433
2574
  if (firstArg === 'spawn') {
@@ -2533,18 +2674,21 @@ async function agentAtris() {
2533
2674
 
2534
2675
 
2535
2676
  async function chatAtris() {
2536
- // Get message from command line args
2537
- const message = process.argv.slice(3).join(' ').trim();
2677
+ // Get message from command line args; --agent forces the legacy cloud-agent lane
2678
+ const rawArgs = process.argv.slice(3);
2679
+ const agentLane = rawArgs.includes('--agent');
2680
+ const message = rawArgs.filter(arg => arg !== '--agent').join(' ').trim();
2538
2681
 
2539
2682
  // Respect -h / --help before any auth/state checks
2540
2683
  if (message === '-h' || message === '--help' || message === 'help') {
2541
2684
  console.log('Usage: atris chat ["message"]');
2542
2685
  console.log('');
2543
- console.log(' Open an interactive session with the selected agent, or send a one-shot message.');
2544
- console.log(' Requires `atris login` and `atris agent` to be run first.');
2686
+ console.log(' Chat with Atris 2 Fast in this workspace: tools attached, same turn as `ax --fast`.');
2687
+ console.log(' Requires `atris login`.');
2545
2688
  console.log('');
2546
- console.log(' atris chat Interactive REPL with selected agent');
2547
- console.log(' atris chat "what now?" One-shot message');
2689
+ console.log(' atris chat Interactive chat (ax --fast --chat)');
2690
+ console.log(' atris chat "what now?" One-shot message (ax --fast)');
2691
+ console.log(' atris chat --agent [...] Legacy cloud-agent lane (needs `atris agent`)');
2548
2692
  process.exit(0);
2549
2693
  }
2550
2694
 
@@ -2560,6 +2704,22 @@ async function chatAtris() {
2560
2704
  process.exit(await runLocalFastMission(missionIntent));
2561
2705
  }
2562
2706
 
2707
+ // Workspace standard v2: `atris chat` is the same turn as `ax --fast`,
2708
+ // Atris 2 Fast with tools attached, ax owning routing (local tool loop,
2709
+ // cloud fallback when no backend listens). The pro-chat cloud-agent lane
2710
+ // survives behind --agent only; it 404s the moment a saved agent is
2711
+ // deleted server-side and cannot see the workspace.
2712
+ if (!agentLane) {
2713
+ try {
2714
+ const axPath = path.join(__dirname, '..', 'ax');
2715
+ const axArgs = message ? ['--fast', message] : ['--fast', '--chat'];
2716
+ const run = spawnSync(process.execPath, [axPath, ...axArgs], { stdio: 'inherit' });
2717
+ process.exit(run.status || 0);
2718
+ } catch {
2719
+ // ax unavailable: fall through to the agent lane.
2720
+ }
2721
+ }
2722
+
2563
2723
  printAtrisGoalBanner(process.cwd());
2564
2724
 
2565
2725
  // Check agent selected
@@ -2609,7 +2769,13 @@ async function chatOnce(config, credentials, message) {
2609
2769
  await streamProChat(endpoint, credentials.token, body);
2610
2770
  console.log('\n\n✓ Complete\n');
2611
2771
  } catch (error) {
2612
- console.error(`\n✗ Error: ${error.message || error}`);
2772
+ const detail = String(error.message || error);
2773
+ if (/404/.test(detail) && /agent not found/i.test(detail)) {
2774
+ console.error(`\n✗ Error: Agent "${config.agent_name || agentId}" no longer exists on the server.`);
2775
+ console.error(' Run "atris agent" to pick a new one, or drop --agent to use the fast workspace lane.');
2776
+ } else {
2777
+ console.error(`\n✗ Error: ${detail}`);
2778
+ }
2613
2779
  process.exit(1);
2614
2780
  }
2615
2781
  }
@@ -2809,6 +2975,20 @@ async function atrisFastChat() {
2809
2975
  process.exit(await runLocalFastMission(missionIntent));
2810
2976
  }
2811
2977
 
2978
+ // One routing brain: `atris fast` defers to ax's AX Context Standard. A
2979
+ // workspace-shaped question asked from inside a workspace needs local tools
2980
+ // (this lane is a tool-less cloud one-shot and confabulates on repo
2981
+ // questions — SwapBench 2026-07-02), so delegate the whole turn to ax.
2982
+ try {
2983
+ const axModule = require('../ax');
2984
+ if (axModule.resolveRoute(message) === 'local') {
2985
+ const run = spawnSync(process.execPath, [path.join(__dirname, '..', 'ax'), '--fast', message], { stdio: 'inherit' });
2986
+ process.exit(run.status || 0);
2987
+ }
2988
+ } catch {
2989
+ // ax unavailable: fall through to the plain cloud one-shot.
2990
+ }
2991
+
2812
2992
  printAtrisGoalBanner(process.cwd());
2813
2993
 
2814
2994
  const ensured = await ensureValidCredentials();
@@ -2840,135 +3020,3 @@ async function atrisFastOnce(credentials, message) {
2840
3020
  await streamProChat(endpoint, credentials.token, body);
2841
3021
  console.log('\n\n✓ Complete\n');
2842
3022
  }
2843
-
2844
- async function atrisDevEntry(userInput = null) {
2845
- // Load workspace context and present planning-ready state
2846
- // userInput: optional task description for hot start
2847
- const targetDir = path.join(process.cwd(), 'atris');
2848
-
2849
- // Check if Atris is initialized
2850
- if (!fs.existsSync(targetDir)) {
2851
- console.log('');
2852
- console.log('🚀 Welcome to Atris\n');
2853
- console.log('Not initialized yet. Let\'s get started:\n');
2854
- console.log(' → atris init Set up your workspace');
2855
- console.log(' → atris help See all commands\n');
2856
- return;
2857
- }
2858
-
2859
- ensureLogDirectory();
2860
- const { logFile, dateFormatted } = getLogPath();
2861
- if (!fs.existsSync(logFile)) {
2862
- createLogFile(logFile, dateFormatted);
2863
- }
2864
-
2865
- // Load context
2866
- const workspaceDir = process.cwd();
2867
- const state = detectWorkspaceState(workspaceDir);
2868
- const context = loadContext(workspaceDir);
2869
-
2870
- // Detect existing features
2871
- const featuresDir = path.join(targetDir, 'features');
2872
- let existingFeatures = [];
2873
- if (fs.existsSync(featuresDir)) {
2874
- existingFeatures = fs.readdirSync(featuresDir)
2875
- .filter(name => {
2876
- const featurePath = path.join(featuresDir, name);
2877
- return fs.statSync(featurePath).isDirectory() && !name.startsWith('_');
2878
- });
2879
- }
2880
-
2881
- console.log('');
2882
- console.log('┌─────────────────────────────────────────────────────────────┐');
2883
- console.log('│ Atris Mode │');
2884
- console.log('└─────────────────────────────────────────────────────────────┘');
2885
- console.log('');
2886
- console.log(`📅 ${dateFormatted}`);
2887
- console.log('');
2888
-
2889
- // Show existing features
2890
- if (existingFeatures.length > 0) {
2891
- console.log('📦 Features: ' + existingFeatures.join(', '));
2892
- console.log('');
2893
- }
2894
-
2895
- // Show active work
2896
- if (context.inProgressFeatures.length > 0) {
2897
- console.log('⚡ Active: ' + context.inProgressFeatures.join(', '));
2898
- console.log('');
2899
- }
2900
-
2901
- // Show inbox
2902
- if (context.hasInbox && context.inboxItems.length > 0) {
2903
- console.log(`📥 Inbox (${context.inboxItems.length}):`);
2904
- context.inboxItems.slice(0, 3).forEach((item, i) => {
2905
- const preview = item.length > 50 ? item.substring(0, 47) + '...' : item;
2906
- console.log(` ${i + 1}. ${preview}`);
2907
- });
2908
- if (context.inboxItems.length > 3) {
2909
- console.log(` ... and ${context.inboxItems.length - 3} more`);
2910
- }
2911
- console.log('');
2912
- }
2913
-
2914
- // Show recent completions
2915
- const logContent = fs.existsSync(logFile) ? fs.readFileSync(logFile, 'utf8') : '';
2916
- const completedMatch = logContent.match(/## Completed ✅\n([\s\S]*?)(?=\n##|$)/);
2917
- if (completedMatch && completedMatch[1].trim()) {
2918
- const completedItems = completedMatch[1].trim().split('\n')
2919
- .filter(line => line.match(/^- \*\*C\d+:/))
2920
- .slice(-2);
2921
- if (completedItems.length > 0) {
2922
- console.log('✅ Recent:');
2923
- completedItems.forEach(item => {
2924
- const match = item.match(/^- \*\*C\d+:\s*(.+)\*\*/);
2925
- if (match) {
2926
- const text = match[1].length > 50 ? match[1].substring(0, 47) + '...' : match[1];
2927
- console.log(` • ${text}`);
2928
- }
2929
- });
2930
- console.log('');
2931
- }
2932
- }
2933
-
2934
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2935
- console.log('atris — navigator agent');
2936
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2937
- console.log('');
2938
-
2939
- if (userInput) {
2940
- // Hot start - user provided task
2941
- console.log('User wants:');
2942
- console.log(`"${userInput}"`);
2943
- console.log('');
2944
- } else {
2945
- // Cold start - no specific task
2946
- console.log('Wait for user to describe what they want.');
2947
- console.log('');
2948
- }
2949
-
2950
- console.log('⚠️ APPROVAL REQUIRED — Follow this workflow:');
2951
- console.log('');
2952
- console.log('STEP 1: Show ASCII visualization');
2953
- console.log(' Create diagrams showing architecture/flow/UI');
2954
- console.log(' SHOW diagrams to user and WAIT for approval.');
2955
- console.log('');
2956
- console.log('STEP 2: After approval, determine scope');
2957
- if (existingFeatures.length > 0) {
2958
- console.log(' Existing: ' + existingFeatures.join(', '));
2959
- }
2960
- console.log(' NEW feature → atris/features/[name]/idea.md + build.md + validate.md');
2961
- console.log(' EXISTING → Update that feature\'s docs');
2962
- console.log(' SIMPLE → TODO.md only');
2963
- console.log('');
2964
- console.log('STEP 3: Create/update docs');
2965
- console.log(' idea.md = intent (any format)');
2966
- console.log(' build.md = technical spec');
2967
- console.log(' validate.md = proof it works (from _templates/validate.md.template)');
2968
- console.log(' lessons.md = read past lessons before planning, write new ones after validating');
2969
- console.log('');
2970
- console.log('⛔ DO NOT execute — that\'s for "atris do"');
2971
- console.log('');
2972
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2973
- console.log('');
2974
- }