atris 3.16.1 → 3.17.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 (58) 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 +400 -30
  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 +135 -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 +55 -25
  34. package/commands/run.js +615 -22
  35. package/commands/slop.js +173 -0
  36. package/commands/spaceship.js +39 -0
  37. package/commands/sync.js +0 -2
  38. package/commands/task.js +429 -37
  39. package/commands/verify.js +7 -3
  40. package/lib/activity-stream.js +166 -0
  41. package/lib/auto-accept-certified.js +23 -1
  42. package/lib/context-gatherer.js +170 -0
  43. package/lib/escape-regexp.js +13 -0
  44. package/lib/file-ops.js +6 -3
  45. package/lib/journal.js +1 -1
  46. package/lib/lesson-contradiction.js +113 -0
  47. package/lib/policy-lessons.js +3 -2
  48. package/lib/pulse.js +401 -0
  49. package/lib/runner-command.js +156 -0
  50. package/lib/slides-deck.js +236 -0
  51. package/lib/state-detection.js +1 -4
  52. package/lib/task-db.js +101 -4
  53. package/lib/task-proof.js +1 -1
  54. package/lib/todo-fallback.js +2 -1
  55. package/lib/todo-sections.js +33 -0
  56. package/package.json +1 -2
  57. package/utils/api.js +14 -2
  58. 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,13 @@ 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');
322
366
  console.log(' run - Auto-chain plan→do→review (autonomous loop, auto-pushes)');
367
+ console.log(' run logs - Browse glass run logs (phase reasoning persisted to disk)');
368
+ console.log(' run search - Search phase reasoning across all run logs');
369
+ console.log(' pulse - Durable overnight self-improvement heartbeat (OS cron, install/status/tick)');
370
+ console.log(' spaceship - Bounded overnight runner that survives bad ticks and emails updates');
323
371
  console.log('');
324
372
  console.log('Context & tracking:');
325
373
  console.log(' log - Add ideas to inbox');
@@ -351,6 +399,7 @@ function showHelp() {
351
399
  console.log(' improve - Run one paid RL tick (POST /api/improve, deducts credits)');
352
400
  console.log(' worktree - Isolated Git worktrees plus guarded ship/merge for parallel agents');
353
401
  console.log(' visualize - Generate a Slack/deck-ready visual from a prompt');
402
+ console.log(' youtube - Process YouTube videos with Gemini native video analysis');
354
403
  console.log('');
355
404
  console.log('Experiments:');
356
405
  console.log(' experiments init [slug] - Prepare atris/experiments/ or scaffold a pack');
@@ -407,8 +456,9 @@ function showHelp() {
407
456
  console.log(' console - Start/attach always-on coding console (tmux daemon)');
408
457
  console.log(' soul - Show, snapshot, or fork workspace identity');
409
458
  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');
459
+ console.log(' agent - Select cloud agent, spawn worker requests, or run `agent doctor`');
460
+ console.log(' chat - Chat with the selected Atris agent (or: atris chat scan)');
461
+ console.log(' fast - Chat with Atris2 Fast');
412
462
  console.log(' login - Sign in or add another account');
413
463
  console.log(' logout - Sign out of current account');
414
464
  console.log(' whoami - Show active account');
@@ -733,8 +783,12 @@ function showAutopilotHelp() {
733
783
  console.log(' --auto Execute without waiting for approval');
734
784
  console.log(' --duration=TIME Run for a time limit (e.g. 1h, 30m, 90m)');
735
785
  console.log(' --iterations=N Max tasks before stopping');
736
- console.log(' --verbose, -v Show detailed claude output');
786
+ console.log(' --verbose, -v Show detailed runner output');
737
787
  console.log(' --dry-run Show suggestions without executing');
788
+ console.log(' --runner-bin PATH Runner binary for this run');
789
+ console.log(' --runner-template CMD Runner command template for this run');
790
+ console.log(' --runner-model MODEL Runner model for this run');
791
+ console.log(' --runner-profile NAME Runner profile for this run (e.g. atris-fast)');
738
792
  console.log('');
739
793
  console.log('Examples:');
740
794
  console.log(' atris autopilot # Suggest from existing work');
@@ -772,11 +826,11 @@ if (command === '2' && ['fast', 'pro'].includes(String(firstCommandArg || '').to
772
826
 
773
827
  // Check if this is a known command or natural language input
774
828
  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',
829
+ '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',
830
+ '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',
831
+ 'ingest', 'query', 'lint', 'loop', 'pulse', 'task', 'mission', 'probe', 'worktree', 'aeo', 'slop', 'deck', 'improve', 'xp', 'play', 'gm', 'x', 'recap',
778
832
  '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'];
833
+ 'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'compile', 'spaceship'];
780
834
 
781
835
  // Check if command is an atris.md spec file - triggers welcome visualization
782
836
  function isSpecFile(cmd) {
@@ -865,11 +919,24 @@ if (!command || !knownCommands.includes(command)) {
865
919
  return;
866
920
  }
867
921
 
922
+ function printAtrisOverview() {
923
+ console.log('');
924
+ console.log('Atris is an AI computer for a workspace.');
925
+ console.log('It keeps project context, tasks, memory, tools, and proof in one loop: plan -> do -> review.');
926
+ console.log('Run `atris` to load the current workspace, or `atris help` to see commands.');
927
+ console.log('');
928
+ }
929
+
868
930
  async function interactiveEntry(userInput) {
869
931
  const workspaceDir = process.cwd();
870
932
  const state = detectWorkspaceState(workspaceDir);
871
933
  const context = loadContext(workspaceDir);
872
934
 
935
+ if (isAtrisMetaQuestion(userInput)) {
936
+ printAtrisOverview();
937
+ return;
938
+ }
939
+
873
940
  // Fresh install - offer init
874
941
  if (state.state === 'fresh') {
875
942
  console.log('\nNo atris/ folder found. Run: atris init');
@@ -935,28 +1002,46 @@ async function interactiveEntry(userInput) {
935
1002
  console.log('└─────────────────────────────────────────────────────────────┘');
936
1003
 
937
1004
  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 {
1005
+ if (shouldGatherContext({
1006
+ root: workspaceDir,
1007
+ userInput,
1008
+ mapStatus,
1009
+ liveMissionsCount,
1010
+ wipCount,
1011
+ backlogCount,
1012
+ inboxCount,
1013
+ })) {
1014
+ const answer = String(userInput || '').trim() || await askContextGatherer(workspaceDir);
1015
+ if (isAtrisMetaQuestion(answer)) {
1016
+ printAtrisOverview();
1017
+ return;
1018
+ }
1019
+ if (!answer.trim()) {
955
1020
  console.log('');
956
- console.log('Then rerun: atris');
1021
+ console.log('No problem. When you are ready, answer in normal words.');
1022
+ console.log('Example: "help me organize college applications" or "help me build a small website".');
1023
+ return;
957
1024
  }
958
- console.log('─────────────────────────────────────────────────────────────');
1025
+ const profile = saveContextProfile(workspaceDir, answer, { source: userInput ? 'hot_start' : 'cold_start' });
1026
+ const starter = createStarterTask(workspaceDir, answer);
959
1027
  console.log('');
1028
+ console.log('Got it. I saved your first direction.');
1029
+ console.log(`Focus: ${profile.first_answer}`);
1030
+ if (starter && starter.display_id) {
1031
+ console.log(`First task: ${starter.display_id} — ${starter.title}`);
1032
+ } else if (starter && starter.title) {
1033
+ console.log(`First task: ${starter.title}`);
1034
+ }
1035
+ if (mapStatus !== 'ready') {
1036
+ printMapBootstrap({ userInput: answer, prefix: 'Next setup step' });
1037
+ return;
1038
+ }
1039
+ await planCmd(answer);
1040
+ return;
1041
+ }
1042
+
1043
+ if (mapStatus !== 'ready') {
1044
+ printMapBootstrap({ userInput });
960
1045
  return;
961
1046
  }
962
1047
 
@@ -1047,9 +1132,45 @@ async function interactiveEntry(userInput) {
1047
1132
  await planCmd(request);
1048
1133
  }
1049
1134
 
1135
+ async function askContextGatherer(workspaceDir) {
1136
+ console.log(renderContextGathererPrompt({ projectName: path.basename(workspaceDir) }));
1137
+ const rl = readline.createInterface({
1138
+ input: process.stdin,
1139
+ output: process.stdout
1140
+ });
1141
+ const answer = await new Promise(r => rl.question('> ', r));
1142
+ rl.close();
1143
+ return answer;
1144
+ }
1145
+
1146
+ function printMapBootstrap({ userInput, prefix = 'Bootstrap required' } = {}) {
1147
+ console.log('');
1148
+ console.log('┌─────────────────────────────────────────────────────────────┐');
1149
+ console.log(`│ ${String(prefix).toUpperCase().padEnd(60)}│`);
1150
+ console.log('└─────────────────────────────────────────────────────────────┘');
1151
+ console.log('');
1152
+ console.log('Atris needs a real `atris/MAP.md` so future steps are grounded in the workspace.');
1153
+ console.log('');
1154
+ console.log('For an agent:');
1155
+ console.log('─────────────────────────────────────────────────────────────');
1156
+ console.log('Read `atris/atris.md`, then generate a complete `atris/MAP.md` for this repo.');
1157
+ console.log('Rules: include file:line refs, keep it grep-friendly, do NOT change code.');
1158
+ if (userInput) {
1159
+ console.log('');
1160
+ console.log('After MAP is generated, continue with:');
1161
+ console.log(`- ${userInput}`);
1162
+ } else {
1163
+ console.log('');
1164
+ console.log('Then rerun: atris');
1165
+ }
1166
+ console.log('─────────────────────────────────────────────────────────────');
1167
+ console.log('');
1168
+ }
1169
+
1050
1170
  // ASCII Welcome Visualization
1051
1171
  function showWelcomeVisualization() {
1052
1172
  const { getTaskCounts } = require('../lib/state-detection');
1173
+ const { readEndgameState } = require('../commands/autopilot');
1053
1174
  const cwd = process.cwd();
1054
1175
  const atrisDir = path.join(cwd, 'atris');
1055
1176
  const projectName = path.basename(cwd);
@@ -1063,6 +1184,7 @@ function showWelcomeVisualization() {
1063
1184
  let journalEntries = 0;
1064
1185
  let hasMap = false;
1065
1186
  let isInitialized = fs.existsSync(atrisDir);
1187
+ let endgameState = { slug: 'unset', horizon: '' };
1066
1188
 
1067
1189
  if (isInitialized) {
1068
1190
  // Check MAP.md
@@ -1086,6 +1208,13 @@ function showWelcomeVisualization() {
1086
1208
  // Silently fail - show 0 tasks if reading fails
1087
1209
  }
1088
1210
 
1211
+ // Read endgame state
1212
+ try {
1213
+ endgameState = readEndgameState(cwd);
1214
+ } catch {
1215
+ // Silently fail - show unset if reading fails
1216
+ }
1217
+
1089
1218
  // Count journal entries today
1090
1219
  const today = new Date();
1091
1220
  const year = today.getFullYear();
@@ -1139,6 +1268,11 @@ function showWelcomeVisualization() {
1139
1268
  console.log(` │ ⏳ Review: ${reviewText.padEnd(26)}│`);
1140
1269
  }
1141
1270
  console.log(` │ 📝 Journal: ${(journalEntries + ' entries today').padEnd(26)}│`);
1271
+ if (endgameState.slug !== 'unset' && endgameState.horizon) {
1272
+ const endgameLine = endgameState.slug + ' — ' + endgameState.horizon;
1273
+ const paddedEndgame = endgameLine.padEnd(26);
1274
+ console.log(` │ 🎯 Endgame: ${paddedEndgame}│`);
1275
+ }
1142
1276
  console.log(' │ │');
1143
1277
  console.log(' │ ┌──────────────────────────────────┐ │');
1144
1278
  console.log(' │ │ MAP.md ←──── YOU ARE HERE │ │');
@@ -1185,6 +1319,11 @@ if (command === 'init') {
1185
1319
  Promise.resolve(require('../commands/mission').missionCommand(process.argv.slice(3)))
1186
1320
  .then(() => process.exit(0))
1187
1321
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1322
+ } else if (command === 'pulse') {
1323
+ // Pulse: durable overnight self-improvement heartbeat (OS cron) for atris-cli.
1324
+ Promise.resolve(require('../commands/pulse').pulseCommand(process.argv.slice(3)))
1325
+ .then((res) => process.exit(res && res.ok === false ? 1 : 0))
1326
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1188
1327
  } else if (command === 'probe') {
1189
1328
  // Chat-lane probe (TRR-22): one real /atris2/turn over the full tool relay.
1190
1329
  Promise.resolve(require('../commands/probe').probeCommand(process.argv.slice(3)))
@@ -1203,6 +1342,16 @@ if (command === 'init') {
1203
1342
  Promise.resolve(require('../commands/codex-goal').codexGoalCommand(process.argv.slice(3)))
1204
1343
  .then(() => process.exit(process.exitCode || 0))
1205
1344
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1345
+ } else if (command === 'slop') {
1346
+ // Slop: deterministic frontend-slop detector (no LLM). Exit 1 = slop found, for CI + the autopilot gate.
1347
+ Promise.resolve(require('../commands/slop').slopCommand(process.argv.slice(3)))
1348
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1349
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1350
+ } else if (command === 'deck') {
1351
+ // Deck: premium Google Slides from a plain content spec, via the Atris deck engine (anti-slop design system).
1352
+ Promise.resolve(require('../commands/deck').run(process.argv.slice(3)))
1353
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
1354
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1206
1355
  } else if (command === 'aeo') {
1207
1356
  // AEO: AI Engine Optimization — credit-metered citation drafting against the customer workspace.
1208
1357
  Promise.resolve(require('../commands/aeo').run(process.argv.slice(3)))
@@ -1303,12 +1452,28 @@ if (command === 'init') {
1303
1452
  }
1304
1453
  upgradeAtris().then(() => process.exit(0)).catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1305
1454
  } else if (command === 'chat') {
1455
+ if (process.argv[3] === 'scan') {
1456
+ try {
1457
+ require('../commands/chat-scan').chatScanCommand(process.argv.slice(4));
1458
+ process.exit(0);
1459
+ } catch (error) {
1460
+ console.error(`✗ Chat scan failed: ${error.message || error}`);
1461
+ process.exit(1);
1462
+ }
1463
+ }
1306
1464
  chatAtris()
1307
1465
  .then(() => process.exit(0))
1308
1466
  .catch((error) => {
1309
1467
  console.error(`✗ Chat failed: ${error.message || error}`);
1310
1468
  process.exit(1);
1311
1469
  });
1470
+ } else if (command === 'fast' || (command === 'ax' && process.argv[3] === 'fast')) {
1471
+ atrisFastChat()
1472
+ .then(() => process.exit(0))
1473
+ .catch((error) => {
1474
+ console.error(`✗ Fast chat failed: ${error.message || error}`);
1475
+ process.exit(1);
1476
+ });
1312
1477
  } else if (command === 'console') {
1313
1478
  consoleCmd();
1314
1479
  } else if (command === 'serve') {
@@ -1395,8 +1560,116 @@ if (command === 'init') {
1395
1560
  require('../commands/visualize').visualizeAtris(process.argv.slice(3))
1396
1561
  .then(() => process.exit(0))
1397
1562
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1563
+ } else if (command === 'youtube') {
1564
+ require('../commands/youtube').youtubeCommand(process.argv.slice(3))
1565
+ .then(() => process.exit(0))
1566
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1398
1567
  } else if (command === 'run') {
1399
1568
  const args = process.argv.slice(3);
1569
+ if (args[0] === 'logs') {
1570
+ // Subcommand: atris run logs [--tail N] [--cat FILE] [--json]
1571
+ const { listRunLogs } = require('../commands/run');
1572
+ const logsArgs = args.slice(1);
1573
+ if (logsArgs.includes('--help') || logsArgs.includes('-h')) {
1574
+ console.log('');
1575
+ console.log('Usage: atris run logs [options]');
1576
+ console.log('');
1577
+ console.log('List and read glass run logs from atris/logs/runs/.');
1578
+ console.log('');
1579
+ console.log('Options:');
1580
+ console.log(' --tail N Show last N lines of each log (default: 5)');
1581
+ console.log(' --cat FILE Print full contents of a specific log file');
1582
+ console.log(' --json Output machine-readable JSON');
1583
+ console.log(' --help Show this help');
1584
+ console.log('');
1585
+ process.exit(0);
1586
+ }
1587
+ listRunLogs(logsArgs);
1588
+ process.exit(0);
1589
+ }
1590
+ if (args[0] === 'prune-logs') {
1591
+ // Subcommand: atris run prune-logs [--keep N] [--dry-run]
1592
+ const { pruneRunLogs } = require('../commands/run');
1593
+ const pruneArgs = args.slice(1);
1594
+ if (pruneArgs.includes('--help') || pruneArgs.includes('-h')) {
1595
+ console.log('');
1596
+ console.log('Usage: atris run prune-logs [options]');
1597
+ console.log('');
1598
+ console.log('Prune old run logs, keeping only the most recent N files.');
1599
+ console.log('');
1600
+ console.log('Options:');
1601
+ console.log(' --keep N Number of recent logs to keep (default: 50)');
1602
+ console.log(' --dry-run Show what would be deleted without deleting');
1603
+ console.log(' --help Show this help');
1604
+ console.log('');
1605
+ process.exit(0);
1606
+ }
1607
+ pruneRunLogs(pruneArgs);
1608
+ process.exit(0);
1609
+ }
1610
+ if (args[0] === 'search') {
1611
+ // Subcommand: atris run search <keyword> [--phase P] [--limit N]
1612
+ const { searchRunLogs } = require('../commands/run');
1613
+ const searchArgs = args.slice(1);
1614
+ if (searchArgs.includes('--help') || searchArgs.includes('-h') || searchArgs.length === 0) {
1615
+ console.log('');
1616
+ console.log('Usage: atris run search <keyword> [options]');
1617
+ console.log('');
1618
+ console.log('Search phase reasoning across all run logs.');
1619
+ console.log('');
1620
+ console.log('Options:');
1621
+ console.log(' --phase P Limit search to a phase (plan, do, review, error)');
1622
+ console.log(' --limit N Max results to show (default: 20)');
1623
+ console.log(' --help Show this help');
1624
+ console.log('');
1625
+ process.exit(0);
1626
+ }
1627
+ searchRunLogs(searchArgs);
1628
+ process.exit(0);
1629
+ }
1630
+ if (args[0] === 'stats') {
1631
+ // Subcommand: atris run stats
1632
+ const { statsRunLogs } = require('../commands/run');
1633
+ statsRunLogs();
1634
+ process.exit(0);
1635
+ }
1636
+ if (args[0] === 'export') {
1637
+ // Subcommand: atris run export [--out FILE]
1638
+ const { exportRunLogs } = require('../commands/run');
1639
+ const exportArgs = args.slice(1);
1640
+ if (exportArgs.includes('--help') || exportArgs.includes('-h')) {
1641
+ console.log('');
1642
+ console.log('Usage: atris run export [options]');
1643
+ console.log('');
1644
+ console.log('Export all run logs as a JSON bundle for backup or transfer.');
1645
+ console.log('');
1646
+ console.log('Options:');
1647
+ console.log(' --out FILE Write to a specific file (default: atris/logs/runs/export.json)');
1648
+ console.log(' --help Show this help');
1649
+ console.log('');
1650
+ process.exit(0);
1651
+ }
1652
+ exportRunLogs(exportArgs);
1653
+ process.exit(0);
1654
+ }
1655
+ if (args[0] === 'diff') {
1656
+ // Subcommand: atris run diff <file1> <file2>
1657
+ const { diffRunLogs } = require('../commands/run');
1658
+ const diffArgs = args.slice(1);
1659
+ if (diffArgs.includes('--help') || diffArgs.includes('-h') || diffArgs.length === 0) {
1660
+ console.log('');
1661
+ console.log('Usage: atris run diff <file1> <file2>');
1662
+ console.log('');
1663
+ console.log('Compare two run logs side by side, showing phase-level differences.');
1664
+ console.log('');
1665
+ console.log('Options:');
1666
+ console.log(' --help Show this help');
1667
+ console.log('');
1668
+ process.exit(0);
1669
+ }
1670
+ diffRunLogs(diffArgs);
1671
+ process.exit(0);
1672
+ }
1400
1673
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1401
1674
  console.log('');
1402
1675
  console.log('Usage: atris run [options]');
@@ -1407,12 +1680,24 @@ if (command === 'init') {
1407
1680
  console.log('Options:');
1408
1681
  console.log(' --cycles=N Max cycles (default: 5)');
1409
1682
  console.log(' --once Single plan→do→review cycle');
1410
- console.log(' --verbose Show claude -p output');
1683
+ console.log(' --verbose Show configured runner output');
1411
1684
  console.log(' --dry-run Preview without executing');
1412
1685
  console.log(' --timeout=N Phase timeout in seconds (default: 600)');
1686
+ console.log(' --runner-bin PATH Runner binary for this run');
1687
+ console.log(' --runner-template CMD Runner command template for this run');
1688
+ console.log(' --runner-model MODEL Runner model for this run');
1689
+ console.log(' --runner-profile NAME Runner profile for this run (e.g. atris-fast)');
1413
1690
  console.log(' --push Auto-push after each cycle (default: true)');
1414
1691
  console.log(' --no-push Skip auto-push after each cycle');
1415
1692
  console.log('');
1693
+ console.log('Subcommands:');
1694
+ console.log(' atris run logs [--tail N] [--cat FILE] [--json] Browse glass run logs');
1695
+ console.log(' atris run prune-logs [--keep N] [--dry-run] Prune old run logs');
1696
+ console.log(' atris run search <keyword> [--phase P] [--limit N] Search run logs');
1697
+ console.log(' atris run stats Show run log stats');
1698
+ console.log(' atris run export [--out FILE] Export logs as JSON');
1699
+ console.log(' atris run diff <file1> <file2> Compare two run logs');
1700
+ console.log('');
1416
1701
  process.exit(0);
1417
1702
  }
1418
1703
 
@@ -1420,6 +1705,7 @@ if (command === 'init') {
1420
1705
  const dryRun = args.includes('--dry-run');
1421
1706
  const once = args.includes('--once');
1422
1707
  const push = !args.includes('--no-push');
1708
+ applyRunnerFlags(args);
1423
1709
  const cyclesArg = args.find(a => a.startsWith('--cycles='));
1424
1710
  const maxCycles = cyclesArg ? parseInt(cyclesArg.split('=')[1]) : 5;
1425
1711
  const timeoutArg = args.find(a => a.startsWith('--timeout='));
@@ -1442,13 +1728,14 @@ if (command === 'init') {
1442
1728
  const verbose = args.includes('--verbose') || args.includes('-v');
1443
1729
  const dryRun = args.includes('--dry-run');
1444
1730
  const auto = args.includes('--auto');
1731
+ applyRunnerFlags(args);
1445
1732
  const maxIterationsArg = args.find(a => a.startsWith('--iterations='));
1446
1733
  const maxIterations = maxIterationsArg ? parseInt(maxIterationsArg.split('=')[1]) : undefined;
1447
1734
  const durationArg = args.find(a => a.startsWith('--duration='));
1448
1735
  const duration = durationArg ? durationArg.split('=')[1] : null;
1449
1736
 
1450
1737
  // Get description (non-flag args)
1451
- const description = args.filter(a => !a.startsWith('-')).join(' ').trim() || null;
1738
+ const description = args.filter((a, i) => !a.startsWith('-') && !isOptionValue(args, i, RUNNER_FLAG_NAMES)).join(' ').trim() || null;
1452
1739
 
1453
1740
  const options = {
1454
1741
  ...(maxIterations !== undefined && { maxIterations }),
@@ -1737,6 +2024,11 @@ if (command === 'init') {
1737
2024
  require('../commands/fleet').fleet(args)
1738
2025
  .then(() => process.exit(0))
1739
2026
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
2027
+ } else if (command === 'spaceship') {
2028
+ const args = process.argv.slice(3);
2029
+ require('../commands/spaceship').spaceship(args)
2030
+ .then(() => process.exit(0))
2031
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1740
2032
  } else if (command === 'code-review' || command === 'cr') {
1741
2033
  const args = process.argv.slice(3);
1742
2034
  require('../commands/review').reviewCommand(...args)
@@ -1949,7 +2241,7 @@ function inspectAgentCliWiring() {
1949
2241
  },
1950
2242
  ];
1951
2243
 
1952
- const binaries = ['atris', 'claude', 'codex', 'cursor-agent', 'devin'].map((name) => ({
2244
+ const binaries = ['atris', 'ax', 'claude', 'codex', 'cursor-agent', 'devin', 'droid'].map((name) => ({
1953
2245
  name,
1954
2246
  path: commandOnPath(name),
1955
2247
  }));
@@ -1986,10 +2278,13 @@ async function agentAtris() {
1986
2278
  // Respect -h / --help / help before any auth/state work
1987
2279
  const firstArg = process.argv[3];
1988
2280
  if (firstArg === '-h' || firstArg === '--help' || firstArg === 'help') {
1989
- console.log('Usage: atris agent [doctor]');
2281
+ console.log('Usage: atris agent [doctor|dogfood|spawn|spawns|spawn-status]');
1990
2282
  console.log('');
1991
2283
  console.log(' Pick which cloud agent to chat with from this workspace.');
2284
+ console.log(' Run `atris agent spawn <role> --task "..."` to create a worker request.');
2285
+ console.log(' Run `atris agent spawns` to list worker requests.');
1992
2286
  console.log(' Run `atris agent doctor` to verify local AI CLIs can see Atris context.');
2287
+ console.log(' Run `atris agent dogfood --live` to smoke-test Devin/Droid with GLM 5.2.');
1993
2288
  console.log(' Requires `atris login` first.');
1994
2289
  console.log('');
1995
2290
  console.log(' After selecting, use: atris chat ["message"]');
@@ -1999,6 +2294,22 @@ async function agentAtris() {
1999
2294
  if (firstArg === 'doctor') {
2000
2295
  agentDoctor();
2001
2296
  }
2297
+ if (firstArg === 'dogfood') {
2298
+ const result = require('../commands/agent-spawn').agentDogfoodCommand(process.argv.slice(4));
2299
+ process.exit(result.ok ? 0 : 1);
2300
+ }
2301
+ if (firstArg === 'spawn') {
2302
+ require('../commands/agent-spawn').agentSpawnCommand(process.argv.slice(4));
2303
+ return;
2304
+ }
2305
+ if (firstArg === 'spawns' || firstArg === 'spawn-list' || firstArg === 'list-spawns') {
2306
+ require('../commands/agent-spawn').agentSpawnListCommand(process.argv.slice(4));
2307
+ return;
2308
+ }
2309
+ if (firstArg === 'spawn-status' || firstArg === 'spawn-show') {
2310
+ require('../commands/agent-spawn').agentSpawnStatusCommand(process.argv.slice(4));
2311
+ return;
2312
+ }
2002
2313
 
2003
2314
  const targetDir = path.join(process.cwd(), 'atris');
2004
2315
 
@@ -2242,6 +2553,65 @@ async function chatInteractive(config, credentials) {
2242
2553
  });
2243
2554
  }
2244
2555
 
2556
+ function atrisFastMessageFromArgs() {
2557
+ const offset = command === 'ax' ? 4 : 3;
2558
+ return process.argv.slice(offset).join(' ').trim();
2559
+ }
2560
+
2561
+ async function atrisFastChat() {
2562
+ if (command === 'ax' && process.argv[3] !== 'fast') {
2563
+ console.error('Usage: atris ax fast "message"');
2564
+ process.exit(1);
2565
+ }
2566
+
2567
+ const message = atrisFastMessageFromArgs();
2568
+
2569
+ if (message === '-h' || message === '--help' || message === 'help') {
2570
+ console.log('Usage: atris fast ["message"]');
2571
+ console.log('');
2572
+ console.log(' Chat with Atris2 Fast through /api/atris2/turn.');
2573
+ console.log(' Requires `atris login`.');
2574
+ console.log('');
2575
+ console.log(' atris fast "what now?" One-shot message');
2576
+ console.log(' atris ax fast "what now?" Alias');
2577
+ process.exit(0);
2578
+ }
2579
+
2580
+ if (!message) {
2581
+ console.error('Usage: atris fast "message"');
2582
+ process.exit(1);
2583
+ }
2584
+
2585
+ const ensured = await ensureValidCredentials();
2586
+ if (ensured.error === 'not_logged_in' || !ensured.credentials?.token) {
2587
+ console.error('✗ Error: Not logged in. Run "atris login" first.');
2588
+ process.exit(1);
2589
+ }
2590
+ if (ensured.error) {
2591
+ console.error(`✗ Error: Authentication failed: ${ensured.detail || ensured.error}. Run "atris login" to re-authenticate.`);
2592
+ process.exit(1);
2593
+ }
2594
+
2595
+ const credentials = ensured.credentials;
2596
+ await atrisFastOnce(credentials, message);
2597
+ }
2598
+
2599
+ async function atrisFastOnce(credentials, message) {
2600
+ console.log('\nAtris2 Fast');
2601
+ console.log('');
2602
+
2603
+ const apiUrl = getApiBaseUrl().replace(/\/api$/, '');
2604
+ const endpoint = `${apiUrl}/api/atris2/turn`;
2605
+ const body = JSON.stringify({
2606
+ message,
2607
+ model: 'atris:fast',
2608
+ max_turns: 1,
2609
+ });
2610
+
2611
+ await streamProChat(endpoint, credentials.token, body);
2612
+ console.log('\n\n✓ Complete\n');
2613
+ }
2614
+
2245
2615
  async function atrisDevEntry(userInput = null) {
2246
2616
  // Load workspace context and present planning-ready state
2247
2617
  // userInput: optional task description for hot start