atris 3.74.3 → 3.74.5
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/FOR_AGENTS.md +12 -12
- package/atris/AGENTS.md +7 -7
- package/atris/CLAUDE.md +10 -10
- package/atris/policies/atris-backend.md +1 -1
- package/atris/policies/atris-design.md +71 -71
- package/atris/policies/design-seed.md +28 -28
- package/atris/policies/writing.md +1 -1
- package/atris/skills/apps/SKILL.md +3 -3
- package/atris/skills/atris-feedback/SKILL.md +2 -2
- package/atris/skills/autopilot/SKILL.md +10 -10
- package/atris/skills/blocks/SKILL.md +16 -16
- package/atris/skills/calendar/SKILL.md +1 -1
- package/atris/skills/clawhub/atris/SKILL.md +4 -4
- package/atris/skills/clawhub/philosophy-of-work/SKILL.md +7 -7
- package/atris/skills/create-app/SKILL.md +18 -18
- package/atris/skills/create-member/SKILL.md +11 -11
- package/atris/skills/design/SKILL.md +1 -1
- package/atris/skills/drive/SKILL.md +25 -25
- package/atris/skills/email-agent/SKILL.md +11 -11
- package/atris/skills/endgame/SKILL.md +33 -33
- package/atris/skills/magic-inbox/SKILL.md +15 -15
- package/atris/skills/notion/SKILL.md +1 -1
- package/atris/skills/ramp/SKILL.md +7 -7
- package/atris/skills/research-search/SKILL.md +7 -7
- package/atris/skills/slides/SKILL.md +7 -7
- package/atris/skills/tidy/SKILL.md +11 -11
- package/atris/skills/wake/SKILL.md +11 -11
- package/atris/skills/writing/SKILL.md +5 -5
- package/atris/team/_template/SOUL.md +3 -3
- package/atris/team/brainstormer/MEMBER.md +2 -2
- package/atris/team/launcher/MEMBER.md +2 -2
- package/atris/team/mission-lead/MISSION.md +7 -7
- package/atris/team/opus-overnight/MISSION.md +16 -16
- package/atris/team/opus-overnight/STEERING.md +5 -5
- package/atris.md +9 -0
- package/bin/atris.js +16 -3
- package/commands/campaign.js +17 -2
- package/commands/card.js +19 -12
- package/commands/engine.js +92 -17
- package/commands/gm.js +30 -0
- package/commands/idea.js +263 -0
- package/commands/init.js +11 -2
- package/commands/land.js +39 -18
- package/commands/mission.js +4 -3
- package/commands/ninety-nine.js +288 -0
- package/commands/one-lap.js +46 -27
- package/commands/reel.js +12 -7
- package/commands/sync.js +10 -2
- package/lib/campaign-keep.js +23 -8
- package/lib/campaign-loop.js +24 -5
- package/lib/claude-boot-block.js +7 -4
- package/lib/engine-registry.js +139 -1
- package/lib/fleet.js +11 -2
- package/lib/gm-bet.js +338 -34
- package/lib/gm-phone-page.js +325 -33
- package/lib/gm-phone.js +5 -1
- package/lib/gm-season.js +14 -0
- package/lib/headless-chrome.js +39 -0
- package/lib/idea-gate.js +33 -0
- package/lib/idea-inbox.js +220 -0
- package/lib/idea-ledger.js +196 -0
- package/lib/idea-tick.js +384 -0
- package/lib/known-commands.js +1 -1
- package/lib/member-engine.js +1 -0
- package/lib/ninety-nine-sync.js +156 -0
- package/lib/ninety-nine.js +247 -0
- package/lib/opening-lines.js +94 -0
- package/lib/roster-markdown.js +39 -1
- package/lib/roster-models.js +1 -1
- package/lib/roster-swap.js +353 -0
- package/lib/spawn-env.js +29 -2
- package/lib/wish-audit.js +14 -0
- package/lib/yes-lock.js +358 -241
- package/lib/zen-apply.js +5 -3
- package/lib/zen.js +13 -1
- package/package.json +1 -1
- package/templates/business-starter/CLAUDE.md +7 -7
- package/templates/business-starter/MAP.md +1 -1
- package/templates/business-starter/TODO.md +1 -1
- package/templates/business-starter/atris.md +2 -2
- package/templates/business-starter/context/README.md +6 -6
- package/templates/business-starter/context/live-workspace.md +1 -1
- package/templates/business-starter/goals.md +1 -1
- package/templates/business-starter/instructions.md +1 -1
- package/templates/business-starter/memory.md +4 -4
- package/templates/business-starter/persona.md +3 -3
- package/templates/business-starter/policies/LESSONS.md +1 -1
- package/templates/business-starter/reports/README.md +1 -1
- package/templates/business-starter/skills/README.md +1 -1
- package/templates/business-starter/team/README.md +1 -1
- package/templates/business-starter/team/START_HERE.md +1 -1
- package/templates/business-starter/team/_template/SOUL.md +3 -3
- package/templates/business-starter/team/comms/SOUL.md +2 -2
- package/templates/business-starter/team/ops/SOUL.md +4 -4
- package/templates/business-starter/team/research/SOUL.md +3 -3
- package/templates/business-starter/wiki/index.md +1 -1
- package/templates/research-canonical/CLAUDE.md +7 -7
- package/templates/research-canonical/MAP.md +1 -1
- package/templates/research-canonical/TODO.md +1 -1
- package/templates/research-canonical/atris.md +2 -2
- package/templates/research-canonical/context/README.md +8 -8
- package/templates/research-canonical/context/live-workspace.md +1 -1
- package/templates/research-canonical/goals.md +2 -2
- package/templates/research-canonical/instructions.md +1 -1
- package/templates/research-canonical/memory.md +4 -4
- package/templates/research-canonical/persona.md +1 -1
- package/templates/research-canonical/policies/LESSONS.md +1 -1
- package/templates/research-canonical/reports/README.md +1 -1
- package/templates/research-canonical/skills/README.md +1 -1
- package/templates/research-canonical/team/README.md +1 -1
- package/templates/research-canonical/team/eval/SOUL.md +1 -1
- package/templates/research-canonical/team/experiment/SOUL.md +2 -2
- package/templates/research-canonical/team/hypothesis/SOUL.md +3 -3
- package/templates/research-canonical/team/literature/SOUL.md +2 -2
- package/templates/research-canonical/wiki/briefs/research-program.md +1 -1
- package/templates/research-canonical/wiki/index.md +1 -1
- package/utils/auth.js +1 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# Mission
|
|
1
|
+
# Mission: opus-overnight
|
|
2
2
|
|
|
3
3
|
<!-- Human-authored purpose file. Keep this durable; runtime state belongs in .atris/state/*.jsonl and now.md. -->
|
|
4
4
|
|
|
5
5
|
## North Star
|
|
6
6
|
|
|
7
|
-
**AGI is the mission.** Concretely, the path is: the best model that works with Atris first (Atris-shaped, customer-trajectory-driven, vertically RL'd), then we swap the base for a better one when the loop justifies it. The loop, the data, and the published number are the moat
|
|
7
|
+
**AGI is the mission.** Concretely, the path is: the best model that works with Atris first (Atris-shaped, customer-trajectory-driven, vertically RL'd), then we swap the base for a better one when the loop justifies it. The loop, the data, and the published number are the moat, not the base model.
|
|
8
8
|
|
|
9
9
|
The motto: **every tick, name the 1%.** Each tick must answer "what's 1% better than last tick, and on what axis?" If a tick can't name its 1%, log it as a no-lift tick honestly and pick a different artifact.
|
|
10
10
|
|
|
@@ -16,25 +16,25 @@ The motto: **every tick, name the 1%.** Each tick must answer "what's 1% better
|
|
|
16
16
|
|
|
17
17
|
ALL of:
|
|
18
18
|
1. One concrete artifact landed in `rl-exp2/` (script, doc, eval, dataset, comparison) on a tick-prefixed commit.
|
|
19
|
-
2. **The tick summary names its 1
|
|
19
|
+
2. **The tick summary names its 1%: what got better, by how much, vs what baseline.** Examples: "+1 training mode unlocked (DPO)", "−1 ambiguity in publishable-number rubric", "+5pp clarity on which base model swap is right", "no-lift tick: tried X, didn't move forward, here's what we learned."
|
|
20
20
|
3. `npm test` in atris-cli still green if any atris-cli code touched (usually none).
|
|
21
21
|
4. A mission-tick receipt recorded via `atris mission tick <id> --summary "Tick N: ... · 1%: <delta>"` so the operator sees the delta in `atris mission status` without reading my chat.
|
|
22
|
-
5. **Zero dollars spent during the tick
|
|
22
|
+
5. **Zero dollars spent during the tick**, no firectl create/fire/scale, no GPU rentals, no atrisos-backend master commits.
|
|
23
23
|
|
|
24
24
|
## Bounded Scope
|
|
25
25
|
|
|
26
|
-
- Code/docs in `rl-exp2
|
|
27
|
-
- Reads from `atrisos-backend/backend/rl/` and `atris/features/rl-gym
|
|
28
|
-
- Supabase reads via the existing trajectory_service shape
|
|
29
|
-
- `firectl list / get / whoami / show
|
|
26
|
+
- Code/docs in `rl-exp2/`: write freely.
|
|
27
|
+
- Reads from `atrisos-backend/backend/rl/` and `atris/features/rl-gym/`: write findings into `rl-exp2/notes/`, never modify the backend without explicit approval.
|
|
28
|
+
- Supabase reads via the existing trajectory_service shape: never insert/update.
|
|
29
|
+
- `firectl list / get / whoami / show`: read-only fine; `create / scale / delete` forbidden.
|
|
30
30
|
|
|
31
31
|
## Domains In Order
|
|
32
32
|
|
|
33
|
-
1. **Evaluator surface
|
|
34
|
-
2. **Corpus
|
|
35
|
-
3. **Base-model swap analysis
|
|
36
|
-
4. **Strategy doc
|
|
37
|
-
5. **rl-gym artifact survey
|
|
33
|
+
1. **Evaluator surface**: heuristic v1 shipped (commit `ae14e32`); LLM-judge v2 next.
|
|
34
|
+
2. **Corpus**: build_sft_corpus.py shipped (commit `bd171fb`); add eval-prompts companion + a dry-run sample dataset for review.
|
|
35
|
+
3. **Base-model swap analysis**: Kimi K2 vs Qwen3.5-30B vs GLM-4.5-Air vs GPT-OSS-20B on Fireworks: cost, throughput, RFT shape support.
|
|
36
|
+
4. **Strategy doc**: smallest publishable number that lets Atris claim "top RL co for vertical workspaces."
|
|
37
|
+
5. **rl-gym artifact survey**: 192 commits, 166 proof artifacts already in atrisos-backend. Map them so future ticks reuse instead of rebuild.
|
|
38
38
|
|
|
39
39
|
## Stop Condition
|
|
40
40
|
|
|
@@ -51,11 +51,11 @@ ALL of:
|
|
|
51
51
|
|
|
52
52
|
## Search Discipline (anti-laziness rule)
|
|
53
53
|
|
|
54
|
-
When the operator says "X exists in our code" or "we have X" and a grep returns 0 hits
|
|
54
|
+
When the operator says "X exists in our code" or "we have X" and a grep returns 0 hits, **do NOT conclude X doesn't exist.** That conclusion is a recurring failure mode (see `atris/lessons.md` slug `narrow-grep-hides-matches`).
|
|
55
55
|
|
|
56
56
|
The required search ladder:
|
|
57
|
-
1. `grep -rn "<term>" <repo
|
|
57
|
+
1. `grep -rn "<term>" <repo>/`: whole repo, no subdir gating
|
|
58
58
|
2. If still nothing, try variants: case-insensitive (`-i`), alternative spellings, related terms
|
|
59
|
-
3. Only after BOTH (1) and (2) miss, surface "I can't find it
|
|
59
|
+
3. Only after BOTH (1) and (2) miss, surface "I can't find it, point me at where" rather than declaring absence
|
|
60
60
|
|
|
61
61
|
Operator's trust is more reliable than my regex. Trust them, then go find it.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# STEERING
|
|
1
|
+
# STEERING: opus-overnight
|
|
2
2
|
|
|
3
3
|
<!-- Operator drops one-line directives here while the autonomous loop runs.
|
|
4
4
|
Each tick reads this file FIRST, applies any unapplied directive as
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
<!-- Drop one-line directives below. Format: `- YYYY-MM-DD: <directive>`.
|
|
10
10
|
Examples and usage live in rl-exp2/notes/operator-steering.md.
|
|
11
|
-
Do NOT put examples here
|
|
11
|
+
Do NOT put examples here: every `- ` line under `## Pending` is read
|
|
12
12
|
as a real directive on the next tick. -->
|
|
13
13
|
|
|
14
14
|
## Applied
|
|
@@ -28,8 +28,8 @@ The autonomous loop runs without the operator present. They can't redirect mid-t
|
|
|
28
28
|
|
|
29
29
|
This is the lightest layer of the four-tier feedback design:
|
|
30
30
|
- **Layer 1 (this file):** taste tweaks, one-line drops
|
|
31
|
-
- **Layer 2:** `atris brain edit <tick> "..."
|
|
32
|
-
- **Layer 3:** `atris mission steer <id> --note "..."
|
|
33
|
-
- **Layer 4:** edit MISSION.md / MEMBER.md
|
|
31
|
+
- **Layer 2:** `atris brain edit <tick> "..."`: thumbs-up/down on past ticks
|
|
32
|
+
- **Layer 3:** `atris mission steer <id> --note "..."`: directives attached to mission state (not yet implemented)
|
|
33
|
+
- **Layer 4:** edit MISSION.md / MEMBER.md: durable contract changes
|
|
34
34
|
|
|
35
35
|
Use the lightest tier that fits. Most steering belongs in Layer 1.
|
package/atris.md
CHANGED
|
@@ -55,6 +55,15 @@ In a project with nothing on the list yet, speak first: three things you noticed
|
|
|
55
55
|
|
|
56
56
|
You can move fast. You do not get to move blindly.
|
|
57
57
|
|
|
58
|
+
We have done a lot of great work, and because of it we will do even more.
|
|
59
|
+
|
|
60
|
+
- **Acceleration.** Progress compounds. The early days are slow, then the curve bends and everything speeds up. Build on what is already done and settled; never redo it or re-ask it. Each landed piece makes the next one faster.
|
|
61
|
+
- **Accuracy.** A win counts when the result is validated: a test, a gate, or a real run proves it. Small fast tasks can be proven by a check alone; good and big work also gets a second reviewer. Accuracy is the floor the other three stand on, so better tests make everything faster.
|
|
62
|
+
- **Ambition.** Always one more level. When the goal has a clear check, go for the bigger version or the great task. We are capable of it, and we always find ways to win.
|
|
63
|
+
- **Agency.** Take action and own the outcome. Use every tool you have. When something is missing or broken, fix it the same turn instead of handing it back.
|
|
64
|
+
|
|
65
|
+
Acknowledge each validated win, then go for the next one.
|
|
66
|
+
|
|
58
67
|
Scale ceremony to blast radius. Before a nontrivial change (new surface,
|
|
59
68
|
multi-file, migrations, anything hard to roll back), state:
|
|
60
69
|
- the goal
|
package/bin/atris.js
CHANGED
|
@@ -143,8 +143,8 @@ const helpRequested = updateCommand === 'help'
|
|
|
143
143
|
const jsonRequested = process.argv.slice(2).includes('--json');
|
|
144
144
|
const dryRunRequested = updateArgs.includes('--dry-run');
|
|
145
145
|
const skipUpdateCheck = Boolean(process.env.ATRIS_SKIP_UPDATE_CHECK || process.env.NO_UPDATE_NOTIFIER || helpRequested || jsonRequested);
|
|
146
|
-
//
|
|
147
|
-
if (!skipUpdateCheck && (!updateCommand || (updateCommand && !['version', 'update', 'mcp', 'recap'].includes(updateCommand)))) {
|
|
146
|
+
// Local-only commands make no registry call and start no background install.
|
|
147
|
+
if (!skipUpdateCheck && (!updateCommand || (updateCommand && !['version', 'update', 'mcp', 'recap', '99'].includes(updateCommand)))) {
|
|
148
148
|
updateCheckPromise = checkForUpdates()
|
|
149
149
|
.then((updateInfo) => {
|
|
150
150
|
if (updateInfo) {
|
|
@@ -528,6 +528,7 @@ function showHelpShort() {
|
|
|
528
528
|
console.log('');
|
|
529
529
|
console.log(' atris "what do you want here?" empty folder');
|
|
530
530
|
console.log(' atris later "..." remember without a room');
|
|
531
|
+
console.log(' atris idea "..." drop an idea; atris builds it later');
|
|
531
532
|
console.log(' atris guide "..." translate your words');
|
|
532
533
|
console.log(' atris do work already here');
|
|
533
534
|
console.log(' atris done land what your agent built');
|
|
@@ -563,6 +564,7 @@ function showHelpAll() {
|
|
|
563
564
|
console.log('you say what you want. already won. one next step.');
|
|
564
565
|
console.log('');
|
|
565
566
|
console.log(' atris later "..." remember without a room');
|
|
567
|
+
console.log(' atris idea "..." drop an idea; atris builds it later');
|
|
566
568
|
console.log(' atris guide "..." translate your words');
|
|
567
569
|
console.log(' atris do work already here');
|
|
568
570
|
console.log(' atris done land what your agent built');
|
|
@@ -600,6 +602,7 @@ function showHelpAll() {
|
|
|
600
602
|
console.log(' log - Add ideas to inbox (`atris log "note"`; `log --repl` for the editor; see also `wish`)');
|
|
601
603
|
console.log(' wish - Say one plain sentence, then Atris asks only for gaps or delegates it');
|
|
602
604
|
console.log(' now - Show atris/now.md, the current operating truth');
|
|
605
|
+
console.log(' 99 - focus, meditation, health, and optional sync');
|
|
603
606
|
console.log(' goal - goal, distance, and what moved today (alias: wtf)');
|
|
604
607
|
console.log(' founder - show weekly commit and completed-task improvement from receipts');
|
|
605
608
|
console.log(' orb - Pick next moves while engine jobs work in the background');
|
|
@@ -608,7 +611,7 @@ function showHelpAll() {
|
|
|
608
611
|
console.log(' who - Show local engines and team members working, waiting, done, or stale');
|
|
609
612
|
console.log(' stream - Watch the whole team work live in one terminal');
|
|
610
613
|
console.log(' team - One team view: members, roles, engine assignments (presence for live)');
|
|
611
|
-
console.log(' gm - Play your team as a front office: ratings, stuck jobs, league, replay, phone');
|
|
614
|
+
console.log(' gm - Play your team as a front office: ratings, stuck jobs, league, replay, phone, walk');
|
|
612
615
|
console.log(' host - The Host: gets to know people, makes two-yes introductions (atris member install host)');
|
|
613
616
|
console.log(' watch - Turn one sentence into an always-on background watcher');
|
|
614
617
|
console.log(' ctop - Show a process-first live agent CPU/memory view');
|
|
@@ -2288,6 +2291,9 @@ if (command === 'guide') {
|
|
|
2288
2291
|
console.error(error.message || String(error));
|
|
2289
2292
|
process.exit(1);
|
|
2290
2293
|
}
|
|
2294
|
+
} else if (command === '99') {
|
|
2295
|
+
Promise.resolve(require('../commands/ninety-nine').ninetyNineAtris(process.argv.slice(3)))
|
|
2296
|
+
.then((code) => exitAfterStdoutDrain(code));
|
|
2291
2297
|
} else if (command === 'now') {
|
|
2292
2298
|
const args = process.argv.slice(3);
|
|
2293
2299
|
const nowCode = require('../commands/now').nowAtris(args);
|
|
@@ -2298,6 +2304,13 @@ if (command === 'guide') {
|
|
|
2298
2304
|
} else if (command === 'later') {
|
|
2299
2305
|
const laterCode = require('../commands/later').laterAtris(process.argv.slice(3));
|
|
2300
2306
|
process.exit(Number.isInteger(laterCode) ? laterCode : 0);
|
|
2307
|
+
} else if (command === 'idea') {
|
|
2308
|
+
Promise.resolve(require('../commands/idea').ideaAtris(process.argv.slice(3))).then((ideaCode) => {
|
|
2309
|
+
process.exit(Number.isInteger(ideaCode) ? ideaCode : 0);
|
|
2310
|
+
}, (error) => {
|
|
2311
|
+
console.error(`atris idea: ${error && error.message || error}`);
|
|
2312
|
+
process.exit(1);
|
|
2313
|
+
});
|
|
2301
2314
|
} else if (command === 'done') {
|
|
2302
2315
|
const doneCode = require('../commands/done').doneAtris(process.argv.slice(3));
|
|
2303
2316
|
process.exit(Number.isInteger(doneCode) ? doneCode : 0);
|
package/commands/campaign.js
CHANGED
|
@@ -33,7 +33,7 @@ function printHelp() {
|
|
|
33
33
|
console.log(' atris campaign start "<title>" --judge "<cmd>" [--budget <dollars>] no --budget means no limit; --cloud needs one');
|
|
34
34
|
console.log(' [--holdout <file>] move held-out data out of the repo; the judge reads it from $ATRIS_CAMPAIGN_HOLDOUT');
|
|
35
35
|
console.log(' [--owner <member>] [--seat <member>[:<role>]]... [--bet "<bet>" [--wrong-if "<what proves it wrong>"]]');
|
|
36
|
-
console.log(' [--standing] [--parent <id>] [--id cmp-<name>] [--cloud [--basis atris_cloud]] [--protect <path>]... [--run]');
|
|
36
|
+
console.log(' [--standing] [--parent <id>] [--id cmp-<name>] [--cloud [--basis atris_cloud]] [--protect <path>]... [--brief <file>] [--run]');
|
|
37
37
|
console.log(' --protect saves paths agents must not touch, and the commit they start from');
|
|
38
38
|
console.log(' --basis atris_cloud: Atris runs it on your Atris computer and bills its engines; the default is your own engines');
|
|
39
39
|
console.log(' atris campaign show <id> bet, judge, answer, budget, seats, missions, asks');
|
|
@@ -89,7 +89,7 @@ const NO_LIMIT_BUDGET = 1e9;
|
|
|
89
89
|
const noLimit = (view) => view.budget.total >= NO_LIMIT_BUDGET;
|
|
90
90
|
|
|
91
91
|
// The flags this command understands, so positional words are what is left.
|
|
92
|
-
const VALUE_FLAGS = ['--mission-minutes', '--basis', '--work', '--break', '--rounds', '--mission', '--parallel', '--max-steps', '--max-ticks', '--runner', '--count', '--judge', '--budget', '--owner', '--seat', '--bet', '--wrong-if', '--parent', '--id', '--role', '--add', '--data', '--timeout', '--as', '--holdout', '--protect', '--every', '--steps'];
|
|
92
|
+
const VALUE_FLAGS = ['--mission-minutes', '--basis', '--work', '--break', '--rounds', '--mission', '--parallel', '--max-steps', '--max-ticks', '--runner', '--count', '--judge', '--budget', '--owner', '--seat', '--bet', '--wrong-if', '--parent', '--id', '--role', '--add', '--data', '--timeout', '--as', '--holdout', '--protect', '--brief', '--every', '--steps'];
|
|
93
93
|
|
|
94
94
|
function positionals(args) {
|
|
95
95
|
const out = [];
|
|
@@ -389,6 +389,14 @@ async function cmdStart(args, ctx) {
|
|
|
389
389
|
const protectPlan = protect.length ? planProtect(process.cwd(), protect) : null;
|
|
390
390
|
if (protectPlan && protectPlan.error) return ctx.fail(protectPlan.error);
|
|
391
391
|
|
|
392
|
+
const briefFlag = readFlag(args, '--brief', '');
|
|
393
|
+
let briefText = null;
|
|
394
|
+
if (briefFlag) {
|
|
395
|
+
const from = path.resolve(briefFlag);
|
|
396
|
+
if (!fs.existsSync(from) || !fs.statSync(from).isFile()) return ctx.fail(`--brief ${briefFlag} is not a file`);
|
|
397
|
+
briefText = fs.readFileSync(from, 'utf8');
|
|
398
|
+
}
|
|
399
|
+
|
|
392
400
|
const holdoutFlag = readFlag(args, '--holdout', '');
|
|
393
401
|
let held = null;
|
|
394
402
|
if (holdoutFlag) {
|
|
@@ -425,6 +433,12 @@ async function cmdStart(args, ctx) {
|
|
|
425
433
|
if (parent) ctx.append(id, 'attached', { parent_id: parent });
|
|
426
434
|
ctx.append(id, 'judge_ran', judgeRanData(reading), 'judge');
|
|
427
435
|
if (protectPlan) saveProtect(ctx.campaignHome, id, protectPlan);
|
|
436
|
+
if (briefText !== null) {
|
|
437
|
+
const { briefPath } = require('../lib/campaign-loop');
|
|
438
|
+
const target = briefPath(process.cwd(), id);
|
|
439
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
440
|
+
fs.writeFileSync(target, briefText);
|
|
441
|
+
}
|
|
428
442
|
|
|
429
443
|
let cloud = null;
|
|
430
444
|
if (args.includes('--cloud')) {
|
|
@@ -800,6 +814,7 @@ async function driveLoop(args, ctx, verb, view) {
|
|
|
800
814
|
log: ctx.asJson ? () => {} : (line) => console.log(line),
|
|
801
815
|
judge: (command) => ctx.judge(command, JUDGE_TIMEOUT_SECONDS, view.id),
|
|
802
816
|
lessons: (id) => require('../lib/campaign-cadence').readLessons(process.cwd(), id),
|
|
817
|
+
brief: (id) => require('../lib/campaign-loop').readBrief(process.cwd(), id),
|
|
803
818
|
...(sync ? { sync } : {}),
|
|
804
819
|
...(ctx.loopDeps || {}),
|
|
805
820
|
});
|
package/commands/card.js
CHANGED
|
@@ -11,6 +11,7 @@ const path = require('path');
|
|
|
11
11
|
const os = require('os');
|
|
12
12
|
const { execFileSync, spawnSync } = require('child_process');
|
|
13
13
|
const { buildCard, SIZES, KINDS } = require('../lib/card');
|
|
14
|
+
const { makeProfileDir, removeProfileDir, headlessArgs } = require('../lib/headless-chrome');
|
|
14
15
|
|
|
15
16
|
function parseFlags(argv) {
|
|
16
17
|
const flags = {}; const pos = [];
|
|
@@ -46,18 +47,24 @@ function findChrome() {
|
|
|
46
47
|
|
|
47
48
|
function renderPng(chrome, html, width, height, outPng) {
|
|
48
49
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'atris-card-'));
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
const profile = makeProfileDir('atris-card-chrome-');
|
|
51
|
+
try {
|
|
52
|
+
const htmlFile = path.join(tmp, 'card.html');
|
|
53
|
+
fs.writeFileSync(htmlFile, html);
|
|
54
|
+
const args = headlessArgs(profile, [
|
|
55
|
+
'--disable-gpu', '--hide-scrollbars',
|
|
56
|
+
'--force-device-scale-factor=2',
|
|
57
|
+
`--window-size=${width},${height}`,
|
|
58
|
+
'--virtual-time-budget=4000',
|
|
59
|
+
`--screenshot=${outPng}`,
|
|
60
|
+
`file://${htmlFile}`,
|
|
61
|
+
]);
|
|
62
|
+
execFileSync(chrome, args, { stdio: 'ignore', timeout: 60000 });
|
|
63
|
+
return fs.existsSync(outPng);
|
|
64
|
+
} finally {
|
|
65
|
+
removeProfileDir(profile);
|
|
66
|
+
removeProfileDir(tmp);
|
|
67
|
+
}
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
function run(argv) {
|
package/commands/engine.js
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
// atris engine roster confirm renew every dated worker for 30 days
|
|
12
12
|
// atris engine roster session [clear] this shell's own changes
|
|
13
13
|
// atris engine roster --available tools and models on this machine
|
|
14
|
+
// atris engine roster set <job> <tool> <model> swap a job's lead; the old lead backs it up
|
|
15
|
+
// atris engine roster set --member <name> <tool> <model> give one member its own model
|
|
14
16
|
// atris engine assign <job> <tool> [--like <kind>] [--model <m>] [--effort <level>] [--max "20 min"] [--prep <job>] [--backup "<tool> [model]"] [--days <n>] [--add] [--session | --everywhere]
|
|
15
17
|
// atris engine assign <job> --promote "<tool> [model]" | --remove <tool> | --clear [--session | --everywhere]
|
|
16
18
|
// jobs are open-ended: search, build, review, or your own ("small build")
|
|
@@ -35,11 +37,12 @@ const {
|
|
|
35
37
|
const { parseScopeFlag } = require('../lib/cli-scope');
|
|
36
38
|
const { isFreshWorkspace, speakFirstMinute } = require('../lib/first-minute');
|
|
37
39
|
const { teamRosterView } = require('../lib/member-engine');
|
|
38
|
-
const { engineModelText, engineRunsView,
|
|
40
|
+
const { engineModelText, engineRunsView, modelLabel } = require('../lib/roster-models');
|
|
39
41
|
const { renderTable } = require('../lib/text-table');
|
|
40
42
|
const { TEAM_FOOTER } = require('../lib/team-lineup');
|
|
41
43
|
const { readRosterRuns, workerRuns, summarizeRuns, recentRuns, renderRunLine, runView, DEFAULT_DAYS: RUN_DAYS } = require('../lib/roster-runs');
|
|
42
44
|
const { attachSuggestions } = require('../lib/roster-suggest');
|
|
45
|
+
const { swapList, swapJob, swapMember } = require('../lib/roster-swap');
|
|
43
46
|
const { PIN_MAX_AGE_DAYS: PIN_STALE_DAYS, attachStaleModels, modelCatalog } = require('../lib/roster-stale');
|
|
44
47
|
const {
|
|
45
48
|
ENGINE_ROLES,
|
|
@@ -48,7 +51,7 @@ const {
|
|
|
48
51
|
ROSTER_LAYERS,
|
|
49
52
|
rosterDecided,
|
|
50
53
|
rosterWorkerWalk,
|
|
51
|
-
|
|
54
|
+
rosterToolName,
|
|
52
55
|
rosterSessionKey,
|
|
53
56
|
clearSessionRoster,
|
|
54
57
|
NO_SESSION_MESSAGE,
|
|
@@ -1399,18 +1402,17 @@ function printJobRoster(root, now = new Date()) {
|
|
|
1399
1402
|
console.log(renderRosterReport(rosterReport(root, now)));
|
|
1400
1403
|
}
|
|
1401
1404
|
|
|
1402
|
-
//
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
.filter((engine) => !HIDDEN_PROFILE_NAMES.includes(engine.id))
|
|
1406
|
-
.filter((engine) => engine.installed || (engine.health && engine.health.status === 'ready'));
|
|
1407
|
-
return availableModels(engines).map((row) => ({ ...row, tool: rosterToolLabel(row.engine) }));
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
function renderAvailable(rows) {
|
|
1405
|
+
// With the owner's picks: the swap list on top (atris engines, then the
|
|
1406
|
+
// picks), then every model under "more models". Without: every tool's list.
|
|
1407
|
+
function renderAvailable(rows, list = null) {
|
|
1411
1408
|
if (!rows.length) return 'no tools found on this machine. run atris engine doctor to check again';
|
|
1412
|
-
const
|
|
1413
|
-
|
|
1409
|
+
const top = list && list.picked ? list.options.filter((option) => !option.more) : [];
|
|
1410
|
+
const width = Math.max(...rows.map((row) => row.tool.length), ...top.map((option) => option.tool.length));
|
|
1411
|
+
const full = rows.map((row) => `${row.tool.padEnd(width)} ${row.models.join(', ') || 'its own default'} (${row.source})`);
|
|
1412
|
+
if (!top.length) return ['on this machine', ...full].join('\n');
|
|
1413
|
+
const labels = Math.max(...top.map((option) => option.label.length));
|
|
1414
|
+
const picks = top.map((option) => `${option.tool.padEnd(width)} ${option.label.padEnd(labels)} ${option.atris ? 'atris' : 'pick'}`);
|
|
1415
|
+
return ['on this machine', ...picks, '', 'more models', ...full].join('\n');
|
|
1414
1416
|
}
|
|
1415
1417
|
|
|
1416
1418
|
// Only this session's changes, from its own file.
|
|
@@ -1518,7 +1520,78 @@ function runRosterRunsCommand(job, json, root, now) {
|
|
|
1518
1520
|
return 0;
|
|
1519
1521
|
}
|
|
1520
1522
|
|
|
1523
|
+
const ROSTER_SET_USAGE = 'usage: atris engine roster set <job> <tool> <model>, or atris engine roster set --member <name> <tool> <model>. the tools and models come from atris engine roster --available';
|
|
1524
|
+
|
|
1525
|
+
// The words after set: the job (one quoted argument, or every word before
|
|
1526
|
+
// the first one that names a tool), the tool (one word, or two like "claude
|
|
1527
|
+
// code"), then the model, which may be several words.
|
|
1528
|
+
function splitSetWords(words, needJob) {
|
|
1529
|
+
// "claude code" and "atris fast" are two words; every other tool is one.
|
|
1530
|
+
const toolAt = (i) => {
|
|
1531
|
+
const two = i + 1 < words.length ? `${words[i]} ${words[i + 1]}` : '';
|
|
1532
|
+
if (two && rosterToolNameOf(two)) return { at: i, size: 2 };
|
|
1533
|
+
return rosterToolNameOf(words[i]) ? { at: i, size: 1 } : null;
|
|
1534
|
+
};
|
|
1535
|
+
let start = 0;
|
|
1536
|
+
let job = '';
|
|
1537
|
+
if (needJob) {
|
|
1538
|
+
if (!words.length) return null;
|
|
1539
|
+
if (/\s/.test(String(words[0]).trim())) {
|
|
1540
|
+
job = String(words[0]).trim();
|
|
1541
|
+
start = 1;
|
|
1542
|
+
} else {
|
|
1543
|
+
let found = null;
|
|
1544
|
+
for (let i = 1; i < words.length && !found; i += 1) found = toolAt(i);
|
|
1545
|
+
if (!found) return null;
|
|
1546
|
+
job = words.slice(0, found.at).join(' ');
|
|
1547
|
+
start = found.at;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
const tool = toolAt(start);
|
|
1551
|
+
if (!tool) return words.length > start ? { job, tool: words[start], model: words.slice(start + 1).join(' ') } : null;
|
|
1552
|
+
return { job, tool: words.slice(start, start + tool.size).join(' '), model: words.slice(start + tool.size).join(' ') };
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
function rosterToolNameOf(words) {
|
|
1556
|
+
return rosterToolName(String(words || '').trim());
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
function runRosterSetCommand(args, root) {
|
|
1560
|
+
const json = args.includes('--json');
|
|
1561
|
+
const words = args.filter((arg) => arg !== '--json');
|
|
1562
|
+
let member = '';
|
|
1563
|
+
const at = words.indexOf('--member');
|
|
1564
|
+
if (at !== -1) {
|
|
1565
|
+
member = words[at + 1] || '';
|
|
1566
|
+
words.splice(at, 2);
|
|
1567
|
+
if (!member || member.startsWith('--')) {
|
|
1568
|
+
console.error(ROSTER_SET_USAGE);
|
|
1569
|
+
return 2;
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
if (words.some((word) => String(word).startsWith('--'))) {
|
|
1573
|
+
console.error(ROSTER_SET_USAGE);
|
|
1574
|
+
return 2;
|
|
1575
|
+
}
|
|
1576
|
+
const parts = splitSetWords(words, !member);
|
|
1577
|
+
if (!parts || (!member && !parts.job)) {
|
|
1578
|
+
console.error(ROSTER_SET_USAGE);
|
|
1579
|
+
return 2;
|
|
1580
|
+
}
|
|
1581
|
+
try {
|
|
1582
|
+
const out = member
|
|
1583
|
+
? swapMember(root, member, parts.tool, parts.model)
|
|
1584
|
+
: swapJob(root, parts.job, parts.tool, parts.model);
|
|
1585
|
+
console.log(json ? JSON.stringify(out, null, 2) : out.line);
|
|
1586
|
+
return 0;
|
|
1587
|
+
} catch (err) {
|
|
1588
|
+
console.error(err.message);
|
|
1589
|
+
return 2;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1521
1593
|
function runRosterCommand(args, root, now = new Date()) {
|
|
1594
|
+
if (args[0] === 'set') return runRosterSetCommand(args.slice(1), root);
|
|
1522
1595
|
const json = args.includes('--json');
|
|
1523
1596
|
const available = args.includes('--available');
|
|
1524
1597
|
const runsAt = args.indexOf('--runs');
|
|
@@ -1536,12 +1609,14 @@ function runRosterCommand(args, root, now = new Date()) {
|
|
|
1536
1609
|
|| (rest.length === 1 && ['confirm', 'session'].includes(rest[0]))
|
|
1537
1610
|
|| (rest.length === 2 && rest[0] === 'session' && rest[1] === 'clear');
|
|
1538
1611
|
if (!known || (available && rest.length)) {
|
|
1539
|
-
console.error('usage: atris engine roster [confirm | session [clear] | --available | --runs [job]] [--json]');
|
|
1612
|
+
console.error('usage: atris engine roster [confirm | session [clear] | set <job> <tool> <model> | set --member <name> <tool> <model> | --available | --runs [job]] [--json]');
|
|
1540
1613
|
return 2;
|
|
1541
1614
|
}
|
|
1542
1615
|
if (available) {
|
|
1543
|
-
|
|
1544
|
-
|
|
1616
|
+
// the same ordered list the phone's engine card reads
|
|
1617
|
+
const list = swapList(root);
|
|
1618
|
+
const rows = list.tools;
|
|
1619
|
+
console.log(json ? JSON.stringify({ tools: rows, list: list.options }, null, 2) : renderAvailable(rows, list));
|
|
1545
1620
|
return 0;
|
|
1546
1621
|
}
|
|
1547
1622
|
if (rest[0] === 'session') return runRosterSessionCommand(rest, json, root, now);
|
|
@@ -2548,7 +2623,7 @@ function engineCommand(args = [], deps = {}) {
|
|
|
2548
2623
|
if (sub === 'help' || args.includes('--help') || args.includes('-h')) {
|
|
2549
2624
|
console.log('\n atris engine watch [<id>|latest] [--no-follow]\n follow one live transcript or list running engine work');
|
|
2550
2625
|
console.log('\n long read-only asks: atris engine ask "..." --engine agy --timeout <seconds>\n quick asks default to 120 seconds; explicit jobs allow up to 3600\n follow live work with atris engine watch latest');
|
|
2551
|
-
console.log('\n atris engine roster + current default\n atris engine roster [--json] show every job and its workers in order: search, build, review, and your own\n flags a model with a newer version on this machine, or a pin not proven in 30 days\n (a landed run, or "verified 2026-09-30" on its line, proves it)\n atris engine roster confirm renew every dated worker for 30 days\n atris engine roster session [clear]\n show or drop the roster changes for this shell only\n atris engine roster --available\n tools on this machine and the models each one offers\n atris engine roster --runs [job]\n the last 20 runs, newest first, with tool, model, time, and outcome\n atris engine assign <job> <tool> [--like <kind>] [--model <m>] [--effort <level>] [--max "20 min"] [--prep <job>] [--backup "<tool> [model]"] [--days <n>] [--add] [--session | --everywhere]\n atris engine assign <job> --promote "<tool> [model]" | --remove <tool> | --clear [--session | --everywhere]\n sets the lead worker of the job; --promote moves one of its workers to the top as written;\n --add puts one more at the end; jobs: search, build, review,\n or your own like "small build" (--like search|build|review when the name does not say);\n --prep search has that job read the task first and hand this worker a short brief;\n --everywhere is for all projects; --session is this shell only (set ATRIS_ROSTER_SESSION=<name>)\n atris engines --chart show the fleet as an org chart\n atris engine list --json [--all] full registry: default + engines with tier, roles, fallback, health (--all includes hidden engines)\n atris engine set <name> --duty leader|errands|learning [--models "a, b"]\n arrange the fleet and save its model policy\n atris engine resolve <role> or <job> [--json]\n choose the best ready engine for navigator|executor|validator or a roster job;\n notes always answers: its roster pick, else agy then haiku\n atris engine health <name> --set ready|not_installed|credit_out\n flip runtime health, for example when credits run out\n atris engine doctor [--json]\n probe which engine CLIs are installed here and sync that into health policy\n atris engine <name> make that engine the default here\n atris engine test [name] preflight: run the engine CLI headless, report pass/fail\n atris engine bench [names...] [--runs N]\n ranked latency scoreboard of engine passes\n atris engine ask "<question>" --engine <name> [--engine <name> ...]\n ask several engines in parallel without allowing edits\n atris engine ask --jobs <jobs.json>\n ask different read-only questions in parallel\n atris engine validate <receipt-path|latest> [--engine <name>]\n check ask answers with a different read-only referee\n atris engine validate scoreboard\n show pass rates by worker engine\n atris engine sweep [--apply] [--json]\n list ssh-agents that engine shells left running; --apply rechecks and stops them\n atris engine dispatch <task-id> [<task-id> ...] --engine cursor|codex [--prompt-file <f>] [--yolo]\n one-command claim, worktree, build, verify, ship, ready\n atris engine login <provider> --yes\n upload a local provider CLI login to the backend vault\n atris engine login <provider> --computer [--seat <name>]\n atris engine login <provider> --business <id> [--seat <name>]\n sign in on an Atris computer by device flow\n atris engine login --list | --remove <provider>\n list or remove vaulted provider logins\n atris engine seats show which named accounts are ready to work\n atris engine seed <provider> --business <id>|--user\n push a vaulted login onto an Atris computer\n atris engine reset back to the house default\n --engine <name> one run on that engine (mission run / autopilot / run)\n');
|
|
2626
|
+
console.log('\n atris engine roster + current default\n atris engine roster [--json] show every job and its workers in order: search, build, review, and your own\n flags a model with a newer version on this machine, or a pin not proven in 30 days\n (a landed run, or "verified 2026-09-30" on its line, proves it)\n atris engine roster confirm renew every dated worker for 30 days\n atris engine roster session [clear]\n show or drop the roster changes for this shell only\n atris engine roster --available\n tools on this machine and the models each one offers\n atris engine roster set <job> <tool> <model>\n swap the lead of a job to a tool and model this machine offers; the old lead becomes the first backup\n atris engine roster set --member <name> <tool> <model>\n give one member its own model; every other job stays put\n atris engine roster --runs [job]\n the last 20 runs, newest first, with tool, model, time, and outcome\n atris engine assign <job> <tool> [--like <kind>] [--model <m>] [--effort <level>] [--max "20 min"] [--prep <job>] [--backup "<tool> [model]"] [--days <n>] [--add] [--session | --everywhere]\n atris engine assign <job> --promote "<tool> [model]" | --remove <tool> | --clear [--session | --everywhere]\n sets the lead worker of the job; --promote moves one of its workers to the top as written;\n --add puts one more at the end; jobs: search, build, review,\n or your own like "small build" (--like search|build|review when the name does not say);\n --prep search has that job read the task first and hand this worker a short brief;\n --everywhere is for all projects; --session is this shell only (set ATRIS_ROSTER_SESSION=<name>)\n atris engines --chart show the fleet as an org chart\n atris engine list --json [--all] full registry: default + engines with tier, roles, fallback, health (--all includes hidden engines)\n atris engine set <name> --duty leader|errands|learning [--models "a, b"]\n arrange the fleet and save its model policy\n atris engine resolve <role> or <job> [--json]\n choose the best ready engine for navigator|executor|validator or a roster job;\n notes always answers: its roster pick, else agy then haiku\n atris engine health <name> --set ready|not_installed|credit_out\n flip runtime health, for example when credits run out\n atris engine doctor [--json]\n probe which engine CLIs are installed here and sync that into health policy\n atris engine <name> make that engine the default here\n atris engine test [name] preflight: run the engine CLI headless, report pass/fail\n atris engine bench [names...] [--runs N]\n ranked latency scoreboard of engine passes\n atris engine ask "<question>" --engine <name> [--engine <name> ...]\n ask several engines in parallel without allowing edits\n atris engine ask --jobs <jobs.json>\n ask different read-only questions in parallel\n atris engine validate <receipt-path|latest> [--engine <name>]\n check ask answers with a different read-only referee\n atris engine validate scoreboard\n show pass rates by worker engine\n atris engine sweep [--apply] [--json]\n list ssh-agents that engine shells left running; --apply rechecks and stops them\n atris engine dispatch <task-id> [<task-id> ...] --engine cursor|codex [--prompt-file <f>] [--yolo]\n one-command claim, worktree, build, verify, ship, ready\n atris engine login <provider> --yes\n upload a local provider CLI login to the backend vault\n atris engine login <provider> --computer [--seat <name>]\n atris engine login <provider> --business <id> [--seat <name>]\n sign in on an Atris computer by device flow\n atris engine login --list | --remove <provider>\n list or remove vaulted provider logins\n atris engine seats show which named accounts are ready to work\n atris engine seed <provider> --business <id>|--user\n push a vaulted login onto an Atris computer\n atris engine reset back to the house default\n --engine <name> one run on that engine (mission run / autopilot / run)\n');
|
|
2552
2627
|
return 0;
|
|
2553
2628
|
}
|
|
2554
2629
|
if (isFreshWorkspace(root)) {
|
package/commands/gm.js
CHANGED
|
@@ -31,6 +31,7 @@ function showHelp() {
|
|
|
31
31
|
console.log(' atris gm card <member> [--json] one member\'s profile page');
|
|
32
32
|
console.log(' atris gm target [--landed N] [--max-abandoned N] [--max-stuck N] [--clear]');
|
|
33
33
|
console.log(' atris gm phone [--port 7777] [--send] [--new-link] decision cards on your phone');
|
|
34
|
+
console.log(' atris gm walk [--real-copy <workspace>] [--threshold 0.8] [--no-browser] check every stage of the phone orb');
|
|
34
35
|
console.log(' atris gm --json machine-readable state');
|
|
35
36
|
console.log(' atris gm --watch [--interval 10]');
|
|
36
37
|
console.log('');
|
|
@@ -121,6 +122,13 @@ function showHelp() {
|
|
|
121
122
|
console.log(' tap. Every tap is logged to ~/.atris/state/gm-phone-actions.jsonl.');
|
|
122
123
|
console.log(' --send texts the link to the number saved with atris autoland on --to.');
|
|
123
124
|
console.log('');
|
|
125
|
+
console.log(' atris gm walk starts the phone page on a made-up workspace with a temp');
|
|
126
|
+
console.log(' home, walks every stage of the orb (the idea, an answer, the weekly');
|
|
127
|
+
console.log(' number, the team, a swap, the job deck, a yes card, Esc, the small and');
|
|
128
|
+
console.log(' big orb), and prints one row per stage: pass or fail, and why. Rules in');
|
|
129
|
+
console.log(' code and a fast judge model both have to agree. atris gm walk --help');
|
|
130
|
+
console.log(' says more. It runs from an atris checkout and never touches ~/.atris.');
|
|
131
|
+
console.log('');
|
|
124
132
|
console.log(' The league treats every real project folder with task events in the');
|
|
125
133
|
console.log(' last 30 days as a club: team rating, landed this week and in 30 days,');
|
|
126
134
|
console.log(' drops, abandons, stuck claims, last task event, and best player.');
|
|
@@ -1622,6 +1630,22 @@ async function gmPhone(args = []) {
|
|
|
1622
1630
|
return undefined;
|
|
1623
1631
|
}
|
|
1624
1632
|
|
|
1633
|
+
// ---- walk ----------------------------------------------------------------------
|
|
1634
|
+
|
|
1635
|
+
// gm walk: every stage of the phone orb, walked and judged on a made-up
|
|
1636
|
+
// workspace (scripts/orb-walk.js). The published package leaves the script
|
|
1637
|
+
// out, so an install says so instead of failing on a missing file.
|
|
1638
|
+
async function gmWalk(args = []) {
|
|
1639
|
+
const script = path.join(__dirname, '..', 'scripts', 'orb-walk.js');
|
|
1640
|
+
if (!fs.existsSync(script)) {
|
|
1641
|
+
console.error('atris gm walk runs from an atris checkout. this install does not include the walk.');
|
|
1642
|
+
process.exitCode = 2;
|
|
1643
|
+
return undefined;
|
|
1644
|
+
}
|
|
1645
|
+
process.exitCode = await require(script).main(args);
|
|
1646
|
+
return undefined;
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1625
1649
|
// ---- player card ------------------------------------------------------------
|
|
1626
1650
|
|
|
1627
1651
|
// One member's profile page as lines. Pure: card in, lines out.
|
|
@@ -1844,6 +1868,12 @@ function wakeableMember(args) {
|
|
|
1844
1868
|
}
|
|
1845
1869
|
|
|
1846
1870
|
async function gmCommand(...args) {
|
|
1871
|
+
// the walk has its own help
|
|
1872
|
+
if (positional(args)[0] === 'walk') {
|
|
1873
|
+
const at = args.indexOf('walk');
|
|
1874
|
+
return gmWalk([...args.slice(0, at), ...args.slice(at + 1)]);
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1847
1877
|
if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
|
|
1848
1878
|
showHelp();
|
|
1849
1879
|
return;
|