atris 3.56.0 → 3.56.2
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/atris/skills/engines/SKILL.md +11 -3
- package/atris/skills/x-search/SKILL.md +20 -1
- package/atris/skills/youtube/SKILL.md +51 -43
- package/bin/atris.js +134 -90
- package/commands/auth.js +6 -1
- package/commands/autopilot-front.js +29 -13
- package/commands/brainstorm.js +77 -476
- package/commands/business.js +13 -1
- package/commands/engine.js +7 -0
- package/commands/experiments.js +178 -0
- package/commands/fleet-report.js +2 -2
- package/commands/founder.js +12 -0
- package/commands/human-missions.js +64 -2
- package/commands/init.js +2 -35
- package/commands/integrations.js +100 -0
- package/commands/land.js +53 -23
- package/commands/later.js +52 -0
- package/commands/launchpad.js +45 -10
- package/commands/log.js +79 -30
- package/commands/mission.js +117 -22
- package/commands/next.js +153 -63
- package/commands/now.js +115 -38
- package/commands/recap.js +97 -4
- package/commands/run-front.js +20 -5
- package/commands/spaceship.js +52 -12
- package/commands/status.js +38 -7
- package/commands/task.js +56 -19
- package/commands/terminal.js +5 -5
- package/commands/workflow.js +19 -5
- package/commands/x-search.js +123 -13
- package/commands/youtube.js +941 -36
- package/lib/account-bound.js +7 -0
- package/lib/apply-gate.js +102 -0
- package/lib/config-guard.js +106 -0
- package/lib/context-gatherer.js +55 -8
- package/lib/engine-ask.js +16 -6
- package/lib/engine-registry.js +14 -4
- package/lib/first-minute.js +571 -26
- package/lib/known-commands.js +1 -1
- package/lib/pack-capabilities.js +13 -3
- package/lib/runner-command.js +5 -3
- package/lib/scratch-root.js +44 -0
- package/lib/workspace-scaffold.js +3 -1
- package/package.json +1 -1
- package/utils/auth.js +84 -6
package/commands/land.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { runGit } = require('../lib/git-spawn');
|
|
4
|
+
const { compactErrorPayload, printCliJson } = require('../lib/cli-json');
|
|
4
5
|
const { listWorktrees, statusCounts } = require('./worktree');
|
|
5
6
|
|
|
6
7
|
const DEFAULT_TTL_DAYS = 7;
|
|
@@ -236,14 +237,18 @@ function collectBoard(root, { ttlDays = DEFAULT_TTL_DAYS, staleHours = DEFAULT_S
|
|
|
236
237
|
// merged branch residue.
|
|
237
238
|
function landSummary(cwd = process.cwd(), ttlDays = DEFAULT_TTL_DAYS) {
|
|
238
239
|
const root = repoRoot(cwd);
|
|
239
|
-
if (!root) return null;
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
240
|
+
if (!root || !hasCommits(root)) return null;
|
|
241
|
+
try {
|
|
242
|
+
const board = collectBoard(root, { ttlDays, light: true });
|
|
243
|
+
return {
|
|
244
|
+
branches: board.summary.active + board.summary.due,
|
|
245
|
+
due: board.summary.due,
|
|
246
|
+
stale: board.summary.stale,
|
|
247
|
+
ttlDays,
|
|
248
|
+
};
|
|
249
|
+
} catch {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
247
252
|
}
|
|
248
253
|
|
|
249
254
|
function salvageDir(root) {
|
|
@@ -636,6 +641,40 @@ function readFollowingFlag(args, name, fallback = '') {
|
|
|
636
641
|
return args[idx + 1] || fallback;
|
|
637
642
|
}
|
|
638
643
|
|
|
644
|
+
const EMPTY_LAND = {
|
|
645
|
+
not_a_git_repo: 'this folder is not a git repo yet, so there is nothing to land.',
|
|
646
|
+
no_commits: 'nothing is in the air yet because this folder has no commits.',
|
|
647
|
+
no_base: 'no master or main branch yet, so there is nothing to land.',
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
function emptyLandKindFromError(err) {
|
|
651
|
+
if (err && err.code === 'LAND_NO_BASE') return 'no_base';
|
|
652
|
+
const msg = String(err && err.message || err || '');
|
|
653
|
+
if (/not a git/i.test(msg)) return 'not_a_git_repo';
|
|
654
|
+
if (/no commits/i.test(msg)) return 'no_commits';
|
|
655
|
+
if (/no master\/main/i.test(msg)) return 'no_base';
|
|
656
|
+
return '';
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
function printEmptyLand(kind, json) {
|
|
660
|
+
const reason = EMPTY_LAND[kind] ? kind : 'no_base';
|
|
661
|
+
const detail = EMPTY_LAND[reason];
|
|
662
|
+
if (json) {
|
|
663
|
+
const payload = compactErrorPayload({ reason, detail });
|
|
664
|
+
printCliJson(payload, payload, ['--json']);
|
|
665
|
+
} else {
|
|
666
|
+
console.log(detail);
|
|
667
|
+
}
|
|
668
|
+
return 2;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function finishLandError(err, json) {
|
|
672
|
+
const kind = emptyLandKindFromError(err);
|
|
673
|
+
if (kind) return printEmptyLand(kind, json);
|
|
674
|
+
console.error(String(err && err.message || err).split('\n')[0]);
|
|
675
|
+
return 2;
|
|
676
|
+
}
|
|
677
|
+
|
|
639
678
|
function showHelp() {
|
|
640
679
|
console.log('');
|
|
641
680
|
console.log('atris land: the landing, what is actually done vs still in the air');
|
|
@@ -666,20 +705,14 @@ function landCommand(args = []) {
|
|
|
666
705
|
showHelp();
|
|
667
706
|
return 0;
|
|
668
707
|
}
|
|
708
|
+
const json = args.includes('--json');
|
|
669
709
|
const root = repoRoot();
|
|
670
|
-
if (!root)
|
|
671
|
-
|
|
672
|
-
return 1;
|
|
673
|
-
}
|
|
674
|
-
if (!hasCommits(root)) {
|
|
675
|
-
console.error('no commits yet. fix: git commit --allow-empty -m init');
|
|
676
|
-
return 1;
|
|
677
|
-
}
|
|
710
|
+
if (!root) return printEmptyLand('not_a_git_repo', json);
|
|
711
|
+
if (!hasCommits(root)) return printEmptyLand('no_commits', json);
|
|
678
712
|
const ttlRaw = readFollowingFlag(args, '--ttl', '');
|
|
679
713
|
const ttlParsed = Number(ttlRaw);
|
|
680
714
|
const ttlDays = ttlRaw !== '' && Number.isFinite(ttlParsed) && ttlParsed >= 0 ? ttlParsed : DEFAULT_TTL_DAYS;
|
|
681
715
|
const base = readFollowingFlag(args, '--base', '');
|
|
682
|
-
const json = args.includes('--json');
|
|
683
716
|
|
|
684
717
|
if (args.includes('--reap')) {
|
|
685
718
|
try {
|
|
@@ -688,8 +721,7 @@ function landCommand(args = []) {
|
|
|
688
721
|
else printReceipt(receipt);
|
|
689
722
|
return 0;
|
|
690
723
|
} catch (err) {
|
|
691
|
-
|
|
692
|
-
return 1;
|
|
724
|
+
return finishLandError(err, json);
|
|
693
725
|
}
|
|
694
726
|
}
|
|
695
727
|
|
|
@@ -706,8 +738,7 @@ function landCommand(args = []) {
|
|
|
706
738
|
else printStory(story);
|
|
707
739
|
return 0;
|
|
708
740
|
} catch (err) {
|
|
709
|
-
|
|
710
|
-
return 1;
|
|
741
|
+
return finishLandError(err, json);
|
|
711
742
|
}
|
|
712
743
|
}
|
|
713
744
|
|
|
@@ -717,8 +748,7 @@ function landCommand(args = []) {
|
|
|
717
748
|
else printBoard(board);
|
|
718
749
|
return 0;
|
|
719
750
|
} catch (err) {
|
|
720
|
-
|
|
721
|
-
return 1;
|
|
751
|
+
return finishLandError(err, json);
|
|
722
752
|
}
|
|
723
753
|
}
|
|
724
754
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { argsWantHelp } = require('../lib/noninteractive');
|
|
4
|
+
const {
|
|
5
|
+
laterNotePath,
|
|
6
|
+
listUserVisibleWork,
|
|
7
|
+
personName,
|
|
8
|
+
renderLaterRemember,
|
|
9
|
+
spokenLaterNote,
|
|
10
|
+
writeLaterNote,
|
|
11
|
+
} = require('../lib/first-minute');
|
|
12
|
+
|
|
13
|
+
function laterUsage() {
|
|
14
|
+
return 'Usage: atris later "<sentence>"';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function laterSentence(args = []) {
|
|
18
|
+
return spokenLaterNote((Array.isArray(args) ? args : [])
|
|
19
|
+
.filter((arg) => !String(arg).startsWith('-'))
|
|
20
|
+
.join(' '));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function laterAtris(args = process.argv.slice(3), {
|
|
24
|
+
root = process.cwd(),
|
|
25
|
+
log = console.log,
|
|
26
|
+
} = {}) {
|
|
27
|
+
const list = Array.isArray(args) ? args : [];
|
|
28
|
+
if (argsWantHelp(list)) {
|
|
29
|
+
log(laterUsage());
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
const sentence = laterSentence(list);
|
|
33
|
+
if (!sentence) {
|
|
34
|
+
log(laterUsage());
|
|
35
|
+
return 2;
|
|
36
|
+
}
|
|
37
|
+
writeLaterNote(root, sentence);
|
|
38
|
+
const files = listUserVisibleWork(root);
|
|
39
|
+
log('');
|
|
40
|
+
log(renderLaterRemember({
|
|
41
|
+
person: personName(),
|
|
42
|
+
sentence,
|
|
43
|
+
files,
|
|
44
|
+
root,
|
|
45
|
+
}));
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
laterAtris,
|
|
51
|
+
laterNotePath,
|
|
52
|
+
};
|
package/commands/launchpad.js
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { hasFlag, readFlag } = require('../lib/arg-parser');
|
|
6
|
+
const {
|
|
7
|
+
isFreshWorkspace,
|
|
8
|
+
isKeepWorkingMinute,
|
|
9
|
+
isClaimMinute,
|
|
10
|
+
buildFirstMinute,
|
|
11
|
+
speakFirstMinute,
|
|
12
|
+
speakKeepWorkingMinute,
|
|
13
|
+
} = require('../lib/first-minute');
|
|
6
14
|
|
|
7
15
|
const ACTIVE_MISSION_STATUSES = new Set(['planning', 'running', 'ready']);
|
|
8
16
|
|
|
@@ -185,15 +193,18 @@ function loadTaskProjection(root) {
|
|
|
185
193
|
};
|
|
186
194
|
}
|
|
187
195
|
|
|
188
|
-
function
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
why: 'No atris/ directory exists yet.',
|
|
195
|
-
};
|
|
196
|
+
function hasLiveKeepWorkingRun(root = process.cwd()) {
|
|
197
|
+
try {
|
|
198
|
+
const { pickLiveLocalMission } = require('./mission');
|
|
199
|
+
return Boolean(pickLiveLocalMission(root));
|
|
200
|
+
} catch {
|
|
201
|
+
return false;
|
|
196
202
|
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function chooseNextAction({ actor, tasks, missions, brain, endgame }) {
|
|
206
|
+
// No-room folders belong to first-minute, not factory init.
|
|
207
|
+
// launchpadCommand speaks those two lines before this ranking runs.
|
|
197
208
|
|
|
198
209
|
const ownClaimed = newest(tasks.filter(task => (
|
|
199
210
|
task.status === 'claimed'
|
|
@@ -609,8 +620,32 @@ function launchpadCommand(args = []) {
|
|
|
609
620
|
showLaunchpadHelp();
|
|
610
621
|
return 0;
|
|
611
622
|
}
|
|
612
|
-
const
|
|
613
|
-
|
|
623
|
+
const asJson = hasFlag(args, '--json');
|
|
624
|
+
const root = process.cwd();
|
|
625
|
+
|
|
626
|
+
// Fresh folder: empty talks first-talk. A file already here
|
|
627
|
+
// names that file, same as bare atris. Do not mint a room.
|
|
628
|
+
if (isFreshWorkspace(root)) {
|
|
629
|
+
return speakFirstMinute({ root, fresh: true, asJson });
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Just-minted file folder, nothing running: same two lines as
|
|
633
|
+
// first-minute / the next keep-working ready. After init, next is claim: same
|
|
634
|
+
// two lines as bare atris / status / now. Not factory init.
|
|
635
|
+
// A live mission still gets the board. --json on the claim
|
|
636
|
+
// path keeps the factory card for scripts.
|
|
637
|
+
if (!hasLiveKeepWorkingRun(root)) {
|
|
638
|
+
const minute = buildFirstMinute({ root });
|
|
639
|
+
if (isKeepWorkingMinute(minute)) {
|
|
640
|
+
return speakKeepWorkingMinute({ root, asJson });
|
|
641
|
+
}
|
|
642
|
+
if (!asJson && isClaimMinute(minute)) {
|
|
643
|
+
return speakFirstMinute({ root });
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const payload = collectLaunchpad(root, args);
|
|
648
|
+
if (asJson) {
|
|
614
649
|
console.log(JSON.stringify(payload, null, 2));
|
|
615
650
|
} else {
|
|
616
651
|
process.stdout.write(renderLaunchpad(payload));
|
package/commands/log.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const readline = require('readline');
|
|
4
4
|
const { getLogPath, ensureLogDirectory, createLogFile, addInboxIdea } = require('../lib/file-ops');
|
|
5
|
+
const { isFreshWorkspace, speakFirstMinute } = require('../lib/first-minute');
|
|
5
6
|
const { isForcedNonInteractive } = require('../lib/noninteractive');
|
|
6
7
|
|
|
7
8
|
function readPipedStdin() {
|
|
@@ -12,42 +13,89 @@ function readPipedStdin() {
|
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
function
|
|
16
|
-
|
|
16
|
+
function journalRel(logFile) {
|
|
17
|
+
return (path.relative(process.cwd(), logFile) || logFile).split(path.sep).join('/');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function printLogJson(payload) {
|
|
21
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
22
|
+
}
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
function printReplNeeded(asJson, dateFormatted, rel) {
|
|
25
|
+
const error = `Daily log REPL needs a terminal (${dateFormatted}).`;
|
|
26
|
+
if (asJson) {
|
|
27
|
+
printLogJson({
|
|
28
|
+
ok: false,
|
|
29
|
+
error,
|
|
30
|
+
journal: rel,
|
|
31
|
+
next_command: 'atris log "note"',
|
|
32
|
+
});
|
|
33
|
+
return;
|
|
21
34
|
}
|
|
35
|
+
console.log(error);
|
|
36
|
+
console.log(`journal: ${rel}`);
|
|
37
|
+
console.log('Next: atris log --repl # in a terminal, or atris log "note" / atris wish --json --no-mission');
|
|
38
|
+
}
|
|
22
39
|
|
|
23
|
-
|
|
24
|
-
|
|
40
|
+
function captureNotes(logFile, notes) {
|
|
41
|
+
return notes.map((note) => addInboxIdea(logFile, note));
|
|
42
|
+
}
|
|
25
43
|
|
|
26
|
-
|
|
27
|
-
|
|
44
|
+
function printCapture(asJson, ids, notes, rel) {
|
|
45
|
+
if (asJson) {
|
|
46
|
+
const payload = {
|
|
47
|
+
ok: true,
|
|
48
|
+
action: 'inbox_capture',
|
|
49
|
+
journal: rel,
|
|
50
|
+
next_command: 'atris logs',
|
|
51
|
+
};
|
|
52
|
+
if (notes.length === 1) {
|
|
53
|
+
payload.id = `I${ids[0]}`;
|
|
54
|
+
payload.note = notes[0];
|
|
55
|
+
} else {
|
|
56
|
+
payload.count = notes.length;
|
|
57
|
+
payload.ids = ids.map((id) => `I${id}`);
|
|
58
|
+
payload.notes = notes;
|
|
59
|
+
}
|
|
60
|
+
printLogJson(payload);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (notes.length === 1) {
|
|
64
|
+
console.log(`captured I${ids[0]}: ${notes[0]}`);
|
|
65
|
+
console.log(`journal: ${rel}`);
|
|
66
|
+
console.log('Next: atris logs');
|
|
67
|
+
return;
|
|
28
68
|
}
|
|
69
|
+
console.log(`captured ${notes.length} note${notes.length === 1 ? '' : 's'} in ${rel}`);
|
|
70
|
+
}
|
|
29
71
|
|
|
72
|
+
function logAtris() {
|
|
73
|
+
const root = process.cwd();
|
|
30
74
|
const args = process.argv.slice(3);
|
|
31
|
-
const
|
|
75
|
+
const asJson = args.includes('--json');
|
|
32
76
|
const forced = isForcedNonInteractive(args);
|
|
33
77
|
const wantsRepl = args.includes('--repl');
|
|
34
78
|
const positional = args.filter((arg) => !String(arg).startsWith('-'));
|
|
35
79
|
const note = positional.join(' ').trim();
|
|
36
80
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
console.log(`journal: ${rel}`);
|
|
42
|
-
console.log('Next: atris logs');
|
|
43
|
-
return;
|
|
81
|
+
// Empty folder talks like bare atris. Do not create atris/ or write
|
|
82
|
+
// a journal. After init, a note still captures.
|
|
83
|
+
if (isFreshWorkspace(root)) {
|
|
84
|
+
process.exit(speakFirstMinute({ root, fresh: true, asJson }));
|
|
44
85
|
}
|
|
45
86
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
87
|
+
ensureLogDirectory();
|
|
88
|
+
const { logFile, dateFormatted } = getLogPath();
|
|
89
|
+
|
|
90
|
+
if (!fs.existsSync(logFile)) {
|
|
91
|
+
createLogFile(logFile, dateFormatted);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const rel = journalRel(logFile);
|
|
95
|
+
|
|
96
|
+
// `atris log "sentence"` (or one slug-like word) appends to today's Inbox.
|
|
97
|
+
if (note && !wantsRepl) {
|
|
98
|
+
printCapture(asJson, captureNotes(logFile, [note]), [note], rel);
|
|
51
99
|
return;
|
|
52
100
|
}
|
|
53
101
|
|
|
@@ -56,16 +104,17 @@ function logAtris() {
|
|
|
56
104
|
const piped = readPipedStdin();
|
|
57
105
|
const lines = piped.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
58
106
|
const notes = lines.filter((line) => line.toLowerCase() !== 'exit');
|
|
59
|
-
if (notes.length
|
|
60
|
-
|
|
61
|
-
console.log(`journal: ${rel}`);
|
|
62
|
-
console.log('Next: atris log --repl # in a terminal, or atris log "note" / atris logs --json');
|
|
107
|
+
if (notes.length > 0 && !wantsRepl) {
|
|
108
|
+
printCapture(asJson, captureNotes(logFile, notes), notes, rel);
|
|
63
109
|
return;
|
|
64
110
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
111
|
+
printReplNeeded(asJson, dateFormatted, rel);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Forced headless: never open a REPL.
|
|
116
|
+
if (forced) {
|
|
117
|
+
printReplNeeded(asJson, dateFormatted, rel);
|
|
69
118
|
return;
|
|
70
119
|
}
|
|
71
120
|
|
package/commands/mission.js
CHANGED
|
@@ -983,6 +983,62 @@ function hasLocalMissionState(root = process.cwd()) {
|
|
|
983
983
|
.some((mission) => mission && mission.id && mission.cloud !== true);
|
|
984
984
|
}
|
|
985
985
|
|
|
986
|
+
const LIVE_MISSION_TICK_MS = 24 * 60 * 60 * 1000;
|
|
987
|
+
|
|
988
|
+
function isLiveInFlightMission(mission, now = Date.now(), root = process.cwd()) {
|
|
989
|
+
if (!mission || !mission.id || mission.cloud === true) return false;
|
|
990
|
+
const status = String(mission.status || '');
|
|
991
|
+
if (TERMINAL_STATUSES.has(status) || status === 'paused' || status === 'blocked') return false;
|
|
992
|
+
if (status === 'running') return true;
|
|
993
|
+
const lastTickAt = Date.parse(mission.last_tick_at || '');
|
|
994
|
+
if (!Number.isFinite(lastTickAt)) return false;
|
|
995
|
+
const age = now - lastTickAt;
|
|
996
|
+
if (!(age >= 0 && age <= LIVE_MISSION_TICK_MS)) return false;
|
|
997
|
+
// ready/planning with a recent receipt is still parked unless a driver is
|
|
998
|
+
// alive. A stalled ready row is the archive, not the live card.
|
|
999
|
+
return missionDriverHealth(mission, root).alive === true;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
function pickLiveLocalMission(root = process.cwd()) {
|
|
1003
|
+
return listMissions(root).find((mission) => isLiveInFlightMission(mission, Date.now(), root)) || null;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function wantsBareMissionArchive(args = []) {
|
|
1007
|
+
return hasFlag(args, '--all')
|
|
1008
|
+
&& args.every((value) => value === '--all' || value === '--json');
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
function missionStatusRef(args = []) {
|
|
1012
|
+
return stripKnownFlags(args, ['--status', '--limit'], ['--json', '--local', '--all'])[0] || '';
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function wantsSpokenMissionStatus(args = []) {
|
|
1016
|
+
if (hasFlag(args, '--all') || hasFlag(args, '--local') || hasFlag(args, '--cloud')) return false;
|
|
1017
|
+
if (readFlag(args, '--status', '') || readFlag(args, '--limit', '')) return false;
|
|
1018
|
+
return !missionStatusRef(args);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function speakMissionDoor(args = [], root = resolveWorkspaceRoot()) {
|
|
1022
|
+
const asJson = args.includes('--json');
|
|
1023
|
+
const live = pickLiveLocalMission(root);
|
|
1024
|
+
if (live) {
|
|
1025
|
+
return statusMission(asJson ? [live.id, '--json'] : [live.id]);
|
|
1026
|
+
}
|
|
1027
|
+
if (isFreshWorkspace(root) || hasLocalMissionState(root)) {
|
|
1028
|
+
const code = speakFirstMinute({
|
|
1029
|
+
root,
|
|
1030
|
+
fresh: isFreshWorkspace(root),
|
|
1031
|
+
asJson,
|
|
1032
|
+
});
|
|
1033
|
+
process.exitCode = code;
|
|
1034
|
+
return code;
|
|
1035
|
+
}
|
|
1036
|
+
return require('./human-missions').currentMissionCommand(args, {
|
|
1037
|
+
root,
|
|
1038
|
+
fallbackOnMissing: true,
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
|
|
986
1042
|
function terminalNextAction(status) {
|
|
987
1043
|
if (status === 'complete') return 'mission complete';
|
|
988
1044
|
if (status === 'stopped') return 'mission stopped';
|
|
@@ -4811,9 +4867,16 @@ function statusMission(args) {
|
|
|
4811
4867
|
console.log('Use --status active for planning, running, ready, paused, and blocked missions.');
|
|
4812
4868
|
return;
|
|
4813
4869
|
}
|
|
4870
|
+
const root = resolveWorkspaceRoot();
|
|
4871
|
+
if (isFreshWorkspace(root) && !missionStatusRef(args)) {
|
|
4872
|
+
return speakMissionDoor(args, root);
|
|
4873
|
+
}
|
|
4874
|
+
if (wantsSpokenMissionStatus(args)) {
|
|
4875
|
+
return speakMissionDoor(args, root);
|
|
4876
|
+
}
|
|
4814
4877
|
const asJson = wantsJson(args);
|
|
4815
4878
|
const localOnly = hasFlag(args, '--local');
|
|
4816
|
-
const ref =
|
|
4879
|
+
const ref = missionStatusRef(args);
|
|
4817
4880
|
const statusFilter = readFlag(args, '--status', '');
|
|
4818
4881
|
if (statusFilter && !VALID_STATUSES.has(statusFilter) && !STATUS_ALIASES.has(statusFilter)) {
|
|
4819
4882
|
exitMissionError(`Invalid --status: ${statusFilter}`, 2, asJson);
|
|
@@ -4871,7 +4934,7 @@ function statusMission(args) {
|
|
|
4871
4934
|
...missionStatusLandingLines(mission.last_landing),
|
|
4872
4935
|
...(completionGateLabel(mission.completion_gate) ? [` gate: ${completionGateLabel(mission.completion_gate)}`] : []),
|
|
4873
4936
|
])
|
|
4874
|
-
: ['No missions yet.
|
|
4937
|
+
: ['No missions yet.'],
|
|
4875
4938
|
asJson,
|
|
4876
4939
|
);
|
|
4877
4940
|
}
|
|
@@ -9014,7 +9077,7 @@ async function parseAndValidateMissionRunPhase(args) {
|
|
|
9014
9077
|
error: null,
|
|
9015
9078
|
};
|
|
9016
9079
|
if (hasFlag(args, '--help') || hasFlag(args, '-h')) {
|
|
9017
|
-
|
|
9080
|
+
console.log('Usage: atris mission run <id|objective> [--max-ticks 4] [--max-wall 3600]');
|
|
9018
9081
|
context.handled = true;
|
|
9019
9082
|
return context;
|
|
9020
9083
|
}
|
|
@@ -10933,10 +10996,36 @@ async function goalLoopMission(args) {
|
|
|
10933
10996
|
if (completedTaskClosed) process.exitCode = 2;
|
|
10934
10997
|
}
|
|
10935
10998
|
|
|
10936
|
-
function help() {
|
|
10999
|
+
function help(args = []) {
|
|
11000
|
+
if (hasFlag(args, '--full')) {
|
|
11001
|
+
helpFull();
|
|
11002
|
+
return;
|
|
11003
|
+
}
|
|
11004
|
+
console.log(`
|
|
11005
|
+
Usage: atris mission
|
|
11006
|
+
|
|
11007
|
+
Keep working on one goal.
|
|
11008
|
+
|
|
11009
|
+
atris mission what's in front of you
|
|
11010
|
+
atris mission start begin one goal
|
|
11011
|
+
atris mission status same as atris mission
|
|
11012
|
+
atris mission stop stop the live goal
|
|
11013
|
+
atris mission list every saved goal
|
|
11014
|
+
atris mission inspect <id> --fields status,runner,ack,pings
|
|
11015
|
+
|
|
11016
|
+
Hours of keep-working: atris spaceship
|
|
11017
|
+
Keep going until you stop: atris autopilot
|
|
11018
|
+
|
|
11019
|
+
More flags: atris mission help --full
|
|
11020
|
+
`.trim());
|
|
11021
|
+
}
|
|
11022
|
+
|
|
11023
|
+
function helpFull() {
|
|
10937
11024
|
console.log(`
|
|
10938
11025
|
atris mission - durable goal + loop + owner + proof state
|
|
10939
11026
|
|
|
11027
|
+
atris mission Same next as bare atris, or the one live mission
|
|
11028
|
+
atris mission list | --all Full mission archive
|
|
10940
11029
|
atris mission start "<objective>" --owner <member> [--destination "<text>"] [--verify "..."] [--always-on] [--budget quick|long|deep] [--xp-task] [--worktree] [--take-goal-slot]
|
|
10941
11030
|
[--runner manual|claude|atris2|codex_goal] [--model <id>]
|
|
10942
11031
|
(runner claude spawns local claude -p per tick, --model passes through;
|
|
@@ -11372,23 +11461,20 @@ function missionCommand(args) {
|
|
|
11372
11461
|
if (args[0] === 'answer') {
|
|
11373
11462
|
return require('./human-missions').answerCommand(args.slice(1));
|
|
11374
11463
|
}
|
|
11375
|
-
if (isBareMission
|
|
11464
|
+
if (isBareMission) {
|
|
11465
|
+
// Empty folder talks like bare atris. A live in-flight mission shows
|
|
11466
|
+
// that one card. Completed, stopped, stalled, or ready-with-no-driver
|
|
11467
|
+
// rows stay in the archive. After init with no live mission, speak the
|
|
11468
|
+
// desk next.
|
|
11469
|
+
return speakMissionDoor(args);
|
|
11470
|
+
}
|
|
11471
|
+
if (wantsBareMissionArchive(args)) {
|
|
11376
11472
|
const root = resolveWorkspaceRoot();
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
if (isFreshWorkspace(root)) {
|
|
11380
|
-
const code = speakFirstMinute({
|
|
11381
|
-
root,
|
|
11382
|
-
fresh: true,
|
|
11383
|
-
asJson: args.includes('--json'),
|
|
11384
|
-
});
|
|
11385
|
-
process.exitCode = code;
|
|
11386
|
-
return code;
|
|
11387
|
-
}
|
|
11388
|
-
return require('./human-missions').currentMissionCommand(args);
|
|
11473
|
+
if (isFreshWorkspace(root)) return speakMissionDoor(args, root);
|
|
11474
|
+
return statusMission(args);
|
|
11389
11475
|
}
|
|
11390
|
-
const subcommand =
|
|
11391
|
-
const rest =
|
|
11476
|
+
const subcommand = args[0] || 'status';
|
|
11477
|
+
const rest = args.slice(1);
|
|
11392
11478
|
// Every mission verb resolves its state store from process.cwd(). Running one
|
|
11393
11479
|
// from a subdirectory used to create a nested .atris store the fleet never
|
|
11394
11480
|
// reads (proven footgun: a nested .atris appeared under
|
|
@@ -11404,8 +11490,6 @@ function missionCommand(args) {
|
|
|
11404
11490
|
case 'new':
|
|
11405
11491
|
return startMission(rest);
|
|
11406
11492
|
case 'status':
|
|
11407
|
-
case 'list':
|
|
11408
|
-
case 'ls':
|
|
11409
11493
|
case 'show':
|
|
11410
11494
|
case 'info':
|
|
11411
11495
|
case 'view':
|
|
@@ -11416,6 +11500,16 @@ function missionCommand(args) {
|
|
|
11416
11500
|
});
|
|
11417
11501
|
}
|
|
11418
11502
|
return statusMission(rest);
|
|
11503
|
+
case 'list':
|
|
11504
|
+
case 'ls': {
|
|
11505
|
+
if (hasFlag(rest, '--help') || hasFlag(rest, '-h') || String(rest[0] || '').trim() === 'help') {
|
|
11506
|
+
console.log('Usage: atris mission list [--status <state>] [--limit <n>] [--local] [--json]');
|
|
11507
|
+
return;
|
|
11508
|
+
}
|
|
11509
|
+
const listRoot = resolveWorkspaceRoot();
|
|
11510
|
+
if (isFreshWorkspace(listRoot)) return speakMissionDoor(rest, listRoot);
|
|
11511
|
+
return statusMission(rest.includes('--all') ? rest : ['--all', ...rest]);
|
|
11512
|
+
}
|
|
11419
11513
|
case 'doctor':
|
|
11420
11514
|
case 'check':
|
|
11421
11515
|
return doctorMission(rest);
|
|
@@ -11469,7 +11563,7 @@ function missionCommand(args) {
|
|
|
11469
11563
|
case 'help':
|
|
11470
11564
|
case '--help':
|
|
11471
11565
|
case '-h':
|
|
11472
|
-
return help();
|
|
11566
|
+
return help(rest);
|
|
11473
11567
|
default: {
|
|
11474
11568
|
const first = String(subcommand || '');
|
|
11475
11569
|
if (first && !first.startsWith('-')) {
|
|
@@ -11505,6 +11599,7 @@ module.exports = {
|
|
|
11505
11599
|
reapPausedMissions,
|
|
11506
11600
|
missionHeartbeatLines,
|
|
11507
11601
|
listMissions,
|
|
11602
|
+
pickLiveLocalMission,
|
|
11508
11603
|
freezeMissionVerifier,
|
|
11509
11604
|
markMissionReviewReady,
|
|
11510
11605
|
listWorktreeRollupMissions,
|