atris 3.16.0 → 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 (59) hide show
  1. package/README.md +33 -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 +446 -43
  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 +466 -20
  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 +574 -0
  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 +444 -0
  31. package/commands/pulse.js +504 -0
  32. package/commands/radar.js +1 -0
  33. package/commands/recap.js +233 -0
  34. package/commands/run.js +615 -22
  35. package/commands/skill.js +6 -2
  36. package/commands/slop.js +173 -0
  37. package/commands/spaceship.js +39 -0
  38. package/commands/sync.js +0 -2
  39. package/commands/task.js +458 -43
  40. package/commands/verify.js +7 -3
  41. package/lib/activity-stream.js +166 -0
  42. package/lib/auto-accept-certified.js +23 -1
  43. package/lib/context-gatherer.js +170 -0
  44. package/lib/escape-regexp.js +13 -0
  45. package/lib/file-ops.js +6 -3
  46. package/lib/journal.js +1 -1
  47. package/lib/lesson-contradiction.js +113 -0
  48. package/lib/policy-lessons.js +3 -2
  49. package/lib/pulse.js +401 -0
  50. package/lib/runner-command.js +156 -0
  51. package/lib/slides-deck.js +236 -0
  52. package/lib/state-detection.js +40 -3
  53. package/lib/task-db.js +101 -4
  54. package/lib/task-proof.js +1 -1
  55. package/lib/todo-fallback.js +2 -1
  56. package/lib/todo-sections.js +33 -0
  57. package/package.json +1 -2
  58. package/utils/api.js +14 -2
  59. 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');
@@ -328,6 +376,7 @@ function showHelp() {
328
376
  console.log(' radar - Show live agents joined with tasks, missions, and worktrees');
329
377
  console.log(' ctop - Show a process-first live agent CPU/memory view');
330
378
  console.log(' status - See local work and completions (`atris status <business>` for remote)');
379
+ console.log(' recap - What your AI team did, in plain English (--share for paste-ready)');
331
380
  console.log(' xp - Show Career XP and contribution graph');
332
381
  console.log(' analytics - Show recent productivity from journals');
333
382
  console.log(' search - Search journal history (atris search <keyword>)');
@@ -350,6 +399,7 @@ function showHelp() {
350
399
  console.log(' improve - Run one paid RL tick (POST /api/improve, deducts credits)');
351
400
  console.log(' worktree - Isolated Git worktrees plus guarded ship/merge for parallel agents');
352
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');
353
403
  console.log('');
354
404
  console.log('Experiments:');
355
405
  console.log(' experiments init [slug] - Prepare atris/experiments/ or scaffold a pack');
@@ -357,6 +407,13 @@ function showHelp() {
357
407
  console.log(' experiments run <slug> - Execute a pack or record an Endstate receipt');
358
408
  console.log(' experiments benchmark [m] - Run validate/runtime experiment benchmarks');
359
409
  console.log('');
410
+ console.log('Compile loop (learn like AI, run like code):');
411
+ console.log(' compile record <name> - Append an execution record (--input/--output)');
412
+ console.log(' compile build <name> - Compile records into a deterministic run.js');
413
+ console.log(' compile backtest <name> - Replay all records, score accuracy vs gate');
414
+ console.log(' compile promote <name> - Activate (gate: accuracy >= threshold)');
415
+ console.log(' compile exec <name> - Run the compiled process token-free');
416
+ console.log('');
360
417
  console.log('Quick commands:');
361
418
  console.log(' atris - Load context and start (natural language)');
362
419
  console.log(' next - Auto-advance to next step');
@@ -399,8 +456,9 @@ function showHelp() {
399
456
  console.log(' console - Start/attach always-on coding console (tmux daemon)');
400
457
  console.log(' soul - Show, snapshot, or fork workspace identity');
401
458
  console.log(' fleet - Inspect local fleet status');
402
- console.log(' agent - Select cloud agent, or run `agent doctor` for local CLI wiring');
403
- 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');
404
462
  console.log(' login - Sign in or add another account');
405
463
  console.log(' logout - Sign out of current account');
406
464
  console.log(' whoami - Show active account');
@@ -725,8 +783,12 @@ function showAutopilotHelp() {
725
783
  console.log(' --auto Execute without waiting for approval');
726
784
  console.log(' --duration=TIME Run for a time limit (e.g. 1h, 30m, 90m)');
727
785
  console.log(' --iterations=N Max tasks before stopping');
728
- console.log(' --verbose, -v Show detailed claude output');
786
+ console.log(' --verbose, -v Show detailed runner output');
729
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)');
730
792
  console.log('');
731
793
  console.log('Examples:');
732
794
  console.log(' atris autopilot # Suggest from existing work');
@@ -764,11 +826,11 @@ if (command === '2' && ['fast', 'pro'].includes(String(firstCommandArg || '').to
764
826
 
765
827
  // Check if this is a known command or natural language input
766
828
  const knownCommands = ['init', 'log', 'now', 'radar', 'ctop', 'status', 'analytics', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', 'plan', 'do', 'review', 'release',
767
- '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',
768
- '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',
769
- 'ingest', 'query', 'lint', 'loop', 'task', 'mission', 'worktree', 'aeo', 'improve', 'xp', 'play', 'gm', 'x',
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',
770
832
  'gmail', 'calendar', 'twitter', 'slack', 'imessage', 'integrations', 'setup', 'clean-workspace', 'cw',
771
- 'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet'];
833
+ 'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'compile', 'spaceship'];
772
834
 
773
835
  // Check if command is an atris.md spec file - triggers welcome visualization
774
836
  function isSpecFile(cmd) {
@@ -857,11 +919,24 @@ if (!command || !knownCommands.includes(command)) {
857
919
  return;
858
920
  }
859
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
+
860
930
  async function interactiveEntry(userInput) {
861
931
  const workspaceDir = process.cwd();
862
932
  const state = detectWorkspaceState(workspaceDir);
863
933
  const context = loadContext(workspaceDir);
864
934
 
935
+ if (isAtrisMetaQuestion(userInput)) {
936
+ printAtrisOverview();
937
+ return;
938
+ }
939
+
865
940
  // Fresh install - offer init
866
941
  if (state.state === 'fresh') {
867
942
  console.log('\nNo atris/ folder found. Run: atris init');
@@ -927,28 +1002,46 @@ async function interactiveEntry(userInput) {
927
1002
  console.log('└─────────────────────────────────────────────────────────────┘');
928
1003
 
929
1004
  const mapStatus = context.mapStatus || (context.mapExists ? 'ready' : 'missing');
930
- if (mapStatus !== 'ready') {
931
- console.log('');
932
- console.log('┌─────────────────────────────────────────────────────────────┐');
933
- console.log('│ BOOTSTRAP REQUIRED │');
934
- console.log('└─────────────────────────────────────────────────────────────┘');
935
- console.log('');
936
- console.log('🗺️ Atris needs a real `atris/MAP.md` (navigation index with file:line refs).');
937
- console.log('');
938
- console.log('Copy/paste into your coding agent:');
939
- console.log('─────────────────────────────────────────────────────────────');
940
- console.log('Read `atris/atris.md`, then generate a complete `atris/MAP.md` for this repo.');
941
- console.log('Rules: include file:line refs, keep it grep-friendly, do NOT change code.');
942
- if (userInput) {
943
- console.log('');
944
- console.log('After MAP is generated, run:');
945
- console.log(`- atris ${userInput}`);
946
- } 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()) {
947
1020
  console.log('');
948
- 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;
949
1024
  }
950
- console.log('─────────────────────────────────────────────────────────────');
1025
+ const profile = saveContextProfile(workspaceDir, answer, { source: userInput ? 'hot_start' : 'cold_start' });
1026
+ const starter = createStarterTask(workspaceDir, answer);
951
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 });
952
1045
  return;
953
1046
  }
954
1047
 
@@ -1039,9 +1132,45 @@ async function interactiveEntry(userInput) {
1039
1132
  await planCmd(request);
1040
1133
  }
1041
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
+
1042
1170
  // ASCII Welcome Visualization
1043
1171
  function showWelcomeVisualization() {
1044
- const { getBacklogTasks, getInProgressTasks } = require('../lib/state-detection');
1172
+ const { getTaskCounts } = require('../lib/state-detection');
1173
+ const { readEndgameState } = require('../commands/autopilot');
1045
1174
  const cwd = process.cwd();
1046
1175
  const atrisDir = path.join(cwd, 'atris');
1047
1176
  const projectName = path.basename(cwd);
@@ -1050,9 +1179,12 @@ function showWelcomeVisualization() {
1050
1179
  let filesIndexed = 0;
1051
1180
  let tasksInBacklog = 0;
1052
1181
  let tasksInProgress = 0;
1182
+ let tasksInReview = 0;
1183
+ let tasksCertified = 0;
1053
1184
  let journalEntries = 0;
1054
1185
  let hasMap = false;
1055
1186
  let isInitialized = fs.existsSync(atrisDir);
1187
+ let endgameState = { slug: 'unset', horizon: '' };
1056
1188
 
1057
1189
  if (isInitialized) {
1058
1190
  // Check MAP.md
@@ -1065,15 +1197,22 @@ function showWelcomeVisualization() {
1065
1197
  filesIndexed = fileRefs ? fileRefs.length : 0;
1066
1198
  }
1067
1199
 
1068
- // Check TODO.md
1069
- const todoPath = path.join(atrisDir, 'TODO.md');
1070
- if (fs.existsSync(todoPath)) {
1071
- try {
1072
- tasksInBacklog = getBacklogTasks(atrisDir).length;
1073
- tasksInProgress = getInProgressTasks(atrisDir).length;
1074
- } catch {
1075
- // Silently fail - show 0 tasks if parsing fails
1076
- }
1200
+ // Task lane counts — DB truth first, TODO.md parse as fallback
1201
+ try {
1202
+ const counts = getTaskCounts(atrisDir);
1203
+ tasksInBacklog = counts.backlog;
1204
+ tasksInProgress = counts.active;
1205
+ tasksInReview = counts.review;
1206
+ tasksCertified = counts.reviewCertified;
1207
+ } catch {
1208
+ // Silently fail - show 0 tasks if reading fails
1209
+ }
1210
+
1211
+ // Read endgame state
1212
+ try {
1213
+ endgameState = readEndgameState(cwd);
1214
+ } catch {
1215
+ // Silently fail - show unset if reading fails
1077
1216
  }
1078
1217
 
1079
1218
  // Count journal entries today
@@ -1122,13 +1261,24 @@ function showWelcomeVisualization() {
1122
1261
  console.log(` │ 📄 Spec: atris.md v${CLI_VERSION.padEnd(18)}│`);
1123
1262
  console.log(` │ 🗺️ Map: ${hasMap ? (filesIndexed + ' files indexed').padEnd(26) : 'not generated yet'.padEnd(26)}│`);
1124
1263
  console.log(` │ 📋 Tasks: ${(tasksInBacklog + ' backlog, ' + tasksInProgress + ' active').padEnd(26)}│`);
1264
+ if (tasksInReview > 0) {
1265
+ const reviewText = tasksCertified > 0
1266
+ ? `${tasksInReview} waiting (${tasksCertified} certified)`
1267
+ : `${tasksInReview} waiting`;
1268
+ console.log(` │ ⏳ Review: ${reviewText.padEnd(26)}│`);
1269
+ }
1125
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
+ }
1126
1276
  console.log(' │ │');
1127
1277
  console.log(' │ ┌──────────────────────────────────┐ │');
1128
1278
  console.log(' │ │ MAP.md ←──── YOU ARE HERE │ │');
1129
1279
  console.log(' │ │ ↓ │ │');
1130
- const taskText = `${tasksInBacklog} tasks waiting`;
1131
- console.log(` │ │ TODO.md ←── ${taskText.padEnd(17)}│ │`);
1280
+ const taskText = `${tasksInBacklog} task${tasksInBacklog === 1 ? '' : 's'} waiting`;
1281
+ console.log(` │ │ TODO.md ←── ${taskText.padEnd(20)}│ │`);
1132
1282
  console.log(' │ │ ↓ │ │');
1133
1283
  console.log(' │ │ navigator → executor → validator│ │');
1134
1284
  console.log(' │ └──────────────────────────────────┘ │');
@@ -1136,7 +1286,11 @@ function showWelcomeVisualization() {
1136
1286
  console.log(' └──────────────────────────────────────────┘');
1137
1287
  }
1138
1288
  console.log('');
1139
- console.log(` Ready. Run 'atris plan' to start.`);
1289
+ if (tasksCertified > 0) {
1290
+ console.log(` Ready. ${tasksCertified} certified await accept — run 'atris task reviews'.`);
1291
+ } else {
1292
+ console.log(` Ready. Run 'atris plan' to start.`);
1293
+ }
1140
1294
  console.log('');
1141
1295
  }
1142
1296
 
@@ -1165,6 +1319,16 @@ if (command === 'init') {
1165
1319
  Promise.resolve(require('../commands/mission').missionCommand(process.argv.slice(3)))
1166
1320
  .then(() => process.exit(0))
1167
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); });
1327
+ } else if (command === 'probe') {
1328
+ // Chat-lane probe (TRR-22): one real /atris2/turn over the full tool relay.
1329
+ Promise.resolve(require('../commands/probe').probeCommand(process.argv.slice(3)))
1330
+ .then((code) => process.exit(code || 0))
1331
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1168
1332
  } else if (command === 'worktree') {
1169
1333
  Promise.resolve(require('../commands/worktree').worktreeCommand(process.argv.slice(3)))
1170
1334
  .then((code) => process.exit(code || 0))
@@ -1178,6 +1342,16 @@ if (command === 'init') {
1178
1342
  Promise.resolve(require('../commands/codex-goal').codexGoalCommand(process.argv.slice(3)))
1179
1343
  .then(() => process.exit(process.exitCode || 0))
1180
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); });
1181
1355
  } else if (command === 'aeo') {
1182
1356
  // AEO: AI Engine Optimization — credit-metered citation drafting against the customer workspace.
1183
1357
  Promise.resolve(require('../commands/aeo').run(process.argv.slice(3)))
@@ -1234,6 +1408,8 @@ if (command === 'init') {
1234
1408
  process.exit(0);
1235
1409
  }
1236
1410
  require('../commands/now').nowAtris(args);
1411
+ } else if (command === 'recap') {
1412
+ require('../commands/recap').recapAtris(process.argv.slice(3));
1237
1413
  } else if (command === 'activate') {
1238
1414
  const args = process.argv.slice(3);
1239
1415
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
@@ -1276,12 +1452,28 @@ if (command === 'init') {
1276
1452
  }
1277
1453
  upgradeAtris().then(() => process.exit(0)).catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1278
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
+ }
1279
1464
  chatAtris()
1280
1465
  .then(() => process.exit(0))
1281
1466
  .catch((error) => {
1282
1467
  console.error(`✗ Chat failed: ${error.message || error}`);
1283
1468
  process.exit(1);
1284
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
+ });
1285
1477
  } else if (command === 'console') {
1286
1478
  consoleCmd();
1287
1479
  } else if (command === 'serve') {
@@ -1368,8 +1560,116 @@ if (command === 'init') {
1368
1560
  require('../commands/visualize').visualizeAtris(process.argv.slice(3))
1369
1561
  .then(() => process.exit(0))
1370
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); });
1371
1567
  } else if (command === 'run') {
1372
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
+ }
1373
1673
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1374
1674
  console.log('');
1375
1675
  console.log('Usage: atris run [options]');
@@ -1380,12 +1680,24 @@ if (command === 'init') {
1380
1680
  console.log('Options:');
1381
1681
  console.log(' --cycles=N Max cycles (default: 5)');
1382
1682
  console.log(' --once Single plan→do→review cycle');
1383
- console.log(' --verbose Show claude -p output');
1683
+ console.log(' --verbose Show configured runner output');
1384
1684
  console.log(' --dry-run Preview without executing');
1385
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)');
1386
1690
  console.log(' --push Auto-push after each cycle (default: true)');
1387
1691
  console.log(' --no-push Skip auto-push after each cycle');
1388
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('');
1389
1701
  process.exit(0);
1390
1702
  }
1391
1703
 
@@ -1393,6 +1705,7 @@ if (command === 'init') {
1393
1705
  const dryRun = args.includes('--dry-run');
1394
1706
  const once = args.includes('--once');
1395
1707
  const push = !args.includes('--no-push');
1708
+ applyRunnerFlags(args);
1396
1709
  const cyclesArg = args.find(a => a.startsWith('--cycles='));
1397
1710
  const maxCycles = cyclesArg ? parseInt(cyclesArg.split('=')[1]) : 5;
1398
1711
  const timeoutArg = args.find(a => a.startsWith('--timeout='));
@@ -1415,13 +1728,14 @@ if (command === 'init') {
1415
1728
  const verbose = args.includes('--verbose') || args.includes('-v');
1416
1729
  const dryRun = args.includes('--dry-run');
1417
1730
  const auto = args.includes('--auto');
1731
+ applyRunnerFlags(args);
1418
1732
  const maxIterationsArg = args.find(a => a.startsWith('--iterations='));
1419
1733
  const maxIterations = maxIterationsArg ? parseInt(maxIterationsArg.split('=')[1]) : undefined;
1420
1734
  const durationArg = args.find(a => a.startsWith('--duration='));
1421
1735
  const duration = durationArg ? durationArg.split('=')[1] : null;
1422
1736
 
1423
1737
  // Get description (non-flag args)
1424
- 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;
1425
1739
 
1426
1740
  const options = {
1427
1741
  ...(maxIterations !== undefined && { maxIterations }),
@@ -1710,6 +2024,11 @@ if (command === 'init') {
1710
2024
  require('../commands/fleet').fleet(args)
1711
2025
  .then(() => process.exit(0))
1712
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); });
1713
2032
  } else if (command === 'code-review' || command === 'cr') {
1714
2033
  const args = process.argv.slice(3);
1715
2034
  require('../commands/review').reviewCommand(...args)
@@ -1746,6 +2065,12 @@ if (command === 'init') {
1746
2065
  const subcommand = process.argv[3];
1747
2066
  const args = process.argv.slice(4);
1748
2067
  require('../commands/experiments').experimentsCommand(subcommand, ...args);
2068
+ } else if (command === 'compile') {
2069
+ const subcommand = process.argv[3];
2070
+ const args = process.argv.slice(4);
2071
+ Promise.resolve(require('../commands/compile').compileCommand(subcommand, ...args))
2072
+ .then(() => process.exit(process.exitCode || 0))
2073
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1749
2074
  } else if (command === 'receipt' || command === 'proof' || command === 'openclaw') {
1750
2075
  const subcommand = process.argv[3];
1751
2076
  const args = process.argv.slice(4);
@@ -1916,7 +2241,7 @@ function inspectAgentCliWiring() {
1916
2241
  },
1917
2242
  ];
1918
2243
 
1919
- const binaries = ['atris', 'claude', 'codex', 'cursor-agent', 'devin'].map((name) => ({
2244
+ const binaries = ['atris', 'ax', 'claude', 'codex', 'cursor-agent', 'devin', 'droid'].map((name) => ({
1920
2245
  name,
1921
2246
  path: commandOnPath(name),
1922
2247
  }));
@@ -1953,10 +2278,13 @@ async function agentAtris() {
1953
2278
  // Respect -h / --help / help before any auth/state work
1954
2279
  const firstArg = process.argv[3];
1955
2280
  if (firstArg === '-h' || firstArg === '--help' || firstArg === 'help') {
1956
- console.log('Usage: atris agent [doctor]');
2281
+ console.log('Usage: atris agent [doctor|dogfood|spawn|spawns|spawn-status]');
1957
2282
  console.log('');
1958
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.');
1959
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.');
1960
2288
  console.log(' Requires `atris login` first.');
1961
2289
  console.log('');
1962
2290
  console.log(' After selecting, use: atris chat ["message"]');
@@ -1966,6 +2294,22 @@ async function agentAtris() {
1966
2294
  if (firstArg === 'doctor') {
1967
2295
  agentDoctor();
1968
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
+ }
1969
2313
 
1970
2314
  const targetDir = path.join(process.cwd(), 'atris');
1971
2315
 
@@ -2209,6 +2553,65 @@ async function chatInteractive(config, credentials) {
2209
2553
  });
2210
2554
  }
2211
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
+
2212
2615
  async function atrisDevEntry(userInput = null) {
2213
2616
  // Load workspace context and present planning-ready state
2214
2617
  // userInput: optional task description for hot start