atris 3.16.1 → 3.22.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 (65) hide show
  1. package/README.md +32 -7
  2. package/atris/skills/atris/SKILL.md +15 -2
  3. package/atris/skills/atris-feedback/SKILL.md +7 -0
  4. package/atris/skills/design/SKILL.md +29 -2
  5. package/atris/skills/engines/SKILL.md +44 -0
  6. package/atris/skills/flow/SKILL.md +1 -1
  7. package/atris/skills/wake/SKILL.md +37 -0
  8. package/atris/skills/youtube/SKILL.md +13 -39
  9. package/atris/team/validator/MEMBER.md +1 -0
  10. package/atris/wiki/concepts/agent-activation-contract.md +3 -3
  11. package/atris/wiki/concepts/workspace-initialization-contract.md +3 -3
  12. package/atris/wiki/index.md +1 -0
  13. package/atris.md +43 -19
  14. package/bin/atris.js +413 -31
  15. package/commands/agent-spawn.js +480 -0
  16. package/commands/analytics.js +6 -3
  17. package/commands/apps.js +11 -0
  18. package/commands/autopilot.js +42 -18
  19. package/commands/brain.js +74 -7
  20. package/commands/brainstorm.js +9 -58
  21. package/commands/clean.js +1 -4
  22. package/commands/compile.js +9 -4
  23. package/commands/console.js +8 -3
  24. package/commands/deck.js +184 -0
  25. package/commands/init.js +22 -11
  26. package/commands/lesson.js +76 -0
  27. package/commands/member.js +252 -48
  28. package/commands/mission.js +405 -13
  29. package/commands/now.js +4 -2
  30. package/commands/probe.js +105 -27
  31. package/commands/pulse.js +504 -0
  32. package/commands/radar.js +1 -0
  33. package/commands/recap.js +71 -25
  34. package/commands/run.js +615 -22
  35. package/commands/site.js +48 -0
  36. package/commands/slop.js +307 -0
  37. package/commands/spaceship.js +39 -0
  38. package/commands/sync.js +0 -2
  39. package/commands/task.js +429 -37
  40. package/commands/theme.js +217 -0
  41. package/commands/verify.js +7 -3
  42. package/lib/activity-stream.js +166 -0
  43. package/lib/auto-accept-certified.js +23 -1
  44. package/lib/context-gatherer.js +170 -0
  45. package/lib/deck-from-md.js +110 -0
  46. package/lib/escape-regexp.js +13 -0
  47. package/lib/file-ops.js +6 -3
  48. package/lib/html-render.js +257 -0
  49. package/lib/journal.js +1 -1
  50. package/lib/lesson-contradiction.js +113 -0
  51. package/lib/memory-view.js +95 -0
  52. package/lib/policy-lessons.js +3 -2
  53. package/lib/pulse.js +401 -0
  54. package/lib/runner-command.js +156 -0
  55. package/lib/site.js +114 -0
  56. package/lib/slides-deck.js +237 -0
  57. package/lib/state-detection.js +1 -4
  58. package/lib/task-db.js +101 -4
  59. package/lib/task-proof.js +1 -1
  60. package/lib/theme.js +264 -0
  61. package/lib/todo-fallback.js +2 -1
  62. package/lib/todo-sections.js +33 -0
  63. package/package.json +1 -2
  64. package/utils/api.js +14 -2
  65. package/atris/atrisDev.md +0 -717
package/bin/atris.js CHANGED
@@ -36,6 +36,13 @@ const {
36
36
 
37
37
  // State detection for smart default
38
38
  const { detectWorkspaceState, loadContext } = require('../lib/state-detection');
39
+ const {
40
+ saveContextProfile,
41
+ createStarterTask,
42
+ shouldGatherContext,
43
+ isAtrisMetaQuestion,
44
+ renderPrompt: renderContextGathererPrompt,
45
+ } = require('../lib/context-gatherer');
39
46
 
40
47
  // Journal & config utilities (canonical modules)
41
48
  const { getLogPath, ensureLogDirectory, createLogFile } = require('../lib/file-ops');
@@ -107,6 +114,41 @@ if (!skipUpdateCheck && (!updateCommand || (updateCommand && !['version', 'updat
107
114
  let command = process.argv[2];
108
115
  const commandArgs = process.argv.slice(3);
109
116
  const firstCommandArg = process.argv[3];
117
+ const RUNNER_FLAG_NAMES = ['--runner-bin', '--runner-template', '--runner-model', '--runner-profile'];
118
+
119
+ function readOptionArg(args, name) {
120
+ const prefix = `${name}=`;
121
+ const inline = args.find((arg) => arg.startsWith(prefix));
122
+ if (inline) return inline.slice(prefix.length);
123
+ const index = args.indexOf(name);
124
+ if (index !== -1 && index < args.length - 1 && !String(args[index + 1]).startsWith('--')) return args[index + 1];
125
+ return null;
126
+ }
127
+
128
+ function isOptionValue(args, index, optionNames) {
129
+ return index > 0 && optionNames.includes(args[index - 1]);
130
+ }
131
+
132
+ function applyRunnerFlags(args) {
133
+ const runnerProfile = readOptionArg(args, '--runner-profile');
134
+ if (runnerProfile) process.env.ATRIS_RUNNER_PROFILE = runnerProfile;
135
+ const runnerBin = readOptionArg(args, '--runner-bin');
136
+ if (runnerBin) {
137
+ process.env.ATRIS_RUNNER_BIN = runnerBin;
138
+ process.env.ATRIS_CLAUDE_BIN = runnerBin;
139
+ }
140
+ const runnerTemplate = readOptionArg(args, '--runner-template');
141
+ if (runnerTemplate) {
142
+ process.env.ATRIS_RUNNER_COMMAND_TEMPLATE = runnerTemplate;
143
+ process.env.ATRIS_CLAUDE_COMMAND_TEMPLATE = runnerTemplate;
144
+ }
145
+ const runnerModel = readOptionArg(args, '--runner-model');
146
+ if (runnerModel) {
147
+ process.env.ATRIS_RUNNER_MODEL = runnerModel;
148
+ process.env.ATRIS_CLAUDE_MODEL = runnerModel;
149
+ }
150
+ }
151
+
110
152
  const isBusinessSyncSafetyCommand = command === 'sync'
111
153
  && (
112
154
  commandArgs.includes('--status')
@@ -319,7 +361,15 @@ function showHelp() {
319
361
  console.log(' plan - Create build spec with visualization');
320
362
  console.log(' do - Execute tasks');
321
363
  console.log(' review - Validate work (tests, safety checks, docs)');
364
+ console.log(' slop - Detect frontend AI-slop tells (deterministic, exit 1 = found)');
365
+ console.log(' deck - Generate a premium Google Slides deck from a content spec');
366
+ console.log(' site - Build a beautiful static site from a folder of markdown');
367
+ console.log(' theme - Brand themes: "theme create" builds your own by feel (deck/html/site)');
322
368
  console.log(' run - Auto-chain plan→do→review (autonomous loop, auto-pushes)');
369
+ console.log(' run logs - Browse glass run logs (phase reasoning persisted to disk)');
370
+ console.log(' run search - Search phase reasoning across all run logs');
371
+ console.log(' pulse - Durable overnight self-improvement heartbeat (OS cron, install/status/tick)');
372
+ console.log(' spaceship - Bounded overnight runner that survives bad ticks and emails updates');
323
373
  console.log('');
324
374
  console.log('Context & tracking:');
325
375
  console.log(' log - Add ideas to inbox');
@@ -328,7 +378,7 @@ function showHelp() {
328
378
  console.log(' radar - Show live agents joined with tasks, missions, and worktrees');
329
379
  console.log(' ctop - Show a process-first live agent CPU/memory view');
330
380
  console.log(' status - See local work and completions (`atris status <business>` for remote)');
331
- console.log(' recap - What your AI team did, in plain English (--share for paste-ready)');
381
+ console.log(' recap - What your AI team did, in plain English (--share, or --html for a memory page)');
332
382
  console.log(' xp - Show Career XP and contribution graph');
333
383
  console.log(' analytics - Show recent productivity from journals');
334
384
  console.log(' search - Search journal history (atris search <keyword>)');
@@ -351,6 +401,7 @@ function showHelp() {
351
401
  console.log(' improve - Run one paid RL tick (POST /api/improve, deducts credits)');
352
402
  console.log(' worktree - Isolated Git worktrees plus guarded ship/merge for parallel agents');
353
403
  console.log(' visualize - Generate a Slack/deck-ready visual from a prompt');
404
+ console.log(' youtube - Process YouTube videos with Gemini native video analysis');
354
405
  console.log('');
355
406
  console.log('Experiments:');
356
407
  console.log(' experiments init [slug] - Prepare atris/experiments/ or scaffold a pack');
@@ -407,8 +458,9 @@ function showHelp() {
407
458
  console.log(' console - Start/attach always-on coding console (tmux daemon)');
408
459
  console.log(' soul - Show, snapshot, or fork workspace identity');
409
460
  console.log(' fleet - Inspect local fleet status');
410
- console.log(' agent - Select cloud agent, or run `agent doctor` for local CLI wiring');
411
- console.log(' chat - Chat with the selected Atris agent');
461
+ console.log(' agent - Select cloud agent, spawn worker requests, or run `agent doctor`');
462
+ console.log(' chat - Chat with the selected Atris agent (or: atris chat scan)');
463
+ console.log(' fast - Chat with Atris2 Fast');
412
464
  console.log(' login - Sign in or add another account');
413
465
  console.log(' logout - Sign out of current account');
414
466
  console.log(' whoami - Show active account');
@@ -733,8 +785,12 @@ function showAutopilotHelp() {
733
785
  console.log(' --auto Execute without waiting for approval');
734
786
  console.log(' --duration=TIME Run for a time limit (e.g. 1h, 30m, 90m)');
735
787
  console.log(' --iterations=N Max tasks before stopping');
736
- console.log(' --verbose, -v Show detailed claude output');
788
+ console.log(' --verbose, -v Show detailed runner output');
737
789
  console.log(' --dry-run Show suggestions without executing');
790
+ console.log(' --runner-bin PATH Runner binary for this run');
791
+ console.log(' --runner-template CMD Runner command template for this run');
792
+ console.log(' --runner-model MODEL Runner model for this run');
793
+ console.log(' --runner-profile NAME Runner profile for this run (e.g. atris-fast)');
738
794
  console.log('');
739
795
  console.log('Examples:');
740
796
  console.log(' atris autopilot # Suggest from existing work');
@@ -772,11 +828,11 @@ if (command === '2' && ['fast', 'pro'].includes(String(firstCommandArg || '').to
772
828
 
773
829
  // Check if this is a known command or natural language input
774
830
  const knownCommands = ['init', 'log', 'now', 'radar', 'ctop', 'status', 'analytics', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', 'plan', 'do', 'review', 'release',
775
- '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',
776
- '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',
777
- 'ingest', 'query', 'lint', 'loop', 'task', 'mission', 'probe', 'worktree', 'aeo', 'improve', 'xp', 'play', 'gm', 'x', 'recap',
831
+ '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',
832
+ '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',
833
+ 'ingest', 'query', 'lint', 'loop', 'pulse', 'task', 'mission', 'probe', 'worktree', 'aeo', 'slop', 'deck', 'site', 'theme', 'improve', 'xp', 'play', 'gm', 'x', 'recap',
778
834
  'gmail', 'calendar', 'twitter', 'slack', 'imessage', 'integrations', 'setup', 'clean-workspace', 'cw',
779
- 'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'compile'];
835
+ 'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'compile', 'spaceship'];
780
836
 
781
837
  // Check if command is an atris.md spec file - triggers welcome visualization
782
838
  function isSpecFile(cmd) {
@@ -865,11 +921,24 @@ if (!command || !knownCommands.includes(command)) {
865
921
  return;
866
922
  }
867
923
 
924
+ function printAtrisOverview() {
925
+ console.log('');
926
+ console.log('Atris is an AI computer for a workspace.');
927
+ console.log('It keeps project context, tasks, memory, tools, and proof in one loop: plan -> do -> review.');
928
+ console.log('Run `atris` to load the current workspace, or `atris help` to see commands.');
929
+ console.log('');
930
+ }
931
+
868
932
  async function interactiveEntry(userInput) {
869
933
  const workspaceDir = process.cwd();
870
934
  const state = detectWorkspaceState(workspaceDir);
871
935
  const context = loadContext(workspaceDir);
872
936
 
937
+ if (isAtrisMetaQuestion(userInput)) {
938
+ printAtrisOverview();
939
+ return;
940
+ }
941
+
873
942
  // Fresh install - offer init
874
943
  if (state.state === 'fresh') {
875
944
  console.log('\nNo atris/ folder found. Run: atris init');
@@ -935,28 +1004,46 @@ async function interactiveEntry(userInput) {
935
1004
  console.log('└─────────────────────────────────────────────────────────────┘');
936
1005
 
937
1006
  const mapStatus = context.mapStatus || (context.mapExists ? 'ready' : 'missing');
938
- if (mapStatus !== 'ready') {
939
- console.log('');
940
- console.log('┌─────────────────────────────────────────────────────────────┐');
941
- console.log('│ BOOTSTRAP REQUIRED │');
942
- console.log('└─────────────────────────────────────────────────────────────┘');
943
- console.log('');
944
- console.log('🗺️ Atris needs a real `atris/MAP.md` (navigation index with file:line refs).');
945
- console.log('');
946
- console.log('Copy/paste into your coding agent:');
947
- console.log('─────────────────────────────────────────────────────────────');
948
- console.log('Read `atris/atris.md`, then generate a complete `atris/MAP.md` for this repo.');
949
- console.log('Rules: include file:line refs, keep it grep-friendly, do NOT change code.');
950
- if (userInput) {
951
- console.log('');
952
- console.log('After MAP is generated, run:');
953
- console.log(`- atris ${userInput}`);
954
- } else {
1007
+ if (shouldGatherContext({
1008
+ root: workspaceDir,
1009
+ userInput,
1010
+ mapStatus,
1011
+ liveMissionsCount,
1012
+ wipCount,
1013
+ backlogCount,
1014
+ inboxCount,
1015
+ })) {
1016
+ const answer = String(userInput || '').trim() || await askContextGatherer(workspaceDir);
1017
+ if (isAtrisMetaQuestion(answer)) {
1018
+ printAtrisOverview();
1019
+ return;
1020
+ }
1021
+ if (!answer.trim()) {
955
1022
  console.log('');
956
- console.log('Then rerun: atris');
1023
+ console.log('No problem. When you are ready, answer in normal words.');
1024
+ console.log('Example: "help me organize college applications" or "help me build a small website".');
1025
+ return;
957
1026
  }
958
- console.log('─────────────────────────────────────────────────────────────');
1027
+ const profile = saveContextProfile(workspaceDir, answer, { source: userInput ? 'hot_start' : 'cold_start' });
1028
+ const starter = createStarterTask(workspaceDir, answer);
959
1029
  console.log('');
1030
+ console.log('Got it. I saved your first direction.');
1031
+ console.log(`Focus: ${profile.first_answer}`);
1032
+ if (starter && starter.display_id) {
1033
+ console.log(`First task: ${starter.display_id} — ${starter.title}`);
1034
+ } else if (starter && starter.title) {
1035
+ console.log(`First task: ${starter.title}`);
1036
+ }
1037
+ if (mapStatus !== 'ready') {
1038
+ printMapBootstrap({ userInput: answer, prefix: 'Next setup step' });
1039
+ return;
1040
+ }
1041
+ await planCmd(answer);
1042
+ return;
1043
+ }
1044
+
1045
+ if (mapStatus !== 'ready') {
1046
+ printMapBootstrap({ userInput });
960
1047
  return;
961
1048
  }
962
1049
 
@@ -1047,9 +1134,45 @@ async function interactiveEntry(userInput) {
1047
1134
  await planCmd(request);
1048
1135
  }
1049
1136
 
1137
+ async function askContextGatherer(workspaceDir) {
1138
+ console.log(renderContextGathererPrompt({ projectName: path.basename(workspaceDir) }));
1139
+ const rl = readline.createInterface({
1140
+ input: process.stdin,
1141
+ output: process.stdout
1142
+ });
1143
+ const answer = await new Promise(r => rl.question('> ', r));
1144
+ rl.close();
1145
+ return answer;
1146
+ }
1147
+
1148
+ function printMapBootstrap({ userInput, prefix = 'Bootstrap required' } = {}) {
1149
+ console.log('');
1150
+ console.log('┌─────────────────────────────────────────────────────────────┐');
1151
+ console.log(`│ ${String(prefix).toUpperCase().padEnd(60)}│`);
1152
+ console.log('└─────────────────────────────────────────────────────────────┘');
1153
+ console.log('');
1154
+ console.log('Atris needs a real `atris/MAP.md` so future steps are grounded in the workspace.');
1155
+ console.log('');
1156
+ console.log('For an agent:');
1157
+ console.log('─────────────────────────────────────────────────────────────');
1158
+ console.log('Read `atris/atris.md`, then generate a complete `atris/MAP.md` for this repo.');
1159
+ console.log('Rules: include file:line refs, keep it grep-friendly, do NOT change code.');
1160
+ if (userInput) {
1161
+ console.log('');
1162
+ console.log('After MAP is generated, continue with:');
1163
+ console.log(`- ${userInput}`);
1164
+ } else {
1165
+ console.log('');
1166
+ console.log('Then rerun: atris');
1167
+ }
1168
+ console.log('─────────────────────────────────────────────────────────────');
1169
+ console.log('');
1170
+ }
1171
+
1050
1172
  // ASCII Welcome Visualization
1051
1173
  function showWelcomeVisualization() {
1052
1174
  const { getTaskCounts } = require('../lib/state-detection');
1175
+ const { readEndgameState } = require('../commands/autopilot');
1053
1176
  const cwd = process.cwd();
1054
1177
  const atrisDir = path.join(cwd, 'atris');
1055
1178
  const projectName = path.basename(cwd);
@@ -1063,6 +1186,7 @@ function showWelcomeVisualization() {
1063
1186
  let journalEntries = 0;
1064
1187
  let hasMap = false;
1065
1188
  let isInitialized = fs.existsSync(atrisDir);
1189
+ let endgameState = { slug: 'unset', horizon: '' };
1066
1190
 
1067
1191
  if (isInitialized) {
1068
1192
  // Check MAP.md
@@ -1086,6 +1210,13 @@ function showWelcomeVisualization() {
1086
1210
  // Silently fail - show 0 tasks if reading fails
1087
1211
  }
1088
1212
 
1213
+ // Read endgame state
1214
+ try {
1215
+ endgameState = readEndgameState(cwd);
1216
+ } catch {
1217
+ // Silently fail - show unset if reading fails
1218
+ }
1219
+
1089
1220
  // Count journal entries today
1090
1221
  const today = new Date();
1091
1222
  const year = today.getFullYear();
@@ -1139,6 +1270,11 @@ function showWelcomeVisualization() {
1139
1270
  console.log(` │ ⏳ Review: ${reviewText.padEnd(26)}│`);
1140
1271
  }
1141
1272
  console.log(` │ 📝 Journal: ${(journalEntries + ' entries today').padEnd(26)}│`);
1273
+ if (endgameState.slug !== 'unset' && endgameState.horizon) {
1274
+ const endgameLine = endgameState.slug + ' — ' + endgameState.horizon;
1275
+ const paddedEndgame = endgameLine.padEnd(26);
1276
+ console.log(` │ 🎯 Endgame: ${paddedEndgame}│`);
1277
+ }
1142
1278
  console.log(' │ │');
1143
1279
  console.log(' │ ┌──────────────────────────────────┐ │');
1144
1280
  console.log(' │ │ MAP.md ←──── YOU ARE HERE │ │');
@@ -1185,6 +1321,11 @@ if (command === 'init') {
1185
1321
  Promise.resolve(require('../commands/mission').missionCommand(process.argv.slice(3)))
1186
1322
  .then(() => process.exit(0))
1187
1323
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1324
+ } else if (command === 'pulse') {
1325
+ // Pulse: durable overnight self-improvement heartbeat (OS cron) for atris-cli.
1326
+ Promise.resolve(require('../commands/pulse').pulseCommand(process.argv.slice(3)))
1327
+ .then((res) => process.exit(res && res.ok === false ? 1 : 0))
1328
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1188
1329
  } else if (command === 'probe') {
1189
1330
  // Chat-lane probe (TRR-22): one real /atris2/turn over the full tool relay.
1190
1331
  Promise.resolve(require('../commands/probe').probeCommand(process.argv.slice(3)))
@@ -1203,6 +1344,26 @@ if (command === 'init') {
1203
1344
  Promise.resolve(require('../commands/codex-goal').codexGoalCommand(process.argv.slice(3)))
1204
1345
  .then(() => process.exit(process.exitCode || 0))
1205
1346
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1347
+ } else if (command === 'slop') {
1348
+ // Slop: deterministic frontend-slop detector (no LLM). Exit 1 = slop found, for CI + the autopilot gate.
1349
+ Promise.resolve(require('../commands/slop').slopCommand(process.argv.slice(3)))
1350
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1351
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1352
+ } else if (command === 'deck') {
1353
+ // Deck: premium Google Slides from a plain content spec, via the Atris deck engine (anti-slop design system).
1354
+ Promise.resolve(require('../commands/deck').run(process.argv.slice(3)))
1355
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1356
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1357
+ } else if (command === 'site') {
1358
+ // Site: beautiful static site from a folder of markdown, in the anti-slop design system.
1359
+ Promise.resolve(require('../commands/site').run(process.argv.slice(3)))
1360
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1361
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1362
+ } else if (command === 'theme') {
1363
+ // Theme: brand themes (.atris/theme.json) for the whole design system (deck/html/site).
1364
+ Promise.resolve(require('../commands/theme').run(process.argv.slice(3)))
1365
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1366
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1206
1367
  } else if (command === 'aeo') {
1207
1368
  // AEO: AI Engine Optimization — credit-metered citation drafting against the customer workspace.
1208
1369
  Promise.resolve(require('../commands/aeo').run(process.argv.slice(3)))
@@ -1303,12 +1464,28 @@ if (command === 'init') {
1303
1464
  }
1304
1465
  upgradeAtris().then(() => process.exit(0)).catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1305
1466
  } else if (command === 'chat') {
1467
+ if (process.argv[3] === 'scan') {
1468
+ try {
1469
+ require('../commands/chat-scan').chatScanCommand(process.argv.slice(4));
1470
+ process.exit(0);
1471
+ } catch (error) {
1472
+ console.error(`✗ Chat scan failed: ${error.message || error}`);
1473
+ process.exit(1);
1474
+ }
1475
+ }
1306
1476
  chatAtris()
1307
1477
  .then(() => process.exit(0))
1308
1478
  .catch((error) => {
1309
1479
  console.error(`✗ Chat failed: ${error.message || error}`);
1310
1480
  process.exit(1);
1311
1481
  });
1482
+ } else if (command === 'fast' || (command === 'ax' && process.argv[3] === 'fast')) {
1483
+ atrisFastChat()
1484
+ .then(() => process.exit(0))
1485
+ .catch((error) => {
1486
+ console.error(`✗ Fast chat failed: ${error.message || error}`);
1487
+ process.exit(1);
1488
+ });
1312
1489
  } else if (command === 'console') {
1313
1490
  consoleCmd();
1314
1491
  } else if (command === 'serve') {
@@ -1395,8 +1572,116 @@ if (command === 'init') {
1395
1572
  require('../commands/visualize').visualizeAtris(process.argv.slice(3))
1396
1573
  .then(() => process.exit(0))
1397
1574
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1575
+ } else if (command === 'youtube') {
1576
+ require('../commands/youtube').youtubeCommand(process.argv.slice(3))
1577
+ .then(() => process.exit(0))
1578
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1398
1579
  } else if (command === 'run') {
1399
1580
  const args = process.argv.slice(3);
1581
+ if (args[0] === 'logs') {
1582
+ // Subcommand: atris run logs [--tail N] [--cat FILE] [--json]
1583
+ const { listRunLogs } = require('../commands/run');
1584
+ const logsArgs = args.slice(1);
1585
+ if (logsArgs.includes('--help') || logsArgs.includes('-h')) {
1586
+ console.log('');
1587
+ console.log('Usage: atris run logs [options]');
1588
+ console.log('');
1589
+ console.log('List and read glass run logs from atris/logs/runs/.');
1590
+ console.log('');
1591
+ console.log('Options:');
1592
+ console.log(' --tail N Show last N lines of each log (default: 5)');
1593
+ console.log(' --cat FILE Print full contents of a specific log file');
1594
+ console.log(' --json Output machine-readable JSON');
1595
+ console.log(' --help Show this help');
1596
+ console.log('');
1597
+ process.exit(0);
1598
+ }
1599
+ listRunLogs(logsArgs);
1600
+ process.exit(0);
1601
+ }
1602
+ if (args[0] === 'prune-logs') {
1603
+ // Subcommand: atris run prune-logs [--keep N] [--dry-run]
1604
+ const { pruneRunLogs } = require('../commands/run');
1605
+ const pruneArgs = args.slice(1);
1606
+ if (pruneArgs.includes('--help') || pruneArgs.includes('-h')) {
1607
+ console.log('');
1608
+ console.log('Usage: atris run prune-logs [options]');
1609
+ console.log('');
1610
+ console.log('Prune old run logs, keeping only the most recent N files.');
1611
+ console.log('');
1612
+ console.log('Options:');
1613
+ console.log(' --keep N Number of recent logs to keep (default: 50)');
1614
+ console.log(' --dry-run Show what would be deleted without deleting');
1615
+ console.log(' --help Show this help');
1616
+ console.log('');
1617
+ process.exit(0);
1618
+ }
1619
+ pruneRunLogs(pruneArgs);
1620
+ process.exit(0);
1621
+ }
1622
+ if (args[0] === 'search') {
1623
+ // Subcommand: atris run search <keyword> [--phase P] [--limit N]
1624
+ const { searchRunLogs } = require('../commands/run');
1625
+ const searchArgs = args.slice(1);
1626
+ if (searchArgs.includes('--help') || searchArgs.includes('-h') || searchArgs.length === 0) {
1627
+ console.log('');
1628
+ console.log('Usage: atris run search <keyword> [options]');
1629
+ console.log('');
1630
+ console.log('Search phase reasoning across all run logs.');
1631
+ console.log('');
1632
+ console.log('Options:');
1633
+ console.log(' --phase P Limit search to a phase (plan, do, review, error)');
1634
+ console.log(' --limit N Max results to show (default: 20)');
1635
+ console.log(' --help Show this help');
1636
+ console.log('');
1637
+ process.exit(0);
1638
+ }
1639
+ searchRunLogs(searchArgs);
1640
+ process.exit(0);
1641
+ }
1642
+ if (args[0] === 'stats') {
1643
+ // Subcommand: atris run stats
1644
+ const { statsRunLogs } = require('../commands/run');
1645
+ statsRunLogs();
1646
+ process.exit(0);
1647
+ }
1648
+ if (args[0] === 'export') {
1649
+ // Subcommand: atris run export [--out FILE]
1650
+ const { exportRunLogs } = require('../commands/run');
1651
+ const exportArgs = args.slice(1);
1652
+ if (exportArgs.includes('--help') || exportArgs.includes('-h')) {
1653
+ console.log('');
1654
+ console.log('Usage: atris run export [options]');
1655
+ console.log('');
1656
+ console.log('Export all run logs as a JSON bundle for backup or transfer.');
1657
+ console.log('');
1658
+ console.log('Options:');
1659
+ console.log(' --out FILE Write to a specific file (default: atris/logs/runs/export.json)');
1660
+ console.log(' --help Show this help');
1661
+ console.log('');
1662
+ process.exit(0);
1663
+ }
1664
+ exportRunLogs(exportArgs);
1665
+ process.exit(0);
1666
+ }
1667
+ if (args[0] === 'diff') {
1668
+ // Subcommand: atris run diff <file1> <file2>
1669
+ const { diffRunLogs } = require('../commands/run');
1670
+ const diffArgs = args.slice(1);
1671
+ if (diffArgs.includes('--help') || diffArgs.includes('-h') || diffArgs.length === 0) {
1672
+ console.log('');
1673
+ console.log('Usage: atris run diff <file1> <file2>');
1674
+ console.log('');
1675
+ console.log('Compare two run logs side by side, showing phase-level differences.');
1676
+ console.log('');
1677
+ console.log('Options:');
1678
+ console.log(' --help Show this help');
1679
+ console.log('');
1680
+ process.exit(0);
1681
+ }
1682
+ diffRunLogs(diffArgs);
1683
+ process.exit(0);
1684
+ }
1400
1685
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1401
1686
  console.log('');
1402
1687
  console.log('Usage: atris run [options]');
@@ -1407,12 +1692,24 @@ if (command === 'init') {
1407
1692
  console.log('Options:');
1408
1693
  console.log(' --cycles=N Max cycles (default: 5)');
1409
1694
  console.log(' --once Single plan→do→review cycle');
1410
- console.log(' --verbose Show claude -p output');
1695
+ console.log(' --verbose Show configured runner output');
1411
1696
  console.log(' --dry-run Preview without executing');
1412
1697
  console.log(' --timeout=N Phase timeout in seconds (default: 600)');
1698
+ console.log(' --runner-bin PATH Runner binary for this run');
1699
+ console.log(' --runner-template CMD Runner command template for this run');
1700
+ console.log(' --runner-model MODEL Runner model for this run');
1701
+ console.log(' --runner-profile NAME Runner profile for this run (e.g. atris-fast)');
1413
1702
  console.log(' --push Auto-push after each cycle (default: true)');
1414
1703
  console.log(' --no-push Skip auto-push after each cycle');
1415
1704
  console.log('');
1705
+ console.log('Subcommands:');
1706
+ console.log(' atris run logs [--tail N] [--cat FILE] [--json] Browse glass run logs');
1707
+ console.log(' atris run prune-logs [--keep N] [--dry-run] Prune old run logs');
1708
+ console.log(' atris run search <keyword> [--phase P] [--limit N] Search run logs');
1709
+ console.log(' atris run stats Show run log stats');
1710
+ console.log(' atris run export [--out FILE] Export logs as JSON');
1711
+ console.log(' atris run diff <file1> <file2> Compare two run logs');
1712
+ console.log('');
1416
1713
  process.exit(0);
1417
1714
  }
1418
1715
 
@@ -1420,6 +1717,7 @@ if (command === 'init') {
1420
1717
  const dryRun = args.includes('--dry-run');
1421
1718
  const once = args.includes('--once');
1422
1719
  const push = !args.includes('--no-push');
1720
+ applyRunnerFlags(args);
1423
1721
  const cyclesArg = args.find(a => a.startsWith('--cycles='));
1424
1722
  const maxCycles = cyclesArg ? parseInt(cyclesArg.split('=')[1]) : 5;
1425
1723
  const timeoutArg = args.find(a => a.startsWith('--timeout='));
@@ -1442,13 +1740,14 @@ if (command === 'init') {
1442
1740
  const verbose = args.includes('--verbose') || args.includes('-v');
1443
1741
  const dryRun = args.includes('--dry-run');
1444
1742
  const auto = args.includes('--auto');
1743
+ applyRunnerFlags(args);
1445
1744
  const maxIterationsArg = args.find(a => a.startsWith('--iterations='));
1446
1745
  const maxIterations = maxIterationsArg ? parseInt(maxIterationsArg.split('=')[1]) : undefined;
1447
1746
  const durationArg = args.find(a => a.startsWith('--duration='));
1448
1747
  const duration = durationArg ? durationArg.split('=')[1] : null;
1449
1748
 
1450
1749
  // Get description (non-flag args)
1451
- const description = args.filter(a => !a.startsWith('-')).join(' ').trim() || null;
1750
+ const description = args.filter((a, i) => !a.startsWith('-') && !isOptionValue(args, i, RUNNER_FLAG_NAMES)).join(' ').trim() || null;
1452
1751
 
1453
1752
  const options = {
1454
1753
  ...(maxIterations !== undefined && { maxIterations }),
@@ -1737,6 +2036,11 @@ if (command === 'init') {
1737
2036
  require('../commands/fleet').fleet(args)
1738
2037
  .then(() => process.exit(0))
1739
2038
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2039
+ } else if (command === 'spaceship') {
2040
+ const args = process.argv.slice(3);
2041
+ require('../commands/spaceship').spaceship(args)
2042
+ .then(() => process.exit(0))
2043
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1740
2044
  } else if (command === 'code-review' || command === 'cr') {
1741
2045
  const args = process.argv.slice(3);
1742
2046
  require('../commands/review').reviewCommand(...args)
@@ -1949,7 +2253,7 @@ function inspectAgentCliWiring() {
1949
2253
  },
1950
2254
  ];
1951
2255
 
1952
- const binaries = ['atris', 'claude', 'codex', 'cursor-agent', 'devin'].map((name) => ({
2256
+ const binaries = ['atris', 'ax', 'claude', 'codex', 'cursor-agent', 'devin', 'droid'].map((name) => ({
1953
2257
  name,
1954
2258
  path: commandOnPath(name),
1955
2259
  }));
@@ -1986,10 +2290,13 @@ async function agentAtris() {
1986
2290
  // Respect -h / --help / help before any auth/state work
1987
2291
  const firstArg = process.argv[3];
1988
2292
  if (firstArg === '-h' || firstArg === '--help' || firstArg === 'help') {
1989
- console.log('Usage: atris agent [doctor]');
2293
+ console.log('Usage: atris agent [doctor|dogfood|spawn|spawns|spawn-status]');
1990
2294
  console.log('');
1991
2295
  console.log(' Pick which cloud agent to chat with from this workspace.');
2296
+ console.log(' Run `atris agent spawn <role> --task "..."` to create a worker request.');
2297
+ console.log(' Run `atris agent spawns` to list worker requests.');
1992
2298
  console.log(' Run `atris agent doctor` to verify local AI CLIs can see Atris context.');
2299
+ console.log(' Run `atris agent dogfood --live` to smoke-test Devin/Droid with GLM 5.2.');
1993
2300
  console.log(' Requires `atris login` first.');
1994
2301
  console.log('');
1995
2302
  console.log(' After selecting, use: atris chat ["message"]');
@@ -1999,6 +2306,22 @@ async function agentAtris() {
1999
2306
  if (firstArg === 'doctor') {
2000
2307
  agentDoctor();
2001
2308
  }
2309
+ if (firstArg === 'dogfood') {
2310
+ const result = require('../commands/agent-spawn').agentDogfoodCommand(process.argv.slice(4));
2311
+ process.exit(result.ok ? 0 : 1);
2312
+ }
2313
+ if (firstArg === 'spawn') {
2314
+ require('../commands/agent-spawn').agentSpawnCommand(process.argv.slice(4));
2315
+ return;
2316
+ }
2317
+ if (firstArg === 'spawns' || firstArg === 'spawn-list' || firstArg === 'list-spawns') {
2318
+ require('../commands/agent-spawn').agentSpawnListCommand(process.argv.slice(4));
2319
+ return;
2320
+ }
2321
+ if (firstArg === 'spawn-status' || firstArg === 'spawn-show') {
2322
+ require('../commands/agent-spawn').agentSpawnStatusCommand(process.argv.slice(4));
2323
+ return;
2324
+ }
2002
2325
 
2003
2326
  const targetDir = path.join(process.cwd(), 'atris');
2004
2327
 
@@ -2242,6 +2565,65 @@ async function chatInteractive(config, credentials) {
2242
2565
  });
2243
2566
  }
2244
2567
 
2568
+ function atrisFastMessageFromArgs() {
2569
+ const offset = command === 'ax' ? 4 : 3;
2570
+ return process.argv.slice(offset).join(' ').trim();
2571
+ }
2572
+
2573
+ async function atrisFastChat() {
2574
+ if (command === 'ax' && process.argv[3] !== 'fast') {
2575
+ console.error('Usage: atris ax fast "message"');
2576
+ process.exit(1);
2577
+ }
2578
+
2579
+ const message = atrisFastMessageFromArgs();
2580
+
2581
+ if (message === '-h' || message === '--help' || message === 'help') {
2582
+ console.log('Usage: atris fast ["message"]');
2583
+ console.log('');
2584
+ console.log(' Chat with Atris2 Fast through /api/atris2/turn.');
2585
+ console.log(' Requires `atris login`.');
2586
+ console.log('');
2587
+ console.log(' atris fast "what now?" One-shot message');
2588
+ console.log(' atris ax fast "what now?" Alias');
2589
+ process.exit(0);
2590
+ }
2591
+
2592
+ if (!message) {
2593
+ console.error('Usage: atris fast "message"');
2594
+ process.exit(1);
2595
+ }
2596
+
2597
+ const ensured = await ensureValidCredentials();
2598
+ if (ensured.error === 'not_logged_in' || !ensured.credentials?.token) {
2599
+ console.error('✗ Error: Not logged in. Run "atris login" first.');
2600
+ process.exit(1);
2601
+ }
2602
+ if (ensured.error) {
2603
+ console.error(`✗ Error: Authentication failed: ${ensured.detail || ensured.error}. Run "atris login" to re-authenticate.`);
2604
+ process.exit(1);
2605
+ }
2606
+
2607
+ const credentials = ensured.credentials;
2608
+ await atrisFastOnce(credentials, message);
2609
+ }
2610
+
2611
+ async function atrisFastOnce(credentials, message) {
2612
+ console.log('\nAtris2 Fast');
2613
+ console.log('');
2614
+
2615
+ const apiUrl = getApiBaseUrl().replace(/\/api$/, '');
2616
+ const endpoint = `${apiUrl}/api/atris2/turn`;
2617
+ const body = JSON.stringify({
2618
+ message,
2619
+ model: 'atris:fast',
2620
+ max_turns: 1,
2621
+ });
2622
+
2623
+ await streamProChat(endpoint, credentials.token, body);
2624
+ console.log('\n\n✓ Complete\n');
2625
+ }
2626
+
2245
2627
  async function atrisDevEntry(userInput = null) {
2246
2628
  // Load workspace context and present planning-ready state
2247
2629
  // userInput: optional task description for hot start