atris 3.31.0 → 3.33.3
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.
- package/README.md +46 -4
- package/atris/CLAUDE.md +8 -0
- package/atris.md +8 -0
- package/ax +458 -15
- package/bin/atris.js +200 -76
- package/commands/autoland.js +78 -12
- package/commands/autopilot-front.js +273 -0
- package/commands/engine.js +299 -0
- package/commands/init.js +21 -0
- package/commands/integrations.js +147 -0
- package/commands/member.js +85 -0
- package/commands/mission.js +500 -43
- package/commands/run-front.js +144 -0
- package/commands/run.js +9 -6
- package/commands/sign.js +90 -0
- package/commands/task.js +380 -20
- package/commands/truth.js +72 -11
- package/lib/autoland.js +133 -25
- package/lib/fleet.js +354 -0
- package/lib/inspect-fields.js +174 -0
- package/lib/operator-next.js +7 -0
- package/lib/pulse.js +4 -3
- package/lib/runner-command.js +54 -11
- package/lib/task-db.js +57 -2
- package/package.json +3 -2
- package/scripts/agent_worktree.py +72 -0
package/bin/atris.js
CHANGED
|
@@ -127,8 +127,30 @@ function isOptionValue(args, index, optionNames) {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
function applyRunnerFlags(args) {
|
|
130
|
+
// --engine <name> is the operator-facing spelling of --runner-profile:
|
|
131
|
+
// one flag rents a specific intelligence for this run.
|
|
132
|
+
const engineFlag = readOptionArg(args, '--engine');
|
|
133
|
+
if (engineFlag) {
|
|
134
|
+
const { canonicalEngineName } = require('../commands/engine');
|
|
135
|
+
const { RUNNER_PROFILE_NAMES } = require('../lib/runner-command');
|
|
136
|
+
const canonical = canonicalEngineName(engineFlag);
|
|
137
|
+
if (!canonical) {
|
|
138
|
+
console.error(`Unknown --engine "${engineFlag}". Known engines: ${RUNNER_PROFILE_NAMES.join(', ')}.`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
process.env.ATRIS_RUNNER_PROFILE = canonical;
|
|
142
|
+
}
|
|
130
143
|
const runnerProfile = readOptionArg(args, '--runner-profile');
|
|
131
|
-
if (runnerProfile)
|
|
144
|
+
if (runnerProfile) {
|
|
145
|
+
// Fail fast at the CLI boundary: an unknown profile otherwise stays silent
|
|
146
|
+
// until a heartbeat spawn resolves it mid-loop (silent overnight outage).
|
|
147
|
+
const { RUNNER_PROFILES, RUNNER_PROFILE_NAMES } = require('../lib/runner-command');
|
|
148
|
+
if (!Object.prototype.hasOwnProperty.call(RUNNER_PROFILES, runnerProfile)) {
|
|
149
|
+
console.error(`Unknown --runner-profile "${runnerProfile}". Known profiles: ${RUNNER_PROFILE_NAMES.join(', ')}.`);
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
process.env.ATRIS_RUNNER_PROFILE = runnerProfile;
|
|
153
|
+
}
|
|
132
154
|
const runnerBin = readOptionArg(args, '--runner-bin');
|
|
133
155
|
if (runnerBin) {
|
|
134
156
|
process.env.ATRIS_RUNNER_BIN = runnerBin;
|
|
@@ -144,8 +166,30 @@ function applyRunnerFlags(args) {
|
|
|
144
166
|
process.env.ATRIS_RUNNER_MODEL = runnerModel;
|
|
145
167
|
process.env.ATRIS_CLAUDE_MODEL = runnerModel;
|
|
146
168
|
}
|
|
169
|
+
// No per-run choice and no env: the workspace's saved engine
|
|
170
|
+
// (.atris/engine.json, written by `atris engine <name>`) becomes the
|
|
171
|
+
// profile for every loop spawn. For the loop commands themselves, fall all
|
|
172
|
+
// the way to the house engine (atris-fast when ax is installed) so the
|
|
173
|
+
// default intelligence is our own. Heartbeats stay engine-agnostic without
|
|
174
|
+
// any loop code knowing about engines.
|
|
175
|
+
if (!process.env.ATRIS_RUNNER_PROFILE) {
|
|
176
|
+
try {
|
|
177
|
+
const engine = require('../commands/engine');
|
|
178
|
+
const saved = engine.readSavedEngine();
|
|
179
|
+
if (saved) {
|
|
180
|
+
process.env.ATRIS_RUNNER_PROFILE = saved;
|
|
181
|
+
} else if (RUNNER_SPAWNING_COMMANDS.includes(command)) {
|
|
182
|
+
const resolved = engine.resolveDefaultEngine();
|
|
183
|
+
if (resolved.source === 'house') process.env.ATRIS_RUNNER_PROFILE = resolved.name;
|
|
184
|
+
}
|
|
185
|
+
} catch {}
|
|
186
|
+
}
|
|
147
187
|
}
|
|
148
188
|
|
|
189
|
+
// Commands whose ticks spawn a worker engine; only these pay the engine
|
|
190
|
+
// detection probe when nothing is configured.
|
|
191
|
+
const RUNNER_SPAWNING_COMMANDS = ['run', 'autopilot', 'mission', 'pulse', 'gm', 'spaceship'];
|
|
192
|
+
|
|
149
193
|
const isBusinessSyncSafetyCommand = command === 'sync'
|
|
150
194
|
&& (
|
|
151
195
|
commandArgs.includes('--status')
|
|
@@ -420,11 +464,13 @@ function showHelp() {
|
|
|
420
464
|
console.log('Quick Start:');
|
|
421
465
|
console.log('');
|
|
422
466
|
console.log(' 1. atris Open a persistent AI computer for this workspace');
|
|
467
|
+
console.log(' npx atris Same command after a local project install');
|
|
423
468
|
console.log(' 2. Describe what you want run, built, researched, or validated');
|
|
424
469
|
console.log(' 3. Atris acts with context, memory, tools, and a review loop');
|
|
425
470
|
console.log('');
|
|
426
471
|
console.log('Common invocations:');
|
|
427
|
-
console.log(' atris init');
|
|
472
|
+
console.log(' atris init Global install: initialize this project');
|
|
473
|
+
console.log(' npx atris init Local install: initialize this project');
|
|
428
474
|
console.log(' atris computer');
|
|
429
475
|
console.log(' atris business init "My Company"');
|
|
430
476
|
console.log(' atris run');
|
|
@@ -449,7 +495,7 @@ function showHelp() {
|
|
|
449
495
|
console.log(' plan - Create build spec with visualization');
|
|
450
496
|
console.log(' do - Execute tasks');
|
|
451
497
|
console.log(' review - Validate work (tests, safety checks, docs)');
|
|
452
|
-
console.log(' run -
|
|
498
|
+
console.log(' run - One bounded mission pursuit: start or resume, tick, complete');
|
|
453
499
|
console.log(' run logs - Browse glass run logs (phase reasoning persisted to disk)');
|
|
454
500
|
console.log(' run search - Search phase reasoning across all run logs');
|
|
455
501
|
console.log(' pulse - Durable overnight self-improvement heartbeat (OS cron, install/status/tick)');
|
|
@@ -483,11 +529,13 @@ function showHelp() {
|
|
|
483
529
|
console.log('');
|
|
484
530
|
console.log('Optional helpers:');
|
|
485
531
|
console.log(' brainstorm - Explore ideas conversationally before planning');
|
|
486
|
-
console.log(' autopilot -
|
|
532
|
+
console.log(' autopilot - Keep the workspace moving: mission/member loop until you stop it');
|
|
487
533
|
console.log(' improve - Run one paid RL tick (POST /api/improve, deducts credits)');
|
|
488
534
|
console.log(' worktree - Isolated Git worktrees plus guarded ship/merge for parallel agents');
|
|
489
535
|
console.log(' land - The landing: what is actually done vs still in the air; --reap backs up + clears overdue');
|
|
490
536
|
console.log(' autoland - Approve the policy once; certified work lands itself, you keep irreversible calls');
|
|
537
|
+
console.log(' engine - Bring any intelligence: roster of installed coding CLIs, default engine, --engine per run, `engine test` preflight');
|
|
538
|
+
console.log(' sign - Co-author trailer on every commit in an atris workspace (on/off/status)');
|
|
491
539
|
console.log(' visualize - Generate a Slack/deck-ready visual from a prompt');
|
|
492
540
|
console.log(' youtube - Process YouTube videos with timestamped transcript-first analysis');
|
|
493
541
|
console.log('');
|
|
@@ -547,7 +595,7 @@ function showHelp() {
|
|
|
547
595
|
console.log(' soul - Show, snapshot, or fork workspace identity');
|
|
548
596
|
console.log(' fleet - Inspect local fleet status');
|
|
549
597
|
console.log(' agent - Select cloud agent, spawn worker requests, or run `agent doctor`');
|
|
550
|
-
console.log(' chat - Chat with
|
|
598
|
+
console.log(' chat - Chat with Atris 2 Fast in this workspace (--agent for cloud agent; or: atris chat scan)');
|
|
551
599
|
console.log(' fast - Chat with Atris2 Fast');
|
|
552
600
|
console.log(' login - Sign in or add another account');
|
|
553
601
|
console.log(' logout - Sign out of current account');
|
|
@@ -557,7 +605,7 @@ function showHelp() {
|
|
|
557
605
|
console.log(' accounts - Manage accounts (list, add, remove)');
|
|
558
606
|
console.log('');
|
|
559
607
|
console.log('Integrations:');
|
|
560
|
-
console.log(' gmail - Email commands (inbox, read)');
|
|
608
|
+
console.log(' gmail - Email commands (inbox, read, archive)');
|
|
561
609
|
console.log(' calendar - Calendar commands (today, week)');
|
|
562
610
|
console.log(' twitter - Twitter commands (post)');
|
|
563
611
|
console.log(' slack - Slack commands (channels)');
|
|
@@ -885,7 +933,7 @@ function showAutopilotHelp() {
|
|
|
885
933
|
console.log(' --runner-bin PATH Runner binary for this run');
|
|
886
934
|
console.log(' --runner-template CMD Runner command template for this run');
|
|
887
935
|
console.log(' --runner-model MODEL Runner model for this run');
|
|
888
|
-
console.log(
|
|
936
|
+
console.log(` --runner-profile NAME Runner profile for this run (one of: ${require('../lib/runner-command').RUNNER_PROFILE_NAMES.join(', ')})`);
|
|
889
937
|
console.log('');
|
|
890
938
|
console.log('Examples:');
|
|
891
939
|
console.log(' atris autopilot # Suggest from existing work');
|
|
@@ -927,7 +975,7 @@ const knownCommands = ['init', 'log', 'now', 'radar', 'ctop', 'launchpad', 'stat
|
|
|
927
975
|
'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
976
|
'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',
|
|
929
977
|
'gmail', 'calendar', 'twitter', 'slack', 'imessage', 'integrations', 'setup', 'clean-workspace', 'cw',
|
|
930
|
-
'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'compile', 'spaceship', 'truth'];
|
|
978
|
+
'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'compile', 'spaceship', 'truth', 'sign', 'engine', 'engines'];
|
|
931
979
|
|
|
932
980
|
// Check if command is an atris.md spec file - triggers welcome visualization
|
|
933
981
|
function isSpecFile(cmd) {
|
|
@@ -1049,7 +1097,11 @@ async function interactiveEntry(userInput) {
|
|
|
1049
1097
|
|
|
1050
1098
|
// Fresh install - offer init
|
|
1051
1099
|
if (state.state === 'fresh') {
|
|
1052
|
-
console.log('\nNo atris/ folder found.
|
|
1100
|
+
console.log('\nNo atris/ folder found.');
|
|
1101
|
+
console.log('');
|
|
1102
|
+
console.log('Start here:');
|
|
1103
|
+
console.log(' atris init if Atris is installed globally');
|
|
1104
|
+
console.log(' npx atris init if Atris was installed in this project');
|
|
1053
1105
|
return;
|
|
1054
1106
|
}
|
|
1055
1107
|
|
|
@@ -1831,23 +1883,26 @@ if (command === 'init') {
|
|
|
1831
1883
|
}
|
|
1832
1884
|
if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
|
|
1833
1885
|
console.log('');
|
|
1834
|
-
console.log('Usage: atris run [options]');
|
|
1886
|
+
console.log('Usage: atris run ["objective"] [options]');
|
|
1835
1887
|
console.log('');
|
|
1836
|
-
console.log('
|
|
1837
|
-
console.log('
|
|
1888
|
+
console.log('One bounded mission pursuit: start a mission from the objective');
|
|
1889
|
+
console.log('(or resume the most logical runnable mission), tick it through the');
|
|
1890
|
+
console.log('mission runtime, complete on pass, then exit.');
|
|
1891
|
+
console.log('For an ongoing loop, use: atris autopilot');
|
|
1838
1892
|
console.log('');
|
|
1839
1893
|
console.log('Options:');
|
|
1840
|
-
console.log(' --
|
|
1841
|
-
console.log(' --
|
|
1842
|
-
console.log(' --
|
|
1843
|
-
console.log(' --
|
|
1844
|
-
console.log(' --
|
|
1845
|
-
console.log(' --
|
|
1846
|
-
console.log(' --
|
|
1847
|
-
console.log('
|
|
1848
|
-
console.log('
|
|
1849
|
-
console.log(' --
|
|
1850
|
-
console.log(' --
|
|
1894
|
+
console.log(' --owner NAME Mission owner (default: mission-lead)');
|
|
1895
|
+
console.log(' --minutes N | --hours N Time budget for the pursuit');
|
|
1896
|
+
console.log(' --max-ticks N Override the tick budget');
|
|
1897
|
+
console.log(' --max-wall N Override the wall clock in seconds');
|
|
1898
|
+
console.log(' --cadence C Mission cadence (e.g. 15m)');
|
|
1899
|
+
console.log(' --no-complete Skip auto-complete after a passing run');
|
|
1900
|
+
console.log(' --legacy Old plan→do→review loop (claude -p cycles)');
|
|
1901
|
+
console.log('');
|
|
1902
|
+
console.log('Legacy options (with --legacy):');
|
|
1903
|
+
console.log(' --cycles=N --once --verbose --dry-run --timeout=N --no-push');
|
|
1904
|
+
console.log(' --runner-bin PATH / --runner-template CMD / --runner-model MODEL');
|
|
1905
|
+
console.log(` --runner-profile NAME Runner profile for this run (one of: ${require('../lib/runner-command').RUNNER_PROFILE_NAMES.join(', ')})`);
|
|
1851
1906
|
console.log('');
|
|
1852
1907
|
console.log('Subcommands:');
|
|
1853
1908
|
console.log(' atris run logs [--tail N] [--cat FILE] [--json] Browse glass run logs');
|
|
@@ -1860,62 +1915,81 @@ if (command === 'init') {
|
|
|
1860
1915
|
process.exit(0);
|
|
1861
1916
|
}
|
|
1862
1917
|
|
|
1863
|
-
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
1864
|
-
const dryRun = args.includes('--dry-run');
|
|
1865
|
-
const once = args.includes('--once');
|
|
1866
|
-
const push = !args.includes('--no-push');
|
|
1867
1918
|
applyRunnerFlags(args);
|
|
1868
|
-
const cyclesArg = args.find(a => a.startsWith('--cycles='));
|
|
1869
|
-
const maxCycles = cyclesArg ? parseInt(cyclesArg.split('=')[1]) : 5;
|
|
1870
|
-
const timeoutArg = args.find(a => a.startsWith('--timeout='));
|
|
1871
|
-
const timeout = timeoutArg ? parseInt(timeoutArg.split('=')[1]) * 1000 : undefined;
|
|
1872
1919
|
|
|
1873
|
-
|
|
1874
|
-
.
|
|
1875
|
-
.
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1920
|
+
if (args.includes('--legacy')) {
|
|
1921
|
+
const legacyArgs = args.filter(a => a !== '--legacy');
|
|
1922
|
+
const verbose = legacyArgs.includes('--verbose') || legacyArgs.includes('-v');
|
|
1923
|
+
const dryRun = legacyArgs.includes('--dry-run');
|
|
1924
|
+
const once = legacyArgs.includes('--once');
|
|
1925
|
+
const push = !legacyArgs.includes('--no-push');
|
|
1926
|
+
const cyclesArg = legacyArgs.find(a => a.startsWith('--cycles='));
|
|
1927
|
+
const maxCycles = cyclesArg ? parseInt(cyclesArg.split('=')[1]) : 5;
|
|
1928
|
+
const timeoutArg = legacyArgs.find(a => a.startsWith('--timeout='));
|
|
1929
|
+
const timeout = timeoutArg ? parseInt(timeoutArg.split('=')[1]) * 1000 : undefined;
|
|
1930
|
+
|
|
1931
|
+
require('../commands/run').runAtris({ maxCycles, verbose, dryRun, once, push, timeout })
|
|
1932
|
+
.then(() => process.exit(0))
|
|
1933
|
+
.catch((error) => {
|
|
1934
|
+
console.error(`\u2717 Run failed: ${error.message || error}`);
|
|
1935
|
+
process.exit(1);
|
|
1936
|
+
});
|
|
1937
|
+
} else {
|
|
1938
|
+
require('../commands/run-front').runMissionFront(args)
|
|
1939
|
+
.then((code) => process.exit(code || 0))
|
|
1940
|
+
.catch((error) => {
|
|
1941
|
+
console.error(`\u2717 Run failed: ${error.message || error}`);
|
|
1942
|
+
process.exit(1);
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1879
1945
|
} else if (command === 'launchpad') {
|
|
1880
1946
|
const code = require('../commands/launchpad').launchpadCommand(process.argv.slice(3));
|
|
1881
1947
|
process.exit(code);
|
|
1882
1948
|
} else if (command === 'autopilot') {
|
|
1883
1949
|
const args = process.argv.slice(3);
|
|
1884
|
-
if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
|
|
1885
|
-
showAutopilotHelp();
|
|
1886
|
-
process.exit(0);
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
// Parse options
|
|
1890
|
-
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
1891
|
-
const dryRun = args.includes('--dry-run');
|
|
1892
|
-
const auto = args.includes('--auto');
|
|
1893
1950
|
applyRunnerFlags(args);
|
|
1894
|
-
const maxIterationsArg = args.find(a => a.startsWith('--iterations='));
|
|
1895
|
-
const maxIterations = maxIterationsArg ? parseInt(maxIterationsArg.split('=')[1]) : undefined;
|
|
1896
|
-
const durationArg = args.find(a => a.startsWith('--duration='));
|
|
1897
|
-
const duration = durationArg ? durationArg.split('=')[1] : null;
|
|
1898
|
-
|
|
1899
|
-
// Get description (non-flag args)
|
|
1900
|
-
const description = args.filter((a, i) => !a.startsWith('-') && !isOptionValue(args, i, RUNNER_FLAG_NAMES)).join(' ').trim() || null;
|
|
1901
|
-
|
|
1902
|
-
const options = {
|
|
1903
|
-
...(maxIterations !== undefined && { maxIterations }),
|
|
1904
|
-
verbose,
|
|
1905
|
-
dryRun,
|
|
1906
|
-
auto,
|
|
1907
|
-
duration
|
|
1908
|
-
};
|
|
1909
1951
|
|
|
1910
|
-
|
|
1911
|
-
|
|
1952
|
+
if (args.includes('--legacy')) {
|
|
1953
|
+
const legacyArgs = args.filter(a => a !== '--legacy');
|
|
1954
|
+
if (legacyArgs.includes('--help') || legacyArgs.includes('-h') || legacyArgs[0] === 'help') {
|
|
1955
|
+
showAutopilotHelp();
|
|
1956
|
+
process.exit(0);
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
// Parse options
|
|
1960
|
+
const verbose = legacyArgs.includes('--verbose') || legacyArgs.includes('-v');
|
|
1961
|
+
const dryRun = legacyArgs.includes('--dry-run');
|
|
1962
|
+
const auto = legacyArgs.includes('--auto');
|
|
1963
|
+
const maxIterationsArg = legacyArgs.find(a => a.startsWith('--iterations='));
|
|
1964
|
+
const maxIterations = maxIterationsArg ? parseInt(maxIterationsArg.split('=')[1]) : undefined;
|
|
1965
|
+
const durationArg = legacyArgs.find(a => a.startsWith('--duration='));
|
|
1966
|
+
const duration = durationArg ? durationArg.split('=')[1] : null;
|
|
1967
|
+
|
|
1968
|
+
// Get description (non-flag args)
|
|
1969
|
+
const description = legacyArgs.filter((a, i) => !a.startsWith('-') && !isOptionValue(legacyArgs, i, RUNNER_FLAG_NAMES)).join(' ').trim() || null;
|
|
1970
|
+
|
|
1971
|
+
const options = {
|
|
1972
|
+
...(maxIterations !== undefined && { maxIterations }),
|
|
1973
|
+
verbose,
|
|
1974
|
+
dryRun,
|
|
1975
|
+
auto,
|
|
1976
|
+
duration
|
|
1977
|
+
};
|
|
1912
1978
|
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1979
|
+
require('../commands/autopilot').autopilotAtris(description, options)
|
|
1980
|
+
.then(() => process.exit(0))
|
|
1981
|
+
.catch((error) => {
|
|
1982
|
+
console.error(`✗ Autopilot failed: ${error.message || error}`);
|
|
1983
|
+
process.exit(1);
|
|
1984
|
+
});
|
|
1985
|
+
} else {
|
|
1986
|
+
Promise.resolve(require('../commands/autopilot-front').autopilotFront(args))
|
|
1987
|
+
.then((code) => process.exit(code || 0))
|
|
1988
|
+
.catch((error) => {
|
|
1989
|
+
console.error(`✗ Autopilot failed: ${error.message || error}`);
|
|
1990
|
+
process.exit(1);
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1919
1993
|
} else if (command === 'brainstorm') {
|
|
1920
1994
|
require('../commands/brainstorm').brainstormAtris()
|
|
1921
1995
|
.then(() => process.exit(0))
|
|
@@ -2240,6 +2314,17 @@ if (command === 'init') {
|
|
|
2240
2314
|
Promise.resolve(require('../commands/compile').compileCommand(subcommand, ...args))
|
|
2241
2315
|
.then(() => process.exit(process.exitCode || 0))
|
|
2242
2316
|
.catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
|
|
2317
|
+
} else if (command === 'engine' || command === 'engines') {
|
|
2318
|
+
// Engine: bring any intelligence — roster of installed coding CLIs, default
|
|
2319
|
+
// engine per workspace, --engine <name> rides any loop for one run.
|
|
2320
|
+
try {
|
|
2321
|
+
const engineArgs = command === 'engines' && process.argv.length <= 3 ? [] : process.argv.slice(3);
|
|
2322
|
+
process.exit(require('../commands/engine').engineCommand(engineArgs) || 0);
|
|
2323
|
+
} catch (err) { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); }
|
|
2324
|
+
} else if (command === 'sign') {
|
|
2325
|
+
// Sign: prepare-commit-msg hook that credits Atris as co-author on commits in atris workspaces.
|
|
2326
|
+
try { process.exit(require('../commands/sign').signCommand(process.argv[3]) || 0); }
|
|
2327
|
+
catch (err) { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); }
|
|
2243
2328
|
} else if (command === 'slop') {
|
|
2244
2329
|
// Slop: deterministic frontend-slop detector (no LLM). Exit 1 = slop found, for CI + the autopilot gate.
|
|
2245
2330
|
Promise.resolve(require('../commands/slop').slopCommand(process.argv.slice(3)))
|
|
@@ -2638,18 +2723,21 @@ async function agentAtris() {
|
|
|
2638
2723
|
|
|
2639
2724
|
|
|
2640
2725
|
async function chatAtris() {
|
|
2641
|
-
// Get message from command line args
|
|
2642
|
-
const
|
|
2726
|
+
// Get message from command line args; --agent forces the legacy cloud-agent lane
|
|
2727
|
+
const rawArgs = process.argv.slice(3);
|
|
2728
|
+
const agentLane = rawArgs.includes('--agent');
|
|
2729
|
+
const message = rawArgs.filter(arg => arg !== '--agent').join(' ').trim();
|
|
2643
2730
|
|
|
2644
2731
|
// Respect -h / --help before any auth/state checks
|
|
2645
2732
|
if (message === '-h' || message === '--help' || message === 'help') {
|
|
2646
2733
|
console.log('Usage: atris chat ["message"]');
|
|
2647
2734
|
console.log('');
|
|
2648
|
-
console.log('
|
|
2649
|
-
console.log(' Requires `atris login
|
|
2735
|
+
console.log(' Chat with Atris 2 Fast in this workspace: tools attached, same turn as `ax --fast`.');
|
|
2736
|
+
console.log(' Requires `atris login`.');
|
|
2650
2737
|
console.log('');
|
|
2651
|
-
console.log(' atris chat Interactive
|
|
2652
|
-
console.log(' atris chat "what now?" One-shot message');
|
|
2738
|
+
console.log(' atris chat Interactive chat (ax --fast --chat)');
|
|
2739
|
+
console.log(' atris chat "what now?" One-shot message (ax --fast)');
|
|
2740
|
+
console.log(' atris chat --agent [...] Legacy cloud-agent lane (needs `atris agent`)');
|
|
2653
2741
|
process.exit(0);
|
|
2654
2742
|
}
|
|
2655
2743
|
|
|
@@ -2665,6 +2753,22 @@ async function chatAtris() {
|
|
|
2665
2753
|
process.exit(await runLocalFastMission(missionIntent));
|
|
2666
2754
|
}
|
|
2667
2755
|
|
|
2756
|
+
// Workspace standard v2: `atris chat` is the same turn as `ax --fast`,
|
|
2757
|
+
// Atris 2 Fast with tools attached, ax owning routing (local tool loop,
|
|
2758
|
+
// cloud fallback when no backend listens). The pro-chat cloud-agent lane
|
|
2759
|
+
// survives behind --agent only; it 404s the moment a saved agent is
|
|
2760
|
+
// deleted server-side and cannot see the workspace.
|
|
2761
|
+
if (!agentLane) {
|
|
2762
|
+
try {
|
|
2763
|
+
const axPath = path.join(__dirname, '..', 'ax');
|
|
2764
|
+
const axArgs = message ? ['--fast', message] : ['--fast', '--chat'];
|
|
2765
|
+
const run = spawnSync(process.execPath, [axPath, ...axArgs], { stdio: 'inherit' });
|
|
2766
|
+
process.exit(run.status || 0);
|
|
2767
|
+
} catch {
|
|
2768
|
+
// ax unavailable: fall through to the agent lane.
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2668
2772
|
printAtrisGoalBanner(process.cwd());
|
|
2669
2773
|
|
|
2670
2774
|
// Check agent selected
|
|
@@ -2714,7 +2818,13 @@ async function chatOnce(config, credentials, message) {
|
|
|
2714
2818
|
await streamProChat(endpoint, credentials.token, body);
|
|
2715
2819
|
console.log('\n\n✓ Complete\n');
|
|
2716
2820
|
} catch (error) {
|
|
2717
|
-
|
|
2821
|
+
const detail = String(error.message || error);
|
|
2822
|
+
if (/404/.test(detail) && /agent not found/i.test(detail)) {
|
|
2823
|
+
console.error(`\n✗ Error: Agent "${config.agent_name || agentId}" no longer exists on the server.`);
|
|
2824
|
+
console.error(' Run "atris agent" to pick a new one, or drop --agent to use the fast workspace lane.');
|
|
2825
|
+
} else {
|
|
2826
|
+
console.error(`\n✗ Error: ${detail}`);
|
|
2827
|
+
}
|
|
2718
2828
|
process.exit(1);
|
|
2719
2829
|
}
|
|
2720
2830
|
}
|
|
@@ -2914,6 +3024,20 @@ async function atrisFastChat() {
|
|
|
2914
3024
|
process.exit(await runLocalFastMission(missionIntent));
|
|
2915
3025
|
}
|
|
2916
3026
|
|
|
3027
|
+
// One routing brain: `atris fast` defers to ax's AX Context Standard. A
|
|
3028
|
+
// workspace-shaped question asked from inside a workspace needs local tools
|
|
3029
|
+
// (this lane is a tool-less cloud one-shot and confabulates on repo
|
|
3030
|
+
// questions — SwapBench 2026-07-02), so delegate the whole turn to ax.
|
|
3031
|
+
try {
|
|
3032
|
+
const axModule = require('../ax');
|
|
3033
|
+
if (axModule.resolveRoute(message) === 'local') {
|
|
3034
|
+
const run = spawnSync(process.execPath, [path.join(__dirname, '..', 'ax'), '--fast', message], { stdio: 'inherit' });
|
|
3035
|
+
process.exit(run.status || 0);
|
|
3036
|
+
}
|
|
3037
|
+
} catch {
|
|
3038
|
+
// ax unavailable: fall through to the plain cloud one-shot.
|
|
3039
|
+
}
|
|
3040
|
+
|
|
2917
3041
|
printAtrisGoalBanner(process.cwd());
|
|
2918
3042
|
|
|
2919
3043
|
const ensured = await ensureValidCredentials();
|
package/commands/autoland.js
CHANGED
|
@@ -6,6 +6,7 @@ const os = require('os');
|
|
|
6
6
|
const { spawnSync } = require('child_process');
|
|
7
7
|
|
|
8
8
|
const autoland = require('../lib/autoland');
|
|
9
|
+
const { operatorReady, hasAgentJargon } = autoland;
|
|
9
10
|
|
|
10
11
|
function repoRoot(cwd = process.cwd()) {
|
|
11
12
|
const result = spawnSync('git', ['rev-parse', '--show-toplevel'], { cwd, encoding: 'utf8' });
|
|
@@ -17,6 +18,25 @@ function projectName(root) {
|
|
|
17
18
|
return path.basename(root);
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
// The digest's "next, if you agree" section: top candidate moves from Atris
|
|
22
|
+
// state, each with the member best suited to own it. Moves that can't explain
|
|
23
|
+
// themselves are counted, not shown. Never blocks the digest.
|
|
24
|
+
function digestNextMoves(root) {
|
|
25
|
+
try {
|
|
26
|
+
const { nextMoves } = require('../lib/next-moves');
|
|
27
|
+
const { resolveFunctionalOwner } = require('../lib/functional-owner');
|
|
28
|
+
const all = (nextMoves(root, 5) || []).filter((move) => move && move.title);
|
|
29
|
+
const ready = all.filter((move) => operatorReady(move.title)).slice(0, 3).map((move) => {
|
|
30
|
+
let owner = null;
|
|
31
|
+
try { owner = resolveFunctionalOwner({ title: move.title, root })?.owner || null; } catch {}
|
|
32
|
+
return { title: move.title, owner };
|
|
33
|
+
});
|
|
34
|
+
return { moves: ready, unexplained: all.length - ready.length };
|
|
35
|
+
} catch {
|
|
36
|
+
return { moves: [], unexplained: 0 };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
20
40
|
function flag(args, name, fallback = '') {
|
|
21
41
|
const idx = args.indexOf(name);
|
|
22
42
|
if (idx === -1) return fallback;
|
|
@@ -24,9 +44,11 @@ function flag(args, name, fallback = '') {
|
|
|
24
44
|
}
|
|
25
45
|
|
|
26
46
|
function runOwnCli(root, cliArgs) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
47
|
+
// Always spawn the bin this module shipped with. Resolving through the
|
|
48
|
+
// target root or a global `atris` breaks whenever the tick runs a project
|
|
49
|
+
// that isn't the CLI repo on a machine without a global install (CI, cron).
|
|
50
|
+
const bin = path.resolve(__dirname, '..', 'bin', 'atris.js');
|
|
51
|
+
const result = spawnSync(process.execPath, [bin, ...cliArgs], { cwd: root, encoding: 'utf8', timeout: 300000 });
|
|
30
52
|
return { status: result.status, stdout: String(result.stdout || ''), stderr: String(result.stderr || '') };
|
|
31
53
|
}
|
|
32
54
|
|
|
@@ -186,6 +208,7 @@ function runDigest(root, args, { forceSend = false } = {}) {
|
|
|
186
208
|
waiting: autoland.waitingOnHuman(tasks),
|
|
187
209
|
landed: landSummarySafe(root),
|
|
188
210
|
project: projectName(root),
|
|
211
|
+
nextMoves: digestNextMoves(root),
|
|
189
212
|
});
|
|
190
213
|
console.log(text);
|
|
191
214
|
// the full story: what each piece actually was, in its own words
|
|
@@ -195,10 +218,15 @@ function runDigest(root, args, { forceSend = false } = {}) {
|
|
|
195
218
|
return t && (t.review?.landing?.happened || t.metadata?.landing_happened);
|
|
196
219
|
});
|
|
197
220
|
if (storied.length > 0) {
|
|
198
|
-
|
|
221
|
+
let printedStoryHeader = false;
|
|
199
222
|
for (const item of storied) {
|
|
200
223
|
const t = byRef.get(item.ref);
|
|
201
224
|
const happened = String(t.review?.landing?.happened || t.metadata?.landing_happened || '').replace(/\s+/g, ' ').slice(0, 160);
|
|
225
|
+
if (!operatorReady(happened)) continue;
|
|
226
|
+
if (!printedStoryHeader) {
|
|
227
|
+
console.log('');
|
|
228
|
+
printedStoryHeader = true;
|
|
229
|
+
}
|
|
202
230
|
console.log(` ${item.ref} ${happened}`);
|
|
203
231
|
}
|
|
204
232
|
}
|
|
@@ -221,7 +249,22 @@ function runTick(root, args) {
|
|
|
221
249
|
return 0;
|
|
222
250
|
}
|
|
223
251
|
|
|
224
|
-
// 1.
|
|
252
|
+
// 1. certify what has executable proof — re-run the runnable check named in
|
|
253
|
+
// each Review proof as a second actor. Without this the tick only lands rows
|
|
254
|
+
// some always-on mission happened to certify, and everything else waits on a
|
|
255
|
+
// human who never needed to look. Denied lanes and check-less proofs still wait.
|
|
256
|
+
if (policy.drain_reviews !== false) {
|
|
257
|
+
const certify = runOwnCli(root, ['task', 'certify-verified', '--json']);
|
|
258
|
+
try {
|
|
259
|
+
const parsed = JSON.parse(certify.stdout);
|
|
260
|
+
receipt.reviews_certified = parsed.certified ?? 0;
|
|
261
|
+
if (parsed.ok !== true) receipt.certify_error = 'certify-verified failed';
|
|
262
|
+
} catch {
|
|
263
|
+
receipt.certify_error = certify.stderr.slice(0, 200) || 'certify-verified output unreadable';
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 2. land what is eligible — the policy is the standing authorization
|
|
225
268
|
const cliArgs = ['task', 'auto-accept-certified', '--json', '--limit', '12'];
|
|
226
269
|
if (policy.strict_verify !== false) cliArgs.push('--strict-verify');
|
|
227
270
|
const accept = runOwnCli(root, cliArgs);
|
|
@@ -232,7 +275,23 @@ function runTick(root, args) {
|
|
|
232
275
|
receipt.accept_error = accept.stderr.slice(0, 200) || 'auto-accept output unreadable';
|
|
233
276
|
}
|
|
234
277
|
|
|
235
|
-
//
|
|
278
|
+
// 2b. tell the operator the moment something lands: one text, one landing
|
|
279
|
+
// sentence per piece (the day-one PM sentence written at finish time),
|
|
280
|
+
// capped at three with the rest counted. Off with live_updates: false.
|
|
281
|
+
const tasksForLive = readProjection(root);
|
|
282
|
+
if (receipt.landed.length > 0 && policy.imessage_to && policy.live_updates !== false) {
|
|
283
|
+
const text = autoland.composeLiveUpdate({
|
|
284
|
+
landedRefs: receipt.landed,
|
|
285
|
+
tasks: tasksForLive,
|
|
286
|
+
project: projectName(root),
|
|
287
|
+
});
|
|
288
|
+
if (text) {
|
|
289
|
+
const sent = autoland.sendImessage(root, policy.imessage_to, text);
|
|
290
|
+
receipt.live_update_sent = sent.ok;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 3. alarm on anything waiting on a human past the line
|
|
236
295
|
const state = autoland.readState(root);
|
|
237
296
|
const tasks = readProjection(root);
|
|
238
297
|
const waiting = autoland.waitingOnHuman(tasks);
|
|
@@ -247,7 +306,7 @@ function runTick(root, args) {
|
|
|
247
306
|
}
|
|
248
307
|
}
|
|
249
308
|
|
|
250
|
-
//
|
|
309
|
+
// 4. daily digest at the configured hour
|
|
251
310
|
const today = new Date().toISOString().slice(0, 10);
|
|
252
311
|
const digestHour = Number(policy.digest_hour ?? autoland.DEFAULT_DIGEST_HOUR);
|
|
253
312
|
if (new Date().getHours() === digestHour && state.last_digest_date !== today) {
|
|
@@ -256,6 +315,7 @@ function runTick(root, args) {
|
|
|
256
315
|
waiting,
|
|
257
316
|
landed: landSummarySafe(root),
|
|
258
317
|
project: projectName(root),
|
|
318
|
+
nextMoves: digestNextMoves(root),
|
|
259
319
|
});
|
|
260
320
|
if (policy.imessage_to) {
|
|
261
321
|
const sent = autoland.sendImessage(root, policy.imessage_to, text);
|
|
@@ -264,7 +324,7 @@ function runTick(root, args) {
|
|
|
264
324
|
receipt.digest_text = text;
|
|
265
325
|
state.last_digest_date = today;
|
|
266
326
|
|
|
267
|
-
//
|
|
327
|
+
// 5. once a day, keep the receipt shelf lean: compress old run receipts
|
|
268
328
|
// into the manifest and drop unreferenced clutter, newest 200 kept.
|
|
269
329
|
const prune = runOwnCli(root, ['mission', 'prune-runs', '--apply', '--days', '14', '--keep-newest', '200', '--json']);
|
|
270
330
|
try {
|
|
@@ -278,7 +338,7 @@ function runTick(root, args) {
|
|
|
278
338
|
|
|
279
339
|
if (json) console.log(JSON.stringify(receipt));
|
|
280
340
|
else {
|
|
281
|
-
console.log(`autoland tick: ${receipt.landed.length} landed${receipt.landed.length ? ` (${receipt.landed.join(', ')})` : ''}, ${receipt.alarms} alarms, digest ${receipt.digest_sent ? 'sent' : 'not due'}`);
|
|
341
|
+
console.log(`autoland tick: ${receipt.reviews_certified ?? 0} reviews certified, ${receipt.landed.length} landed${receipt.landed.length ? ` (${receipt.landed.join(', ')})` : ''}, ${receipt.alarms} alarms, digest ${receipt.digest_sent ? 'sent' : 'not due'}`);
|
|
282
342
|
}
|
|
283
343
|
return 0;
|
|
284
344
|
}
|
|
@@ -297,15 +357,21 @@ function showHelp() {
|
|
|
297
357
|
console.log(' on you past 24h');
|
|
298
358
|
console.log(' atris autoland off back to approving every item');
|
|
299
359
|
console.log(' atris autoland digest [--send] the daily message, now');
|
|
300
|
-
console.log(' atris autoland tick
|
|
360
|
+
console.log(' atris autoland tick [--json] one heartbeat (what the hourly cron runs)');
|
|
361
|
+
console.log('');
|
|
362
|
+
console.log('help is always read-only: atris autoland tick --help never lands work.');
|
|
301
363
|
console.log('');
|
|
302
364
|
return 0;
|
|
303
365
|
}
|
|
304
366
|
|
|
367
|
+
function wantsHelp(args = []) {
|
|
368
|
+
return args.some((arg) => arg === 'help' || arg === '--help' || arg === '-h');
|
|
369
|
+
}
|
|
370
|
+
|
|
305
371
|
function autolandCommand(args = []) {
|
|
306
372
|
const [sub, ...rest] = args;
|
|
373
|
+
if (wantsHelp(args)) return showHelp();
|
|
307
374
|
if (!sub || sub.startsWith('--')) return showStatus(repoRoot(), args);
|
|
308
|
-
if (sub === 'help' || sub === '--help' || sub === '-h') return showHelp();
|
|
309
375
|
const root = repoRoot();
|
|
310
376
|
if (sub === 'on') return turnOn(root, rest);
|
|
311
377
|
if (sub === 'off') return turnOff(root);
|
|
@@ -316,4 +382,4 @@ function autolandCommand(args = []) {
|
|
|
316
382
|
return 1;
|
|
317
383
|
}
|
|
318
384
|
|
|
319
|
-
module.exports = { autolandCommand };
|
|
385
|
+
module.exports = { autolandCommand, operatorReady, hasAgentJargon };
|