atris 3.30.12 → 3.31.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/AGENTS.md +16 -3
  2. package/FOR_AGENTS.md +81 -0
  3. package/README.md +3 -1
  4. package/atris/atris.md +51 -19
  5. package/atris/skills/README.md +1 -0
  6. package/atris/skills/blocks/SKILL.md +134 -0
  7. package/atris/skills/clawhub/philosophy-of-work/SKILL.md +56 -0
  8. package/atris/skills/youtube/SKILL.md +31 -11
  9. package/atris.md +7 -0
  10. package/ax +95 -3
  11. package/bin/atris.js +126 -153
  12. package/commands/autoland.js +319 -0
  13. package/commands/autopilot.js +94 -4
  14. package/commands/business.js +1 -1
  15. package/commands/clean.js +72 -9
  16. package/commands/codex-goal.js +72 -22
  17. package/commands/computer.js +48 -3
  18. package/commands/gm.js +1 -1
  19. package/commands/harvest.js +179 -0
  20. package/commands/init.js +1 -1
  21. package/commands/land.js +442 -0
  22. package/commands/loop-front.js +122 -4
  23. package/commands/member.js +519 -19
  24. package/commands/mission.js +3330 -290
  25. package/commands/play.js +1 -1
  26. package/commands/pulse.js +65 -7
  27. package/commands/run.js +3 -3
  28. package/commands/strings.js +301 -0
  29. package/commands/task.js +575 -102
  30. package/commands/truth.js +170 -0
  31. package/commands/xp.js +32 -8
  32. package/commands/youtube.js +72 -5
  33. package/decks/README.md +6 -12
  34. package/lib/auto-accept-certified.js +10 -0
  35. package/lib/autoland.js +283 -0
  36. package/lib/context-gatherer.js +0 -8
  37. package/lib/mission-artifact.js +504 -0
  38. package/lib/mission-room.js +846 -0
  39. package/lib/next-moves.js +212 -6
  40. package/lib/pulse.js +74 -1
  41. package/lib/runner-command.js +20 -8
  42. package/lib/runs-prune.js +242 -0
  43. package/lib/task-proof.js +1 -1
  44. package/package.json +3 -3
  45. package/decks/atris-seed-pitch-v3.json +0 -118
  46. package/decks/atris-seed-pitch-v4-skeleton.json +0 -106
  47. package/decks/atris-seed-pitch-v5.json +0 -109
  48. package/decks/atris-seed-pitch-v6.json +0 -137
  49. package/decks/atris-seed-pitch-v7.json +0 -133
  50. package/decks/mark-pincus-narrative.json +0 -102
  51. package/decks/mark-pincus-sourcery.json +0 -94
  52. package/decks/yash-applied-compute-detailed.json +0 -150
  53. package/decks/yash-applied-compute-generalist.json +0 -82
  54. package/decks/yash-applied-compute-narrative.json +0 -54
  55. package/lib/ax-chat-input.js +0 -164
  56. package/lib/ax-goal.js +0 -307
  57. package/lib/ax-prefs.js +0 -63
  58. package/lib/ax-shimmer.js +0 -63
package/ax CHANGED
@@ -1165,6 +1165,51 @@ function formatApprovalTime(value) {
1165
1165
  return `${months[Number(month) - 1] || month} ${Number(day)}, ${year} at ${hour12}:${minute} ${ampm}`;
1166
1166
  }
1167
1167
 
1168
+ function previewText(value, limit = 80) {
1169
+ return String(value || '').replace(/\s+/g, ' ').trim().slice(0, limit);
1170
+ }
1171
+
1172
+ function previewContact(value) {
1173
+ if (!value) return '';
1174
+ if (typeof value === 'string') return previewText(value, 120);
1175
+ if (typeof value !== 'object') return previewText(value, 120);
1176
+ return previewText(value.email || value.address || value.value || value.name || '', 120);
1177
+ }
1178
+
1179
+ function previewContacts(value) {
1180
+ const values = Array.isArray(value)
1181
+ ? value
1182
+ : String(value || '').includes(',')
1183
+ ? String(value).split(',')
1184
+ : [value];
1185
+ return values
1186
+ .map(previewContact)
1187
+ .filter(Boolean)
1188
+ .slice(0, 3)
1189
+ .join(', ');
1190
+ }
1191
+
1192
+ function gmailApprovalDetails(receipt, request, approvalEvent = null) {
1193
+ const payload = request && request.payload && typeof request.payload === 'object' ? request.payload : {};
1194
+ const taskPreview = receipt && receipt.task_preview && typeof receipt.task_preview === 'object' ? receipt.task_preview : {};
1195
+ const slots = taskPreview.slots && typeof taskPreview.slots === 'object' ? taskPreview.slots : {};
1196
+ const preview = approvalEvent && approvalEvent.preview && typeof approvalEvent.preview === 'object' ? approvalEvent.preview : {};
1197
+ return {
1198
+ to: previewContacts(payload.to || payload.recipient || payload.recipients || payload.email || slots.to || slots.recipient || preview.to),
1199
+ subject: previewText(payload.subject || payload.title || slots.subject || preview.subject, 80),
1200
+ };
1201
+ }
1202
+
1203
+ function isGmailApproval(request, approvalEvent = null, accept = {}) {
1204
+ const connector = canonicalConnectorId((request && request.connector) || (approvalEvent && approvalEvent.tool));
1205
+ const action = String(
1206
+ (request && (request.executor_action_type || request.action_type || request.action))
1207
+ || accept.task
1208
+ || ''
1209
+ ).toLowerCase().replace(/_/g, ' ');
1210
+ return connector === 'gmail' && /\b(send|reply|compose)\b/.test(action);
1211
+ }
1212
+
1168
1213
  function formatApprovalReceipt(receipt) {
1169
1214
  if (!receipt || typeof receipt !== 'object') return null;
1170
1215
  const events = Array.isArray(receipt.tool_events) ? receipt.tool_events : [];
@@ -1182,6 +1227,15 @@ function formatApprovalReceipt(receipt) {
1182
1227
  return `Approval needed before creation: calendar event "${String(title).slice(0, 80)}"${when ? ` ${when}` : ''}`;
1183
1228
  }
1184
1229
 
1230
+ if (isGmailApproval(request, approvalEvent, accept)) {
1231
+ const details = gmailApprovalDetails(receipt, request, approvalEvent);
1232
+ const parts = [
1233
+ details.to ? `to ${details.to}` : '',
1234
+ details.subject ? `subject "${details.subject}"` : '',
1235
+ ].filter(Boolean);
1236
+ return `Approval needed before sending Gmail${parts.length ? `: ${parts.join(' ')}` : ''} (body hidden until approved)`;
1237
+ }
1238
+
1185
1239
  const connector = String(request.connector || approvalEvent.tool || 'connector').replace(/_/g, ' ');
1186
1240
  const action = String(request.action || actionType || 'action').replace(/_/g, ' ');
1187
1241
  return `Approval needed before ${action}: ${connector}`;
@@ -1210,6 +1264,16 @@ function formatTaskPreviewPlan(receipt) {
1210
1264
  if (!taskPreview) return null;
1211
1265
  const task = String(taskPreview.task || '').trim();
1212
1266
  const slots = taskPreview.slots && typeof taskPreview.slots === 'object' ? taskPreview.slots : {};
1267
+ const approvalEvent = taskPreviewApprovalEvent(receipt);
1268
+ const request = approvalEvent && approvalEvent.approval_request;
1269
+ if (isGmailApproval(request, approvalEvent, receipt.task_accept_receipt || {}) || task === 'gmail.send_message') {
1270
+ const details = gmailApprovalDetails(receipt, request || { payload: slots }, approvalEvent);
1271
+ return [
1272
+ 'send Gmail',
1273
+ details.to ? `to ${details.to}` : '',
1274
+ details.subject ? `subject "${details.subject}"` : '',
1275
+ ].filter(Boolean).join(' ');
1276
+ }
1213
1277
  if (task === 'calendar.create_event') {
1214
1278
  const title = taskPreviewTitle(receipt);
1215
1279
  const start = slots.start || (receipt.task_accept_receipt && receipt.task_accept_receipt.start);
@@ -1712,12 +1776,30 @@ function buildMessage(message, history = []) {
1712
1776
  return String(message || '').trim();
1713
1777
  }
1714
1778
 
1779
+ function createTurnId() {
1780
+ return `axt_${Date.now().toString(36)}_${crypto.randomBytes(4).toString('hex')}`;
1781
+ }
1782
+
1783
+ function connectorTurnPolicy(message, options = {}) {
1784
+ if (!connectorWriteIntent(message)) return null;
1785
+ const policy = {
1786
+ schema: 'ax.connector_turn.v1',
1787
+ scope: 'current_turn_only',
1788
+ policy: 'preview_then_explicit_approval',
1789
+ writes_require_approval: true,
1790
+ };
1791
+ if (options.turnId) policy.turn_id = String(options.turnId);
1792
+ if (options.conversationId) policy.conversation_id = String(options.conversationId);
1793
+ return policy;
1794
+ }
1795
+
1715
1796
  function buildPayload(message, options = {}) {
1716
1797
  const mode = normalizeMode(options.mode);
1717
1798
  const route = options.business ? 'local' : resolveRoute(message, options);
1718
1799
  const local = route !== 'cloud';
1719
1800
  const verifyCommand = String(options.verify || '').trim();
1720
1801
  const previousMessages = structuredHistory(options.history || []);
1802
+ const turnId = String(options.turnId || '').trim();
1721
1803
  const payload = {
1722
1804
  message: buildMessage(message, options.history || []),
1723
1805
  model: modelForMode(mode),
@@ -1730,6 +1812,14 @@ function buildPayload(message, options = {}) {
1730
1812
  if (options.conversationId) {
1731
1813
  payload.conversation_id = String(options.conversationId);
1732
1814
  }
1815
+ if (turnId) {
1816
+ payload.turn_id = turnId;
1817
+ }
1818
+ const connectorPolicy = !local ? connectorTurnPolicy(message, { ...options, turnId }) : null;
1819
+ if (connectorPolicy) {
1820
+ payload.connector_turn = connectorPolicy;
1821
+ payload.external_action_policy = 'preview_then_explicit_approval';
1822
+ }
1733
1823
  if (options.business) {
1734
1824
  // Business cloud workspace: the model loop stays on the backend and every
1735
1825
  // file/bash tool call relays here, executing on the business EC2 via the
@@ -1882,6 +1972,7 @@ async function postTurn(message, options = {}) {
1882
1972
  const local = route !== 'cloud';
1883
1973
  const endpointRoute = options.business ? 'cloud' : route;
1884
1974
  const token = authToken();
1975
+ const turnId = options.turnId || createTurnId();
1885
1976
  const shouldSendConnectionContext = options.connectionContext
1886
1977
  || route === 'cloud'
1887
1978
  || mentionsConnector(message)
@@ -1890,7 +1981,7 @@ async function postTurn(message, options = {}) {
1890
1981
  ? await buildConnectionContext({ token, localWorkspace: local })
1891
1982
  : null);
1892
1983
  const connectionUserId = !local && isLoopbackBackend({ route: endpointRoute }) ? authUserId() : '';
1893
- const payload = buildPayload(message, { ...options, route, connectionContext, connectionUserId });
1984
+ const payload = buildPayload(message, { ...options, route, connectionContext, connectionUserId, turnId });
1894
1985
  const postData = JSON.stringify(payload);
1895
1986
  const output = options.output || process.stdout;
1896
1987
  // Relayed business turns wait on EC2 terminal calls (up to 60s each) with no
@@ -2269,7 +2360,8 @@ async function chat(options = {}) {
2269
2360
  const turnRoute = options.business ? 'cloud' : resolveRoute(trimmed, { route });
2270
2361
  if (!(await ensureRuntimeReady(turnRoute))) return false;
2271
2362
  const turnFunction = options.turnFunction || turnFunctionForMode(mode);
2272
- const result = await turnFunction(trimmed, { mode, cwd, history, output, route, business: options.business, verify: options.verify, conversationId });
2363
+ const turnId = options.turnId || createTurnId();
2364
+ const result = await turnFunction(trimmed, { mode, cwd, history, output, route, business: options.business, verify: options.verify, conversationId, turnId });
2273
2365
  if (result.output && !result.output.endsWith('\n')) output.write('\n');
2274
2366
  const approvalExecution = normalizeMode(mode) === 'code-fast'
2275
2367
  ? null
@@ -2331,7 +2423,7 @@ function printBackendHint(options = {}) {
2331
2423
  }
2332
2424
 
2333
2425
  function backendStartCommand() {
2334
- return `cd /Users/keshavrao/arena/atrisos-backend/backend && ATRIS2_ALLOW_LOCAL_WORKSPACE=1 ENVIRONMENT=development ENV=development ../venv/bin/uvicorn main:app --host ${BACKEND.host} --port ${BACKEND.port}`;
2426
+ return `cd ${process.env.HOME}/arena/atrisos-backend/backend && ATRIS2_ALLOW_LOCAL_WORKSPACE=1 ENVIRONMENT=development ENV=development ../venv/bin/uvicorn main:app --host ${BACKEND.host} --port ${BACKEND.port}`;
2335
2427
  }
2336
2428
 
2337
2429
  function formatBackendHint(options = {}) {
package/bin/atris.js CHANGED
@@ -468,6 +468,7 @@ function showHelp() {
468
468
  console.log(' analytics - Show recent productivity from journals');
469
469
  console.log(' search - Search journal history (atris search <keyword>)');
470
470
  console.log(' clean - Housekeeping (stale tasks, archive journals, broken refs)');
471
+ console.log(' harvest - Find bugs and next actions from receipts, run logs, and thinking');
471
472
  console.log(' verify - Validate work is done (tests, MAP.md, changes)');
472
473
  console.log(' task - Local agent task plane (atomic claims, TODO import)');
473
474
  console.log(' mission - Goal + loop + member owner + verifier + receipt');
@@ -485,8 +486,10 @@ function showHelp() {
485
486
  console.log(' autopilot - Guided loop that can clarify TODOs and run plan → do → review');
486
487
  console.log(' improve - Run one paid RL tick (POST /api/improve, deducts credits)');
487
488
  console.log(' worktree - Isolated Git worktrees plus guarded ship/merge for parallel agents');
489
+ console.log(' land - The landing: what is actually done vs still in the air; --reap backs up + clears overdue');
490
+ console.log(' autoland - Approve the policy once; certified work lands itself, you keep irreversible calls');
488
491
  console.log(' visualize - Generate a Slack/deck-ready visual from a prompt');
489
- console.log(' youtube - Process YouTube videos with Gemini native video analysis');
492
+ console.log(' youtube - Process YouTube videos with timestamped transcript-first analysis');
490
493
  console.log('');
491
494
  console.log('Experiments:');
492
495
  console.log(' experiments init [slug] - Prepare atris/experiments/ or scaffold a pack');
@@ -831,22 +834,27 @@ function showServeHelp() {
831
834
 
832
835
  function showLoopHelp() {
833
836
  console.log('');
834
- console.log('Usage: atris loop [--dry-run] [--json] [--limit=N]');
837
+ console.log('Usage: atris loop [add|start|status|report|stop|wiki] [options]');
835
838
  console.log('');
836
839
  console.log('Description:');
837
- console.log(' Inspect wiki upkeep state and optionally refresh wiki status/log files.');
840
+ console.log(' One front door for the self-improvement loop. It reads ROADMAP.md,');
841
+ console.log(' runs bounded proof-backed work, and keeps wiki upkeep under loop wiki.');
838
842
  console.log('');
839
- console.log('Options:');
840
- console.log(' --dry-run Preview wiki loop state without writing files.');
841
- console.log(' --json Print the loop report as JSON.');
842
- console.log(' --limit=N Limit suggested source count.');
843
- console.log(' --help, -h Show this help.');
843
+ console.log('Commands:');
844
+ console.log(' atris loop add "<task>" Put a bounded task into the loop queue.');
845
+ console.log(' atris loop start [--once] Run the local loop.');
846
+ console.log(' atris loop start --overnight Install the durable heartbeat.');
847
+ console.log(' atris loop status [--json] Show heartbeat, local runs, and next moves.');
848
+ console.log(' atris loop report [--json] Show proof of handled and queued loop work.');
849
+ console.log(' atris loop stop Remove the durable heartbeat.');
850
+ console.log(' atris loop wiki Run the wiki upkeep loop.');
851
+ console.log(' --help, -h Show this help.');
844
852
  console.log('');
845
853
  }
846
854
 
847
855
  function showCleanHelp() {
848
856
  console.log('');
849
- console.log('Usage: atris clean [--dry-run]');
857
+ console.log('Usage: atris clean [--dry-run] [--json]');
850
858
  console.log('');
851
859
  console.log('Description:');
852
860
  console.log(' Check workspace housekeeping: stale tasks, MAP.md refs, old journals,');
@@ -854,6 +862,7 @@ function showCleanHelp() {
854
862
  console.log('');
855
863
  console.log('Options:');
856
864
  console.log(' --dry-run, -n Preview cleanup without changing files.');
865
+ console.log(' --json Print machine-readable cleanup results.');
857
866
  console.log(' --help, -h Show this help.');
858
867
  console.log('');
859
868
  }
@@ -913,12 +922,12 @@ if (command === '2' && ['fast', 'pro'].includes(String(firstCommandArg || '').to
913
922
  }
914
923
 
915
924
  // Check if this is a known command or natural language input
916
- const knownCommands = ['init', 'log', 'now', 'radar', 'ctop', 'launchpad', 'status', 'analytics', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', 'plan', 'do', 'review', 'release',
925
+ const knownCommands = ['init', 'log', 'now', 'radar', 'ctop', 'launchpad', 'status', 'analytics', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', '_start', 'plan', 'do', 'review', 'release',
917
926
  'activate', '_activate', 'agent', 'chat', 'fast', 'ax', 'console', 'serve', 'login', 'logout', 'whoami', 'switch', 'use', 'accounts', '_resolve', '_profile-email', '_switch-session', 'shell-init', 'update', 'upgrade', 'version', 'help', 'next', 'atris',
918
- 'clean', 'verify', 'search', 'skill', 'member', 'codex-goal', 'app', 'apps', 'learn', 'lesson', 'plugin', 'experiments', 'receipt', 'proof', 'openclaw', 'pull', 'push', 'live', 'align', 'terminal', 'computer', 'diff', 'business', 'sync', 'youtube',
919
- 'ingest', 'query', 'lint', 'loop', 'pulse', 'task', 'mission', 'probe', 'worktree', 'aeo', 'slop', 'security-review', 'secure', 'deck', 'site', 'theme', 'card', 'reel', 'improve', 'xp', 'play', 'gm', 'x', 'recap', 'signup', 'clarity', 'moves',
927
+ 'clean', 'harvest', 'verify', 'search', 'skill', 'member', 'codex-goal', 'app', 'apps', 'learn', 'lesson', 'plugin', 'experiments', 'receipt', 'proof', 'openclaw', 'pull', 'push', 'live', 'align', 'terminal', 'computer', 'diff', 'business', 'sync', 'youtube',
928
+ 'ingest', 'query', 'lint', 'loop', 'pulse', 'task', 'mission', 'probe', 'worktree', 'land', 'autoland', 'aeo', 'slop', 'strings', 'security-review', 'secure', 'deck', 'site', 'theme', 'card', 'reel', 'improve', 'xp', 'play', 'gm', 'x', 'recap', 'signup', 'clarity', 'moves',
920
929
  'gmail', 'calendar', 'twitter', 'slack', 'imessage', 'integrations', 'setup', 'clean-workspace', 'cw',
921
- 'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'compile', 'spaceship'];
930
+ 'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'compile', 'spaceship', 'truth'];
922
931
 
923
932
  // Check if command is an atris.md spec file - triggers welcome visualization
924
933
  function isSpecFile(cmd) {
@@ -940,7 +949,15 @@ if (command === '--version' || command === '-v' || process.argv.includes('--vers
940
949
  // If no command OR command is not recognized, treat as natural language
941
950
  // Voice-friendly aliases — natural language → command mapping
942
951
  // Solves speech-to-text issues (inspired by gstack v0.14.6 voice-triggers)
952
+ const START_MISSION_OBJECTIVE = 'self improve goal after goal: pick one useful bounded mission from current Atris state, run proof, and continue only with real next work';
953
+
943
954
  const voiceTriggers = {
955
+ 'start': '_start',
956
+ 'start now': '_start',
957
+ 'go': '_start',
958
+ 'keep going': '_start',
959
+ 'keepgoing': '_start',
960
+ 'keep-going': '_start',
944
961
  'review my code': 'code-review',
945
962
  'check my code': 'code-review',
946
963
  'run a review': 'code-review',
@@ -965,7 +982,12 @@ const voiceTriggers = {
965
982
  if (!command || !knownCommands.includes(command)) {
966
983
  // Check voice triggers before falling through to natural language
967
984
  const fullInput = process.argv.slice(2).join(' ').toLowerCase().trim();
968
- const triggered = voiceTriggers[fullInput];
985
+ const fullInputWithoutFlags = process.argv.slice(2)
986
+ .filter((arg, index, args) => !String(arg).startsWith('-') && !isOptionValue(args, index, RUNNER_FLAG_NAMES))
987
+ .join(' ')
988
+ .toLowerCase()
989
+ .trim();
990
+ const triggered = voiceTriggers[fullInput] || voiceTriggers[fullInputWithoutFlags];
969
991
  if (triggered) {
970
992
  command = triggered;
971
993
  // Re-check — if it's now a known command, fall through to dispatch
@@ -1355,6 +1377,12 @@ function showWelcomeVisualization() {
1355
1377
  : `${tasksInReview} waiting`;
1356
1378
  console.log(` │ ā³ Review: ${reviewText.padEnd(26)}│`);
1357
1379
  }
1380
+ let landInfo = null;
1381
+ try { landInfo = require('../commands/land').landSummary(process.cwd()); } catch (err) { landInfo = null; }
1382
+ if (landInfo && landInfo.branches > 0) {
1383
+ const landText = `${landInfo.branches} in the air, ${landInfo.due} overdue`;
1384
+ console.log(` │ šŸ›¬ Land: ${landText.padEnd(26)}│`);
1385
+ }
1358
1386
  console.log(` │ šŸ“ Journal: ${(journalEntries + ' entries today').padEnd(26)}│`);
1359
1387
  if (endgameState.slug !== 'unset' && endgameState.horizon) {
1360
1388
  const endgameLine = endgameState.slug + ' — ' + endgameState.horizon;
@@ -1377,7 +1405,13 @@ function showWelcomeVisualization() {
1377
1405
  if (tasksCertified > 0) {
1378
1406
  console.log(` Ready. ${tasksCertified} certified await accept — run 'atris task reviews'.`);
1379
1407
  } else {
1380
- console.log(` Ready. Run 'atris plan' to start.`);
1408
+ let landHint = null;
1409
+ try { landHint = require('../commands/land').landSummary(process.cwd()); } catch (err) { landHint = null; }
1410
+ if (landHint && landHint.due > 0) {
1411
+ console.log(` Ready. ${landHint.due} overdue in the landing — run 'atris land --reap'.`);
1412
+ } else {
1413
+ console.log(` Ready. Run 'atris plan' to start.`);
1414
+ }
1381
1415
  }
1382
1416
  console.log('');
1383
1417
  }
@@ -1398,6 +1432,40 @@ if (command === 'init') {
1398
1432
  console.error(`āœ— Error: ${error.message || error}`);
1399
1433
  process.exit(1);
1400
1434
  });
1435
+ } else if (command === '_start') {
1436
+ const args = process.argv.slice(3);
1437
+ if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
1438
+ console.log('');
1439
+ console.log('Usage: atris start [options]');
1440
+ console.log('');
1441
+ console.log('Starts the durable mission loop for one useful self-improvement mission.');
1442
+ console.log('');
1443
+ console.log('Options:');
1444
+ console.log(' --json Print the mission route without starting it.');
1445
+ console.log(' --owner X Override mission owner.');
1446
+ console.log(' --cadence X Override mission cadence.');
1447
+ console.log('');
1448
+ process.exit(0);
1449
+ }
1450
+
1451
+ if (args.includes('--json')) {
1452
+ console.log(JSON.stringify({
1453
+ ok: true,
1454
+ action: 'start_mission_run',
1455
+ route: `atris mission run "${START_MISSION_OBJECTIVE}"`,
1456
+ reason: 'casual_launch',
1457
+ objective: START_MISSION_OBJECTIVE,
1458
+ expected_loop: 'mission_run',
1459
+ }, null, 2));
1460
+ process.exit(0);
1461
+ }
1462
+
1463
+ Promise.resolve(require('../commands/mission').missionCommand(['run', START_MISSION_OBJECTIVE, ...args]))
1464
+ .then(() => process.exit(process.exitCode || 0))
1465
+ .catch((error) => {
1466
+ console.error(`āœ— Start failed: ${error.message || error}`);
1467
+ process.exit(1);
1468
+ });
1401
1469
  } else if (command === 'task') {
1402
1470
  // SQLite-backed task plane. ~/.atris/tasks.db, gitignored, per-workspace.
1403
1471
  Promise.resolve(require('../commands/task').run(process.argv.slice(3)))
@@ -1421,11 +1489,24 @@ if (command === 'init') {
1421
1489
  Promise.resolve(require('../commands/worktree').worktreeCommand(process.argv.slice(3)))
1422
1490
  .then((code) => process.exit(code || 0))
1423
1491
  .catch((err) => { console.error(`\nāœ— Error: ${err.message || err}`); process.exit(1); });
1492
+ } else if (command === 'autoland') {
1493
+ Promise.resolve(require('../commands/autoland').autolandCommand(process.argv.slice(3)))
1494
+ .then((code) => process.exit(code || 0))
1495
+ .catch((err) => { console.error(`\nāœ— Error: ${err.message || err}`); process.exit(1); });
1496
+ } else if (command === 'land') {
1497
+ Promise.resolve(require('../commands/land').landCommand(process.argv.slice(3)))
1498
+ .then((code) => process.exit(code || 0))
1499
+ .catch((err) => { console.error(`\nāœ— Error: ${err.message || err}`); process.exit(1); });
1424
1500
  } else if (command === 'radar' || command === 'ctop') {
1425
1501
  const radarArgs = command === 'ctop' ? ['--agents', ...process.argv.slice(3)] : process.argv.slice(3);
1426
1502
  Promise.resolve(require('../commands/radar').radarCommand(radarArgs))
1427
1503
  .then((code) => process.exit(code || 0))
1428
1504
  .catch((err) => { console.error(`\nāœ— Error: ${err.message || err}`); process.exit(1); });
1505
+ } else if (command === 'truth') {
1506
+ // Truth: one table rolling up mission state, tasks, feature proof receipts, and loop heartbeats.
1507
+ Promise.resolve(require('../commands/truth').truthCommand(process.argv.slice(3)))
1508
+ .then((code) => process.exit(code || 0))
1509
+ .catch((err) => { console.error(`\nāœ— Error: ${err.message || err}`); process.exit(1); });
1429
1510
  } else if (command === 'codex-goal') {
1430
1511
  Promise.resolve(require('../commands/codex-goal').codexGoalCommand(process.argv.slice(3)))
1431
1512
  .then(() => process.exit(process.exitCode || 0))
@@ -1922,7 +2003,14 @@ if (command === 'init') {
1922
2003
  process.exit(0);
1923
2004
  }
1924
2005
  const dryRun = process.argv.includes('--dry-run') || process.argv.includes('-n');
1925
- require('../commands/clean').cleanAtris({ dryRun });
2006
+ const json = process.argv.includes('--json');
2007
+ require('../commands/clean').cleanAtris({ dryRun, json });
2008
+ } else if (command === 'harvest') {
2009
+ Promise.resolve(require('../commands/harvest').harvestCommand(process.argv.slice(3)))
2010
+ .catch((err) => {
2011
+ console.error(err.message || err);
2012
+ process.exit(1);
2013
+ });
1926
2014
  } else if (command === 'verify') {
1927
2015
  const args = process.argv.slice(3);
1928
2016
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
@@ -2130,8 +2218,8 @@ if (command === 'init') {
2130
2218
  showLoopHelp();
2131
2219
  process.exit(0);
2132
2220
  }
2133
- require('../commands/loop').loopAtris(args)
2134
- .then(() => process.exit(0))
2221
+ Promise.resolve(require('../commands/loop-front').loopFront(args))
2222
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2135
2223
  .catch((err) => { console.error(`\nāœ— Error: ${err.message || err}`); process.exit(1); });
2136
2224
  } else if (command === 'clean-workspace' || command === 'cw') {
2137
2225
  const { cleanWorkspace } = require('../commands/workspace-clean');
@@ -2157,6 +2245,12 @@ if (command === 'init') {
2157
2245
  Promise.resolve(require('../commands/slop').slopCommand(process.argv.slice(3)))
2158
2246
  .then((code) => process.exit(typeof code === 'number' ? code : 0))
2159
2247
  .catch((err) => { console.error(`\nāœ— Error: ${err.message || err}`); process.exit(1); });
2248
+ } else if (command === 'strings') {
2249
+ // Strings: content design system from live code (no LLM). Extracts UI strings, flags variants,
2250
+ // enforces preferred terms at the gate. Exit 1 = variants/banned terms found, for CI + the gate.
2251
+ Promise.resolve(require('../commands/strings').stringsCommand(process.argv.slice(3)))
2252
+ .then((code) => process.exit(typeof code === 'number' ? code : 0))
2253
+ .catch((err) => { console.error(`\nāœ— Error: ${err.message || err}`); process.exit(1); });
2160
2254
  } else if (command === 'security-review' || command === 'secure') {
2161
2255
  // Security review: deterministic secrets/PII/code-risk scan (no LLM). Exit 1 = HIGH finding,
2162
2256
  // for the autopilot/mission/CI gate + a SOC 2 evidence artifact via --json.
@@ -2410,13 +2504,12 @@ async function agentAtris() {
2410
2504
  // Respect -h / --help / help before any auth/state work
2411
2505
  const firstArg = process.argv[3];
2412
2506
  if (firstArg === '-h' || firstArg === '--help' || firstArg === 'help') {
2413
- console.log('Usage: atris agent [doctor|dogfood|spawn|spawns|spawn-status]');
2507
+ console.log('Usage: atris agent [doctor|spawn|spawns|spawn-status]');
2414
2508
  console.log('');
2415
2509
  console.log(' Pick which cloud agent to chat with from this workspace.');
2416
2510
  console.log(' Run `atris agent spawn <role> --task "..."` to create a worker request.');
2417
2511
  console.log(' Run `atris agent spawns` to list worker requests.');
2418
2512
  console.log(' Run `atris agent doctor` to verify local AI CLIs can see Atris context.');
2419
- console.log(' Run `atris agent dogfood --live` to smoke-test Devin/Droid with GLM 5.2.');
2420
2513
  console.log(' Requires `atris login` first.');
2421
2514
  console.log('');
2422
2515
  console.log(' After selecting, use: atris chat ["message"]');
@@ -2427,7 +2520,19 @@ async function agentAtris() {
2427
2520
  agentDoctor();
2428
2521
  }
2429
2522
  if (firstArg === 'dogfood') {
2430
- const result = require('../commands/agent-spawn').agentDogfoodCommand(process.argv.slice(4));
2523
+ // Internal diagnostic, gated off the public CLI. Operators use `atris agent doctor`.
2524
+ if (!process.env.ATRIS_INTERNAL_AGENT_DOGFOOD) {
2525
+ console.error('atris agent dogfood is an internal diagnostic and is not part of the public CLI.');
2526
+ console.error('Run `atris agent doctor` to verify local AI CLIs can see Atris context.');
2527
+ process.exit(1);
2528
+ }
2529
+ const dogfoodArgs = process.argv.slice(4);
2530
+ if (dogfoodArgs.includes('--help') || dogfoodArgs.includes('-h') || dogfoodArgs[0] === 'help') {
2531
+ console.log('Internal usage: atris agent dogfood [--live]');
2532
+ console.log(' Smoke-test Devin/Droid with GLM 5.2. Gated behind ATRIS_INTERNAL_AGENT_DOGFOOD.');
2533
+ process.exit(0);
2534
+ }
2535
+ const result = require('../commands/agent-spawn').agentDogfoodCommand(dogfoodArgs);
2431
2536
  process.exit(result.ok ? 0 : 1);
2432
2537
  }
2433
2538
  if (firstArg === 'spawn') {
@@ -2840,135 +2945,3 @@ async function atrisFastOnce(credentials, message) {
2840
2945
  await streamProChat(endpoint, credentials.token, body);
2841
2946
  console.log('\n\nāœ“ Complete\n');
2842
2947
  }
2843
-
2844
- async function atrisDevEntry(userInput = null) {
2845
- // Load workspace context and present planning-ready state
2846
- // userInput: optional task description for hot start
2847
- const targetDir = path.join(process.cwd(), 'atris');
2848
-
2849
- // Check if Atris is initialized
2850
- if (!fs.existsSync(targetDir)) {
2851
- console.log('');
2852
- console.log('šŸš€ Welcome to Atris\n');
2853
- console.log('Not initialized yet. Let\'s get started:\n');
2854
- console.log(' → atris init Set up your workspace');
2855
- console.log(' → atris help See all commands\n');
2856
- return;
2857
- }
2858
-
2859
- ensureLogDirectory();
2860
- const { logFile, dateFormatted } = getLogPath();
2861
- if (!fs.existsSync(logFile)) {
2862
- createLogFile(logFile, dateFormatted);
2863
- }
2864
-
2865
- // Load context
2866
- const workspaceDir = process.cwd();
2867
- const state = detectWorkspaceState(workspaceDir);
2868
- const context = loadContext(workspaceDir);
2869
-
2870
- // Detect existing features
2871
- const featuresDir = path.join(targetDir, 'features');
2872
- let existingFeatures = [];
2873
- if (fs.existsSync(featuresDir)) {
2874
- existingFeatures = fs.readdirSync(featuresDir)
2875
- .filter(name => {
2876
- const featurePath = path.join(featuresDir, name);
2877
- return fs.statSync(featurePath).isDirectory() && !name.startsWith('_');
2878
- });
2879
- }
2880
-
2881
- console.log('');
2882
- console.log('ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”');
2883
- console.log('│ Atris Mode │');
2884
- console.log('ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜');
2885
- console.log('');
2886
- console.log(`šŸ“… ${dateFormatted}`);
2887
- console.log('');
2888
-
2889
- // Show existing features
2890
- if (existingFeatures.length > 0) {
2891
- console.log('šŸ“¦ Features: ' + existingFeatures.join(', '));
2892
- console.log('');
2893
- }
2894
-
2895
- // Show active work
2896
- if (context.inProgressFeatures.length > 0) {
2897
- console.log('⚔ Active: ' + context.inProgressFeatures.join(', '));
2898
- console.log('');
2899
- }
2900
-
2901
- // Show inbox
2902
- if (context.hasInbox && context.inboxItems.length > 0) {
2903
- console.log(`šŸ“„ Inbox (${context.inboxItems.length}):`);
2904
- context.inboxItems.slice(0, 3).forEach((item, i) => {
2905
- const preview = item.length > 50 ? item.substring(0, 47) + '...' : item;
2906
- console.log(` ${i + 1}. ${preview}`);
2907
- });
2908
- if (context.inboxItems.length > 3) {
2909
- console.log(` ... and ${context.inboxItems.length - 3} more`);
2910
- }
2911
- console.log('');
2912
- }
2913
-
2914
- // Show recent completions
2915
- const logContent = fs.existsSync(logFile) ? fs.readFileSync(logFile, 'utf8') : '';
2916
- const completedMatch = logContent.match(/## Completed āœ…\n([\s\S]*?)(?=\n##|$)/);
2917
- if (completedMatch && completedMatch[1].trim()) {
2918
- const completedItems = completedMatch[1].trim().split('\n')
2919
- .filter(line => line.match(/^- \*\*C\d+:/))
2920
- .slice(-2);
2921
- if (completedItems.length > 0) {
2922
- console.log('āœ… Recent:');
2923
- completedItems.forEach(item => {
2924
- const match = item.match(/^- \*\*C\d+:\s*(.+)\*\*/);
2925
- if (match) {
2926
- const text = match[1].length > 50 ? match[1].substring(0, 47) + '...' : match[1];
2927
- console.log(` • ${text}`);
2928
- }
2929
- });
2930
- console.log('');
2931
- }
2932
- }
2933
-
2934
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2935
- console.log('atris — navigator agent');
2936
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2937
- console.log('');
2938
-
2939
- if (userInput) {
2940
- // Hot start - user provided task
2941
- console.log('User wants:');
2942
- console.log(`"${userInput}"`);
2943
- console.log('');
2944
- } else {
2945
- // Cold start - no specific task
2946
- console.log('Wait for user to describe what they want.');
2947
- console.log('');
2948
- }
2949
-
2950
- console.log('āš ļø APPROVAL REQUIRED — Follow this workflow:');
2951
- console.log('');
2952
- console.log('STEP 1: Show ASCII visualization');
2953
- console.log(' Create diagrams showing architecture/flow/UI');
2954
- console.log(' SHOW diagrams to user and WAIT for approval.');
2955
- console.log('');
2956
- console.log('STEP 2: After approval, determine scope');
2957
- if (existingFeatures.length > 0) {
2958
- console.log(' Existing: ' + existingFeatures.join(', '));
2959
- }
2960
- console.log(' NEW feature → atris/features/[name]/idea.md + build.md + validate.md');
2961
- console.log(' EXISTING → Update that feature\'s docs');
2962
- console.log(' SIMPLE → TODO.md only');
2963
- console.log('');
2964
- console.log('STEP 3: Create/update docs');
2965
- console.log(' idea.md = intent (any format)');
2966
- console.log(' build.md = technical spec');
2967
- console.log(' validate.md = proof it works (from _templates/validate.md.template)');
2968
- console.log(' lessons.md = read past lessons before planning, write new ones after validating');
2969
- console.log('');
2970
- console.log('ā›” DO NOT execute — that\'s for "atris do"');
2971
- console.log('');
2972
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2973
- console.log('');
2974
- }