atris 3.35.0 → 3.36.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 (133) hide show
  1. package/AGENTS.md +37 -0
  2. package/README.md +5 -3
  3. package/atris/GETTING_STARTED.md +1 -1
  4. package/atris/atris.md +3 -0
  5. package/atris/policies/day-loop-voice.md +102 -0
  6. package/atris/policies/outbound-artifact-gate.md +2 -0
  7. package/atris/skills/design/SKILL.md +56 -32
  8. package/atris/skills/endgame/SKILL.md +12 -6
  9. package/atris/skills/engines/SKILL.md +22 -4
  10. package/atris/skills/fable-method/SKILL.md +66 -0
  11. package/atris/skills/improve/SKILL.md +65 -45
  12. package/atris/skills/youtube/SKILL.md +10 -1
  13. package/atris.md +2 -0
  14. package/ax +147 -19
  15. package/bin/atris.js +565 -265
  16. package/commands/activate.js +194 -88
  17. package/commands/agents.js +166 -0
  18. package/commands/autoland.js +459 -107
  19. package/commands/autopilot-front.js +20 -2
  20. package/commands/autopilot.js +118 -2
  21. package/commands/avail.js +407 -0
  22. package/commands/bench.js +188 -0
  23. package/commands/brain.js +3 -0
  24. package/commands/brief.js +651 -0
  25. package/commands/business-sync.js +192 -6
  26. package/commands/clean.js +50 -24
  27. package/commands/close.js +1083 -0
  28. package/commands/cloud.js +245 -0
  29. package/commands/compile.js +292 -1
  30. package/commands/computer.js +150 -3
  31. package/commands/dream.js +365 -0
  32. package/commands/drill.js +371 -0
  33. package/commands/engine.js +993 -32
  34. package/commands/experiments.js +28 -0
  35. package/commands/feedback.js +34 -12
  36. package/commands/fleet-report.js +206 -0
  37. package/commands/gm.js +23 -0
  38. package/commands/goal.js +247 -0
  39. package/commands/improve.js +642 -26
  40. package/commands/init.js +72 -44
  41. package/commands/interview.js +67 -1
  42. package/commands/land.js +152 -52
  43. package/commands/lifecycle.js +39 -3
  44. package/commands/log.js +84 -1
  45. package/commands/loops.js +220 -16
  46. package/commands/meet.js +220 -0
  47. package/commands/member.js +511 -34
  48. package/commands/mission.js +3029 -339
  49. package/commands/next.js +137 -0
  50. package/commands/now.js +220 -25
  51. package/commands/one-lap.js +776 -0
  52. package/commands/orb.js +314 -0
  53. package/commands/pack-craft.js +179 -0
  54. package/commands/pack.js +823 -0
  55. package/commands/play.js +3 -2
  56. package/commands/probe.js +30 -3
  57. package/commands/pulse.js +241 -46
  58. package/commands/push.js +260 -82
  59. package/commands/rainmaker.js +49 -0
  60. package/commands/report.js +415 -0
  61. package/commands/scout.js +147 -0
  62. package/commands/search.js +363 -0
  63. package/commands/skill.js +47 -3
  64. package/commands/slop.js +50 -2
  65. package/commands/soul.js +1 -1
  66. package/commands/stream.js +861 -0
  67. package/commands/study.js +693 -0
  68. package/commands/sync.js +67 -54
  69. package/commands/task.js +1346 -117
  70. package/commands/team.js +73 -0
  71. package/commands/verify.js +96 -0
  72. package/commands/watch.js +303 -0
  73. package/commands/wish.js +500 -0
  74. package/commands/workflow.js +11 -5
  75. package/commands/worktree.js +234 -13
  76. package/commands/xp.js +29 -11
  77. package/lib/auto-accept-certified.js +331 -34
  78. package/lib/autoland.js +319 -54
  79. package/lib/ax-auto-lane.js +79 -0
  80. package/lib/bench/context.js +147 -0
  81. package/lib/bench/engines.js +141 -0
  82. package/lib/bench/report.js +140 -0
  83. package/lib/bench/runner.js +512 -0
  84. package/lib/brief-ledger.js +350 -0
  85. package/lib/cloud-mission.js +259 -0
  86. package/lib/codex-flight.js +154 -0
  87. package/lib/default-runner.js +45 -0
  88. package/lib/default-verifier.js +70 -0
  89. package/lib/engine-registry.js +232 -0
  90. package/lib/experiments/daily.js +640 -0
  91. package/lib/fleet.js +2219 -67
  92. package/lib/improve-vitals-html.js +171 -0
  93. package/lib/known-commands.js +58 -0
  94. package/lib/loop-doctor.js +416 -0
  95. package/lib/member-switches.js +144 -0
  96. package/lib/mission-room.js +1 -0
  97. package/lib/mission-root.js +52 -0
  98. package/lib/next-moves.js +327 -10
  99. package/lib/one-lap-validator.js +60 -0
  100. package/lib/orb-context.js +477 -0
  101. package/lib/orb-scorecard.js +224 -0
  102. package/lib/policy-lessons.js +52 -1
  103. package/lib/pulse.js +277 -3
  104. package/lib/receipt-block.js +168 -0
  105. package/lib/receipt-evidence.js +65 -4
  106. package/lib/router-brain.js +352 -0
  107. package/lib/runner-command.js +10 -0
  108. package/lib/self-drive.js +258 -0
  109. package/lib/short-name.js +103 -0
  110. package/lib/spawn-env.js +18 -0
  111. package/lib/state-detection.js +56 -1
  112. package/lib/sync-status.js +59 -0
  113. package/lib/task-db.js +108 -29
  114. package/lib/task-proof.js +23 -1
  115. package/lib/team-presence.js +260 -0
  116. package/lib/tool-result-encode.js +7 -0
  117. package/lib/trust-tiers.js +90 -0
  118. package/lib/usage.js +107 -0
  119. package/lib/voice-gate.js +163 -0
  120. package/lib/wish-audit.js +1368 -0
  121. package/lib/wish-delegate.js +1840 -0
  122. package/lib/wish-design.js +110 -0
  123. package/lib/wish-stats.js +183 -0
  124. package/lib/wish-store.js +354 -0
  125. package/lib/zip.js +221 -0
  126. package/package.json +3 -1
  127. package/templates/loops/atris/loops/LOOPS.md +55 -0
  128. package/templates/loops/atris/loops/TICK.md +24 -0
  129. package/templates/loops/atris/loops/feedback.md +22 -0
  130. package/templates/loops/atris/loops/quality.md +22 -0
  131. package/templates/loops/atris/wiki/systems/loops.md +41 -0
  132. package/utils/api.js +5 -1
  133. package/utils/auth.js +57 -21
@@ -0,0 +1,147 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+ const { spawnSync } = require('node:child_process');
7
+
8
+ const PROFILE_ENV_KEYS = [
9
+ 'ATRIS_PROFILE',
10
+ 'ATRIS_PROFILE_EMAIL',
11
+ 'ATRIS_AUTH_PROFILE',
12
+ 'ATRIS_ACTIVE_PROFILE',
13
+ 'ATRIS_ACCOUNT',
14
+ 'ATRIS_ACCOUNT_ID',
15
+ 'ATRIS_BUSINESS_ID',
16
+ 'ATRIS_BUSINESS_SLUG',
17
+ 'ATRIS_OWNER_ID',
18
+ 'ATRIS_OWNER_SLUG',
19
+ 'AX_PROFILE',
20
+ 'AX_ACCOUNT',
21
+ ];
22
+
23
+ function defaultRepoRoot() {
24
+ return path.resolve(__dirname, '..', '..');
25
+ }
26
+
27
+ function hermeticEnv(home, extra = {}) {
28
+ const env = { ...process.env };
29
+ for (const key of PROFILE_ENV_KEYS) env[key] = '';
30
+ for (const key of Object.keys(env)) {
31
+ if (key.startsWith('NODE_TEST_')) delete env[key];
32
+ }
33
+ env.HOME = home;
34
+ env.USERPROFILE = home;
35
+ env.XDG_CONFIG_HOME = path.join(home, '.config');
36
+ env.XDG_CACHE_HOME = path.join(home, '.cache');
37
+ env.XDG_DATA_HOME = path.join(home, '.local', 'share');
38
+ env.ATRIS_SKIP_UPDATE_CHECK = '1';
39
+ env.NO_UPDATE_NOTIFIER = '1';
40
+ env.NODE_NO_WARNINGS = '1';
41
+ env.ATRIS_AGENT_PROOF_ONLY = '0';
42
+ env.ATRIS_TASKS_DB = path.join(home, '.atris', 'tasks.db');
43
+ env.ATRIS_AGENT_ID = env.ATRIS_AGENT_ID || 'bench';
44
+ return { ...env, ...extra };
45
+ }
46
+
47
+ function spawnErrorFromResult(result, command) {
48
+ const source = result && result.error ? result.error : new Error(`failed to spawn ${command}`);
49
+ const err = new Error(source.message || `failed to spawn ${command}`);
50
+ err.name = source.name || err.name;
51
+ err.code = source.code || 'SPAWN_ERROR';
52
+ err.errno = source.errno;
53
+ err.syscall = source.syscall || `spawn ${command}`;
54
+ err.path = source.path || command;
55
+ err.spawnError = true;
56
+ err.result = result;
57
+ return err;
58
+ }
59
+
60
+ function createBenchContext(options = {}) {
61
+ const repoRoot = path.resolve(options.repoRoot || defaultRepoRoot());
62
+ const cliPath = options.cliPath || path.join(repoRoot, 'bin', 'atris.js');
63
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), `atris-bench-${options.taskId || 'task'}-`));
64
+ const home = path.join(tempRoot, 'home');
65
+ const workspace = path.join(tempRoot, 'workspace');
66
+ fs.mkdirSync(home, { recursive: true });
67
+ fs.mkdirSync(workspace, { recursive: true });
68
+ const env = hermeticEnv(home, options.env || {});
69
+ const defaultTimeout = Number(options.timeoutMs || 30000);
70
+
71
+ const ctx = {
72
+ repoRoot,
73
+ cliPath,
74
+ env,
75
+ workspace,
76
+ run(command, args = [], runOptions = {}) {
77
+ const result = spawnSync(command, args, {
78
+ cwd: runOptions.cwd || workspace,
79
+ input: runOptions.input,
80
+ encoding: 'utf8',
81
+ timeout: Number(runOptions.timeoutMs || runOptions.timeout || defaultTimeout),
82
+ maxBuffer: Number(runOptions.maxBuffer || 16 * 1024 * 1024),
83
+ env: {
84
+ ...env,
85
+ ...(runOptions.env || {}),
86
+ },
87
+ });
88
+ if (result.error) throw spawnErrorFromResult(result, command);
89
+ return result;
90
+ },
91
+ runCli(args = [], runOptions = {}) {
92
+ const result = spawnSync(process.execPath, [cliPath, ...args], {
93
+ cwd: workspace,
94
+ input: runOptions.input,
95
+ encoding: 'utf8',
96
+ timeout: Number(runOptions.timeoutMs || runOptions.timeout || defaultTimeout),
97
+ maxBuffer: Number(runOptions.maxBuffer || 16 * 1024 * 1024),
98
+ env: {
99
+ ...env,
100
+ ...(runOptions.env || {}),
101
+ },
102
+ });
103
+ if (result.error) throw spawnErrorFromResult(result, process.execPath);
104
+ return result;
105
+ },
106
+ requireCmd(command, args = ['--version']) {
107
+ const result = spawnSync(command, args, {
108
+ cwd: workspace,
109
+ encoding: 'utf8',
110
+ timeout: 5000,
111
+ env,
112
+ });
113
+ if (result.error || result.status !== 0) {
114
+ const err = result.error
115
+ ? spawnErrorFromResult(result, command)
116
+ : new Error(`${command} exited ${result.status}`);
117
+ err.code = err.code || 'COMMAND_UNAVAILABLE';
118
+ err.command = command;
119
+ throw err;
120
+ }
121
+ return command;
122
+ },
123
+ };
124
+
125
+ return {
126
+ ctx,
127
+ teardown() {
128
+ fs.rmSync(tempRoot, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
129
+ },
130
+ };
131
+ }
132
+
133
+ async function withBenchContext(options, fn) {
134
+ const { ctx, teardown } = createBenchContext(options);
135
+ try {
136
+ return await fn(ctx);
137
+ } finally {
138
+ teardown();
139
+ }
140
+ }
141
+
142
+ module.exports = {
143
+ PROFILE_ENV_KEYS,
144
+ createBenchContext,
145
+ hermeticEnv,
146
+ withBenchContext,
147
+ };
@@ -0,0 +1,141 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const { spawnSync } = require('node:child_process');
6
+
7
+ const ENGINE_NAMES = Object.freeze([
8
+ 'codex',
9
+ 'cursor',
10
+ 'claude',
11
+ 'atris-fast',
12
+ 'null',
13
+ 'solution',
14
+ ]);
15
+
16
+ const ENGINE_BINS = Object.freeze({
17
+ codex: 'codex',
18
+ cursor: 'cursor-agent',
19
+ claude: 'claude',
20
+ 'atris-fast': 'atris',
21
+ });
22
+
23
+ function normalizeEngineName(name) {
24
+ const normalized = String(name || '').trim();
25
+ if (!normalized) return '';
26
+ if (!ENGINE_NAMES.includes(normalized)) {
27
+ throw new Error(`unknown bench engine: ${normalized}`);
28
+ }
29
+ return normalized;
30
+ }
31
+
32
+ function commandAvailable(command) {
33
+ const result = spawnSync('/bin/sh', ['-c', `command -v ${command}`], {
34
+ encoding: 'utf8',
35
+ env: process.env,
36
+ });
37
+ return result.status === 0 && Boolean(String(result.stdout || '').trim());
38
+ }
39
+
40
+ function spawnEngine(command, args, workspaceDir, timeoutMs, options = {}) {
41
+ const result = spawnSync(command, args, {
42
+ cwd: workspaceDir,
43
+ input: options.input,
44
+ encoding: 'utf8',
45
+ timeout: Number(timeoutMs || 300000),
46
+ maxBuffer: Number(options.maxBuffer || 16 * 1024 * 1024),
47
+ env: {
48
+ ...process.env,
49
+ ...(options.env || {}),
50
+ },
51
+ });
52
+ return {
53
+ status: result.status === null ? 1 : result.status,
54
+ stdout: result.stdout || '',
55
+ stderr: result.error ? String(result.error.message || result.error) : (result.stderr || ''),
56
+ };
57
+ }
58
+
59
+ function realEngineAdapter(name, bin, runArgs) {
60
+ return {
61
+ name,
62
+ available(workspaceDir) {
63
+ if (!commandAvailable(bin)) {
64
+ return { available: false, reason: `${bin} not found` };
65
+ }
66
+ if (name === 'atris-fast' && !fs.existsSync(path.join(workspaceDir, 'atris'))) {
67
+ return { available: false, reason: 'workspace root is not an Atris workspace' };
68
+ }
69
+ return { available: true, reason: 'available' };
70
+ },
71
+ run(promptText, workspaceDir, timeoutMs) {
72
+ return spawnEngine(bin, runArgs(promptText, workspaceDir), workspaceDir, timeoutMs);
73
+ },
74
+ };
75
+ }
76
+
77
+ function nullEngineAdapter() {
78
+ return {
79
+ name: 'null',
80
+ available() {
81
+ return { available: true, reason: 'available' };
82
+ },
83
+ run() {
84
+ return { status: 0, stdout: '', stderr: '' };
85
+ },
86
+ };
87
+ }
88
+
89
+ function solutionEngineAdapter(solutionPath) {
90
+ return {
91
+ name: 'solution',
92
+ available() {
93
+ if (!solutionPath || !fs.existsSync(solutionPath)) {
94
+ return { available: false, reason: 'solution.sh not found' };
95
+ }
96
+ return { available: true, reason: 'available' };
97
+ },
98
+ run(_promptText, workspaceDir, timeoutMs) {
99
+ return spawnEngine('/bin/sh', [solutionPath], workspaceDir, timeoutMs);
100
+ },
101
+ };
102
+ }
103
+
104
+ function getEngineAdapter(name, options = {}) {
105
+ const normalized = normalizeEngineName(name);
106
+ if (normalized === 'null') return nullEngineAdapter();
107
+ if (normalized === 'solution') return solutionEngineAdapter(options.solutionPath);
108
+ if (normalized === 'codex') {
109
+ return realEngineAdapter('codex', ENGINE_BINS.codex, (promptText, workspaceDir) => [
110
+ 'exec',
111
+ '--dangerously-bypass-approvals-and-sandbox',
112
+ '-C',
113
+ workspaceDir,
114
+ promptText,
115
+ ]);
116
+ }
117
+ if (normalized === 'cursor') {
118
+ return realEngineAdapter('cursor', ENGINE_BINS.cursor, (promptText) => ['--trust', '-p', promptText]);
119
+ }
120
+ if (normalized === 'claude') {
121
+ return realEngineAdapter('claude', ENGINE_BINS.claude, (promptText) => [
122
+ '-p',
123
+ promptText,
124
+ '--dangerously-skip-permissions',
125
+ ]);
126
+ }
127
+ if (normalized === 'atris-fast') {
128
+ return realEngineAdapter('atris-fast', ENGINE_BINS['atris-fast'], (promptText) => [
129
+ 'chat',
130
+ '--print',
131
+ promptText,
132
+ ]);
133
+ }
134
+ throw new Error(`unknown bench engine: ${name}`);
135
+ }
136
+
137
+ module.exports = {
138
+ ENGINE_NAMES,
139
+ getEngineAdapter,
140
+ normalizeEngineName,
141
+ };
@@ -0,0 +1,140 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const { readResultRecords, taskMetadata } = require('./runner');
5
+
6
+ const REPORT_SCHEMA = 'atris.bench.report.v1';
7
+ const DEFAULT_PACK = 'agents-v1';
8
+ const CATEGORY_ORDER = ['navigate', 'edit', 'contract', 'build', 'recover'];
9
+
10
+ function repoRootFromHere() {
11
+ return path.resolve(__dirname, '..', '..');
12
+ }
13
+
14
+ function isFullRun(record, totalTaskCount) {
15
+ return Array.isArray(record.tasks) && totalTaskCount > 0 && record.tasks.length === totalTaskCount;
16
+ }
17
+
18
+ // Latest full run wins outright; partial runs (from --task filtering) are only
19
+ // merged together when the engine never produced a full run of the pack.
20
+ function effectiveTasksForEngine(records, totalTaskCount) {
21
+ const sorted = [...records].sort((a, b) => new Date(a.started) - new Date(b.started));
22
+ const fullRuns = sorted.filter((record) => isFullRun(record, totalTaskCount));
23
+ if (fullRuns.length) {
24
+ return fullRuns[fullRuns.length - 1].tasks;
25
+ }
26
+ const merged = new Map();
27
+ for (const record of sorted) {
28
+ for (const task of record.tasks || []) {
29
+ merged.set(task.id, task);
30
+ }
31
+ }
32
+ return Array.from(merged.values());
33
+ }
34
+
35
+ function meanDurationMs(tasks) {
36
+ const durations = tasks
37
+ .filter((task) => !task.skipped)
38
+ .map((task) => Number(task.duration_ms) || 0);
39
+ if (!durations.length) return null;
40
+ const sum = durations.reduce((total, value) => total + value, 0);
41
+ return Math.round(sum / durations.length);
42
+ }
43
+
44
+ function buildBenchReport(options = {}) {
45
+ const repoRoot = path.resolve(options.repoRoot || repoRootFromHere());
46
+ const stateRoot = options.stateRoot || process.cwd();
47
+ const pack = options.pack || DEFAULT_PACK;
48
+
49
+ const records = readResultRecords({ stateRoot }).filter((record) => record.pack === pack);
50
+ if (!records.length) {
51
+ return { schema: REPORT_SCHEMA, pack, engines: [] };
52
+ }
53
+
54
+ const taskCategories = new Map();
55
+ let totalTaskCount = 0;
56
+ try {
57
+ const tasks = taskMetadata({ repoRoot, pack });
58
+ totalTaskCount = tasks.length;
59
+ for (const task of tasks) taskCategories.set(task.id, task.category || null);
60
+ } catch (err) {
61
+ // degrade instead of crashing the report: fall back to the largest observed run
62
+ totalTaskCount = records.reduce((max, record) => Math.max(max, (record.tasks || []).length), 0);
63
+ }
64
+
65
+ const byEngine = new Map();
66
+ for (const record of records) {
67
+ const engineName = record.engine || 'unknown';
68
+ if (!byEngine.has(engineName)) byEngine.set(engineName, []);
69
+ byEngine.get(engineName).push(record);
70
+ }
71
+
72
+ const categoryTotals = new Map();
73
+ for (const category of taskCategories.values()) {
74
+ if (!category) continue;
75
+ categoryTotals.set(category, (categoryTotals.get(category) || 0) + 1);
76
+ }
77
+
78
+ const engines = Array.from(byEngine.keys())
79
+ .sort((a, b) => a.localeCompare(b))
80
+ .map((engineName) => {
81
+ const tasks = effectiveTasksForEngine(byEngine.get(engineName), totalTaskCount);
82
+ const passed = tasks.filter((task) => task.passed && !task.skipped);
83
+ const failed = tasks.filter((task) => !task.passed && !task.skipped);
84
+ const skipped = tasks.filter((task) => task.skipped);
85
+
86
+ const categories = CATEGORY_ORDER.map((name) => {
87
+ const categoryTasks = tasks.filter((task) => taskCategories.get(task.id) === name);
88
+ const categoryPassed = categoryTasks.filter((task) => task.passed && !task.skipped);
89
+ return {
90
+ name,
91
+ passed: categoryPassed.length,
92
+ total: categoryTotals.get(name) || 0,
93
+ };
94
+ });
95
+
96
+ return {
97
+ engine: engineName,
98
+ passed: passed.length,
99
+ total: totalTaskCount,
100
+ categories,
101
+ meanDurationMs: meanDurationMs(tasks),
102
+ failed: failed.map((task) => task.id),
103
+ skipped: skipped.length,
104
+ };
105
+ });
106
+
107
+ return { schema: REPORT_SCHEMA, pack, engines };
108
+ }
109
+
110
+ function formatDuration(ms) {
111
+ if (ms === null || ms === undefined) return 'n/a';
112
+ if (ms < 1000) return `${ms}ms`;
113
+ return `${(ms / 1000).toFixed(1)}s`;
114
+ }
115
+
116
+ function renderBenchReportText(report) {
117
+ if (!report.engines.length) {
118
+ return `no runs recorded for pack ${report.pack}`;
119
+ }
120
+ const lines = [`pack: ${report.pack}`, ''];
121
+ for (const engine of report.engines) {
122
+ lines.push(`engine: ${engine.engine}`);
123
+ lines.push(` score ${engine.passed}/${engine.total} passed`);
124
+ for (const category of engine.categories) {
125
+ lines.push(` ${category.name.padEnd(10)} ${category.passed}/${category.total}`);
126
+ }
127
+ lines.push(` mean dur ${formatDuration(engine.meanDurationMs)}`);
128
+ lines.push(` skipped ${engine.skipped}`);
129
+ lines.push(` failed ${engine.failed.length ? engine.failed.join(', ') : 'none'}`);
130
+ lines.push('');
131
+ }
132
+ return lines.join('\n').trimEnd();
133
+ }
134
+
135
+ module.exports = {
136
+ DEFAULT_PACK,
137
+ REPORT_SCHEMA,
138
+ buildBenchReport,
139
+ renderBenchReportText,
140
+ };