atris 3.58.7 → 3.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +3 -2
- package/GETTING_STARTED.md +2 -2
- package/PERSONA.md +11 -11
- package/README.md +1 -1
- package/atris/GEMINI.md +7 -6
- package/atris/PERSONA.md +4 -4
- package/atris/features/README.md +77 -6
- package/atris/skills/README.md +44 -15
- package/atris/skills/aeo/SKILL.md +10 -10
- package/atris/skills/atris/SKILL.md +1 -1
- package/atris/skills/engines/SKILL.md +69 -50
- package/atris/skills/meta/SKILL.md +4 -4
- package/bin/atris.js +8 -6
- package/commands/aeo.js +9 -12
- package/commands/analytics.js +8 -11
- package/commands/autopilot.js +79 -8
- package/commands/avail.js +1 -1
- package/commands/brain.js +3 -1
- package/commands/business.js +13 -8
- package/commands/clean.js +17 -0
- package/commands/close.js +2 -1
- package/commands/computer.js +15 -10
- package/commands/context-sync.js +3 -3
- package/commands/doc-health.js +55 -2
- package/commands/engine.js +384 -4
- package/commands/experiments.js +13 -4
- package/commands/host.js +53 -0
- package/commands/improve.js +1 -1
- package/commands/integrations.js +2 -5
- package/commands/learn.js +1 -1
- package/commands/member.js +231 -53
- package/commands/mission.js +129 -19
- package/commands/one-lap.js +58 -3
- package/commands/pack.js +2 -2
- package/commands/probe.js +11 -5
- package/commands/pull.js +1 -1
- package/commands/push.js +1 -1
- package/commands/rainmaker.js +3 -16
- package/commands/run.js +4 -1
- package/commands/scout.js +1 -1
- package/commands/search.js +5 -3
- package/commands/spaceship.js +1 -1
- package/commands/stream.js +27 -2
- package/commands/study.js +15 -9
- package/commands/sync.js +1 -1
- package/commands/task.js +13 -2
- package/commands/unknowns.js +5 -0
- package/commands/verify.js +12 -4
- package/commands/visualize.js +20 -3
- package/commands/workflow.js +23 -7
- package/lib/codex-flight.js +28 -5
- package/lib/dispatch-scout.js +31 -22
- package/lib/engine-registry.js +1482 -13
- package/lib/feedback-triage.js +2 -0
- package/lib/fleet.js +111 -12
- package/lib/host.js +336 -0
- package/lib/improve-vitals-html.js +3 -2
- package/lib/known-commands.js +1 -1
- package/lib/lesson-contradiction.js +2 -2
- package/lib/map-refs.js +226 -0
- package/lib/member-engine.js +253 -0
- package/lib/mission-room.js +4 -4
- package/lib/noninteractive.js +15 -4
- package/lib/policy-lessons.js +1 -1
- package/lib/receipt-block.js +137 -28
- package/lib/roster-markdown.js +206 -0
- package/lib/roster-models.js +179 -0
- package/lib/runner-command.js +95 -15
- package/lib/scorecard.js +4 -4
- package/lib/self-drive.js +9 -1
- package/lib/sleep-sync.js +11 -0
- package/lib/state-detection.js +28 -9
- package/lib/task-db.js +3 -2
- package/lib/todo-fallback.js +1 -1
- package/lib/wish-audit.js +10 -6
- package/lib/wish-delegate.js +29 -5
- package/mcp/atris-mcp/index.mjs +77 -24
- package/package.json +2 -5
- package/scripts/det/det.js +2 -2
- package/scripts/det/pr-description.js +2 -2
- package/scripts/det/test.js +2 -2
- package/templates/members/host/MEMBER.md +23 -0
- package/templates/members/host/MISSION.md +11 -0
- package/templates/members/host/SOUL.md +7 -0
- package/templates/members/host/context/why.md +3 -0
- package/templates/members/host/goals.json +5 -0
- package/templates/members/host/goals.md +3 -0
- package/templates/members/host/skills/host/SKILL.md +13 -0
- package/utils/api.js +1 -1
- package/utils/auth.js +17 -7
- package/utils/backend-root.js +31 -0
- package/utils/config.js +5 -4
- package/utils/owner-identity.js +61 -0
- package/atris/policies/LESSONS.md +0 -13
- package/atris/skills/autopilot/atris-autopilot.md +0 -71
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: engines
|
|
3
3
|
description: "Dispatch work to an installed terminal agent or named Atris engine profile. Supports Atris Fast, Claude, Codex, Cursor, Fable, Composer, Haiku, Devin, Grok, Antigravity (agy), and opencode. Triggers on: use codex, use cursor, use devin, use grok, use agy, use antigravity, use gemini, gemini session, use fable, use claude, use opencode, use atris, engine, dispatch to, worker agent, second opinion build."
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.7.0
|
|
5
5
|
tags:
|
|
6
6
|
- engines
|
|
7
7
|
- claude
|
|
@@ -20,10 +20,36 @@ tags:
|
|
|
20
20
|
- orchestration
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
# Engines
|
|
23
|
+
# Engines: interchangeable terminal workers
|
|
24
24
|
|
|
25
25
|
One contract, eleven live profiles. The orchestrator writes a bounded task prompt, dispatches it to an engine, then **independently verifies, lands, and pushes** the result. Engines never self-certify.
|
|
26
26
|
|
|
27
|
+
## Pick the job owner first
|
|
28
|
+
|
|
29
|
+
Before choosing an engine for search, build, or review, run `atris engine roster` and use that job's lead worker. Only deviate when the user names an engine.
|
|
30
|
+
|
|
31
|
+
The roster is a markdown file the owner can edit any time: `atris/ROSTER.md` for this project, `~/.atris/ROSTER.md` for every project on this machine. A project job wins over the machine job with the same name. Each `## job` heading lists its workers in order, one per line, naming the tool and the model:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
## review
|
|
35
|
+
- codex, model: gpt-6-astra, effort: medium, max: 20 min
|
|
36
|
+
- claude code, model: opus 5.5
|
|
37
|
+
|
|
38
|
+
## small build
|
|
39
|
+
- devin, model: swe-2-max, until 2026-10-24
|
|
40
|
+
- grok, model: grok 4.7 fast
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The first worker that is ready and not expired leads; the rest are its backups, in order. Tools take engine ids or `claude code`, `atris fast`, and `gemini`. The older one-line shape (`build: claude opus 5.5, backup codex`) still reads the same, and the first `atris engine assign` rewrites the file into sections. `assign <job> <tool>` sets the lead and keeps the rest; `--add` puts a worker at the end, `--remove <tool>` drops one, `--clear` drops the job, and `--everywhere` writes the machine file. `atris engine roster --available` lists the tools and models on this machine.
|
|
44
|
+
|
|
45
|
+
A job's list is also its team. When a task splits into parallel parts, give the parts to that job's workers in order, first worker first, instead of piling them all on the lead. Heavy models like astra and fable should only judge: have the search lead write them a trimmed brief first.
|
|
46
|
+
|
|
47
|
+
Changes for one shell only go in a session file above both rosters. Set `ATRIS_ROSTER_SESSION=<name>` (agent shells have no terminal, so they must), then `atris engine assign <job> <tool> --session`. A session job replaces that job's whole list for this shell; `atris engine roster session` shows the changes and `atris engine roster session clear` drops them. A session nobody used for a day is dropped on its own.
|
|
48
|
+
|
|
49
|
+
Jobs are open-ended: besides search, build, and review, the owner can add a job like `## small build`, or `## quick fixes (like build)` when the name does not say its kind. For a small, tightly specified build slice, use the small build lead when `atris engine roster` shows one; bigger builds use build.
|
|
50
|
+
|
|
51
|
+
Team members pick automatically. Navigators, researchers, and scouts do search; validators, reviewers, judges, critics, and verifiers do review; everyone else builds. A line under `## team` overrides that, with a job (`- researcher: search`) or a tool and model (`- judge: claude code, model: opus 5.5`). `member run`, missions with an owner, and autopilot's plan, do, and review use the member's pick; `--engine` still wins. `atris engine roster` lists the team and warns about any line it could not use.
|
|
52
|
+
|
|
27
53
|
## three verbs
|
|
28
54
|
|
|
29
55
|
- ask: `atris engine <name> "<question>"`; pin a model with `atris engine <name> --model <model> "<question>"`
|
|
@@ -59,54 +85,47 @@ substitute raw Claude or another engine and call the result FABLE.
|
|
|
59
85
|
|
|
60
86
|
Raw spawns are not the default because they skip Atris receipts, watch, and coaching.
|
|
61
87
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
88
|
+
Smoke: `reply with the word OK`, run from `$HOME`, 120s cap, one at a time on a loaded Mac (2026-09-23). Seconds are wall time under that load.
|
|
89
|
+
|
|
90
|
+
| Engine | Exact command | Pinned model | Verified | Seconds | Notes |
|
|
91
|
+
|--------|---------------|--------------|----------|--------:|-------|
|
|
92
|
+
| Atris Fast | `atris chat --print "<prompt>"` | `atris:fast` | 2026-09-23 | 3.2 | Needs an initialized `atris/` workspace; see runtime requirements below. |
|
|
93
|
+
| Claude | `claude -p "<prompt>" --model claude-opus-5-5 --output-format json --no-session-persistence` | `claude-opus-5-5` | 2026-09-23 | 20.3 | `--model opus` also resolves to `claude-opus-5-5` today; pin the id for reproducibility. |
|
|
94
|
+
| Codex | `codex exec --dangerously-bypass-approvals-and-sandbox --ephemeral -m gpt-6-sol -c model_reasoning_effort=high -o <result-file> "<prompt>" </dev/null` (run from the target repo/worktree; read-only: swap the bypass flag for `--sandbox read-only`) | `gpt-6-sol`, high | 2026-09-23 | 13.8 | Keep the `</dev/null`: with stdin open codex prints "Reading additional input from stdin..." and waits forever (observed 2026-09-23). Outside a git repo add `--skip-git-repo-check` or it exits 1 (re-checked 2026-09-23). Final answer lands in the `-o` file. Run as a tracked background Bash task; in one-shot `claude -p` workers run it in the FOREGROUND, a one-shot session never wakes again (2026-08-11). `codex-companion.mjs task --background` is deprecated: its job store never notifies the session (2026-08-10). |
|
|
95
|
+
| Cursor | `cursor-agent --trust --model composer-2.5 -p "<prompt>"` (run from the target repo) | `composer-2.5` | 2026-09-23 | 37.5 | `--trust` required for non-interactive. |
|
|
96
|
+
| Fable | `atris engine fable --timeout 120 --json "<question>"` | `claude-fable-5` | 2026-09-23 | 21.4 | Canonical read-only FABLE ask with receipt; scale `--timeout` to the work. |
|
|
97
|
+
| Composer | `cursor-agent --trust --model composer-2.5 -p "<prompt>"` | `composer-2.5` via Cursor | 2026-09-23 | 37.5 | Same binary as Cursor. |
|
|
98
|
+
| Haiku | `claude -p "<prompt>" --model claude-haiku-4-5 --output-format json --no-session-persistence` | `claude-haiku-4-5` | 2026-09-23 | 19.0 | Fast validation and bounded read-only checks. |
|
|
99
|
+
| Devin (build) | `devin -p --permission-mode dangerous --model swe-2-max --prompt-file <brief>` (run from an isolated worktree) | `swe-2-max` (Free tag in `devin models list`, 2026-09-23) | 2026-09-23 | 14.0 | In `-p` mode the default permission mode rejects shell commands too, so even a read-only audit dies after one step (2026-09-23). Keep the brief outside the worktree. Long runs (5+ min) can print nothing even on success: judge by the worktree diff. `devin cloud` outlives this machine. |
|
|
100
|
+
| Devin (search) | `devin -p --permission-mode dangerous --model swe-1.7-lightning -- "<prompt>"` | `swe-1.7-lightning` (paid, no Free tag on 2026-09-23) | 2026-09-23 | 13.9 | |
|
|
101
|
+
| Grok | `grok --always-approve --model grok-4.7 -p "<prompt>"` (run from the target repo) | `grok-4.7` | 2026-09-23 | 11.7 | Unpinned default is now `grok-4.7-build-fast`; `grok models` lists the menu. Uses grok.com login. `--best-of-n <N>` for tricky bounded builds. |
|
|
102
|
+
| Antigravity (agy) | `agy --mode accept-edits --add-dir "$PWD" --model gemini-3.8-flash-high --output-format json -p "<prompt>"` (run from the target repo) | `gemini-3.8-flash-high` | 2026-09-23 | 15.9 | **`--add-dir` is mandatory for writes**: without it agy edits `~/.gemini/antigravity-cli/scratch/` and the project never changes (2026-08-28). `--mode plan --sandbox` for read-only review. Also answers to "gemini". |
|
|
103
|
+
| opencode | `opencode run -m opencode/muse-spark-1.3-contributor-free "<prompt>"` (read-only: add `--agent plan`) | `opencode/muse-spark-1.3-contributor-free` | 2026-09-23 | 41.1 | Build work needs `--auto` (isolated worktree only). `opencode models` lists the live menu. |
|
|
75
104
|
|
|
76
105
|
Headless dispatch permissions (verified 2026-08-11): `codex exec`, `grok`, and `cursor-agent` are allowlisted in `~/.claude/settings.json` so fresh and one-shot sessions can dispatch without a human approval click. A cold session that gets "requires approval" on an engine command means that allowlist regressed.
|
|
77
106
|
|
|
78
107
|
## Picking an engine
|
|
79
108
|
|
|
80
|
-
- **
|
|
81
|
-
- **
|
|
82
|
-
- **
|
|
83
|
-
- **
|
|
84
|
-
- **
|
|
85
|
-
- **
|
|
86
|
-
- **
|
|
87
|
-
- **Grok** - fastest frontier lookups and quick second opinions (grok-4.6, ~5-10s; reads MAP first); use `--best-of-n` for tricky bounded builds. Uses grok.com login.
|
|
88
|
-
- **Antigravity / agy** — flexible executor work across Gemini, Claude, and GPT-OSS models; use `agy` as the canonical short name.
|
|
89
|
-
- **opencode** — multi-provider executor in one CLI (Claude, GPT, Gemini, DeepSeek menus via `opencode models`); plan agent for read-only asks, `--auto` builds only inside an isolated worktree.
|
|
109
|
+
- **Cheap bounded errands**: Devin `swe-2-max` while its Free tag holds (check `devin models list`).
|
|
110
|
+
- **Multi-file or long build**: Use the project's build pick. If there is no pick, use the router's choice.
|
|
111
|
+
- **Judgment-heavy build**: Opus 5.5 subagent (Claude row).
|
|
112
|
+
- **Review / deep judgment**: FABLE profile; Codex `gpt-6-sol` or Opus 5.5 as second validator.
|
|
113
|
+
- **Quick fix**: Cursor.
|
|
114
|
+
- **Search / bounded lookup**: Atris Fast; Haiku or Devin `swe-1.7-lightning` for read-only sweeps; Grok for quick second opinions.
|
|
115
|
+
- **agy / opencode**: extra executors when you want a different model family.
|
|
90
116
|
- Parallel builds across repos: one engine job per repo, never two engines writing the same checkout.
|
|
91
117
|
|
|
92
|
-
## Models worth pinning (verified live 2026-
|
|
93
|
-
|
|
94
|
-
Each engine CLI can pin a specific model. Current best picks:
|
|
118
|
+
## Models worth pinning (verified live 2026-09-23)
|
|
95
119
|
|
|
96
|
-
| Engine | Flag |
|
|
97
|
-
|
|
98
|
-
| Claude
|
|
99
|
-
| Devin | `--model
|
|
100
|
-
| Cursor | `--model
|
|
101
|
-
|
|
|
102
|
-
|
|
|
103
|
-
| Grok | (default) | `grok-4.6` default (confirmed live 2026-08-12, ~5s lookup), `grok-4.5` still available via `-m` |
|
|
104
|
-
| Codex | `-m <model>` | CLI default rides `~/.codex/config.toml`; pin with `-m` only when the task needs it |
|
|
105
|
-
| Antigravity / agy | `--model <id>` | `gemini-3.7-flash-high` for speed, `gemini-3.1-pro-high` for depth, `claude-sonnet-4-6`, `claude-opus-4-6-thinking`, or `gpt-oss-120b-medium` |
|
|
106
|
-
| opencode | `-m <provider/model>` | `opencode/big-pickle`, `opencode/gpt-5.2`, `opencode/claude-opus-4-8`; `opencode models` lists the live menu |
|
|
107
|
-
| Atris Fast | (fixed) | api.atris.ai fast lane |
|
|
120
|
+
| Engine | Flag | Alternates on the live menu |
|
|
121
|
+
|--------|------|-----------------------------|
|
|
122
|
+
| Claude | `--model <id>` | `claude-opus-5-5`, `claude-haiku-4-5` |
|
|
123
|
+
| Devin | `--model <id>` | `swe-2-max`, `swe-2-high`, `swe-2-medium` (Free); `claude-opus-5-5-high`, `gpt-6-sol` (paid) |
|
|
124
|
+
| Cursor | `--model <id>` | `composer-2.5`, `grok-4.7-xhigh`, `claude-opus-5-thinking-high`; `--list-models` |
|
|
125
|
+
| Grok | `--model <id>` | `grok-4.7`, `grok-4.7-build-fast` (default), `grok-4.6`, `grok-4.5`; `grok models` |
|
|
126
|
+
| Codex | `-m <id>` | `gpt-6-sol` is the `~/.codex/config.toml` default |
|
|
108
127
|
|
|
109
|
-
|
|
128
|
+
Pins expire 30 days after their verified date. Re-verify on a new model: run the CLI's model list, smoke one prompt, update the row.
|
|
110
129
|
|
|
111
130
|
## Keep the local roster current
|
|
112
131
|
|
|
@@ -115,7 +134,7 @@ Run `atris engine doctor`, then `atris engine --help`. The canonical profiles li
|
|
|
115
134
|
## Atris Fast runtime requirements (verified live 2026-07-03)
|
|
116
135
|
|
|
117
136
|
- Must run from an **initialized Atris workspace** (an `atris/` folder) under an allowed workspace root (e.g. `~/arena/*`). `atris chat --print` outside one exits 1 with "Run atris init"; `ax --fast --print` outside an allowed root fail-closes with `{ok:false, error:"workspace_path must be under an allowed local workspace root"}`.
|
|
118
|
-
- In an allowed root **without** an `atris/` folder the turn silently routes to the cloud no-tools chat lane
|
|
137
|
+
- In an allowed root **without** an `atris/` folder the turn silently routes to the cloud no-tools chat lane. The model will honestly refuse file work ("no Atris Desktop runtime attached"). If the output says that, you dispatched from the wrong directory; it is not an engine failure.
|
|
119
138
|
|
|
120
139
|
## Prompt contract (every dispatch)
|
|
121
140
|
|
|
@@ -140,16 +159,16 @@ the actual final state: draft, queued, and merged are different outcomes.
|
|
|
140
159
|
Record elapsed time, engine calls, retries, and exposed token usage. Unknown
|
|
141
160
|
usage stays unknown; one pilot does not establish a speedup or perfect accuracy.
|
|
142
161
|
|
|
143
|
-
1. Name the absolute repo path and tell the engine to `cd` there (Atris Fast scopes to the cwd it runs from
|
|
162
|
+
1. Name the absolute repo path and tell the engine to `cd` there (Atris Fast scopes to the cwd it runs from, so cd first and name absolute paths in the prompt).
|
|
144
163
|
2. Bound the slice: one task, explicit exit criteria, the verify command to run.
|
|
145
|
-
3. Git rules: `git status` first; stage only own files; never revert others' changes; never destructive git; work on a branch `member/<name>-<slug>` or a worktree. (Atris Fast does not run git
|
|
146
|
-
4. Require a final report: files changed, verify command + result, branch name. (Atris Fast returns one JSON `output` field
|
|
164
|
+
3. Git rules: `git status` first; stage only own files; never revert others' changes; never destructive git; work on a branch `member/<name>-<slug>` or a worktree. (Atris Fast does not run git. For edit tasks the orchestrator commits after verifying.)
|
|
165
|
+
4. Require a final report: files changed, verify command + result, branch name. (Atris Fast returns one JSON `output` field. Ask for file:line evidence in it.)
|
|
147
166
|
|
|
148
|
-
## Landing (orchestrator duties
|
|
167
|
+
## Landing (orchestrator duties, never skip)
|
|
149
168
|
|
|
150
169
|
- **Codex sandbox cannot reach github.com and may get read-only repo access.** Expect temp clones / `git format-patch` fallbacks under `/private/tmp`. Apply patches in a fresh worktree, re-run the verify command yourself, then push.
|
|
151
|
-
- Cursor and Devin run unsandboxed
|
|
152
|
-
- Long Devin runs (5+ min) can return empty stdout even when the build fully succeeded
|
|
153
|
-
- Atris Fast answers are model output over a real tool runtime
|
|
170
|
+
- Cursor and Devin run unsandboxed. Still re-run the verify command yourself before pushing.
|
|
171
|
+
- Long Devin runs (5+ min) can return empty stdout even when the build fully succeeded. Judge by `git status` and the diff in its worktree, never by the printed report.
|
|
172
|
+
- Atris Fast answers are model output over a real tool runtime. Treat `output` as a claim, spot-check the cited file:line, and re-run any verifier yourself before acting on it.
|
|
154
173
|
- Engine task DBs and receipts written inside a sandbox are snapshots; reconcile against the live `atris task` plane after landing.
|
|
155
|
-
- A stalled job (no log output for 30+ min) gets cancelled and taken over; don't wait on it. Atris Fast turns that exceed ~60s have hung
|
|
174
|
+
- A stalled job (no log output for 30+ min) gets cancelled and taken over; don't wait on it. Atris Fast turns that exceed ~60s have hung. Kill and retry once with a tighter prompt.
|
|
@@ -23,7 +23,7 @@ How to be an effective agent. Load this when you need to check yourself.
|
|
|
23
23
|
|
|
24
24
|
1. **MAP first** - Read `atris/MAP.md` before any search. It's the index.
|
|
25
25
|
2. **Journal context** - Check today's log for recent work, patterns, blockers.
|
|
26
|
-
3. **
|
|
26
|
+
3. **atris/lessons.md** - What failed before? Don't repeat.
|
|
27
27
|
4. **Skill routing** - Frontend? Load `design`. Backend? Load `backend`. Writing? Load `writing`.
|
|
28
28
|
|
|
29
29
|
## Pace control
|
|
@@ -48,7 +48,7 @@ Stop and reassess when:
|
|
|
48
48
|
## Memory leverage
|
|
49
49
|
|
|
50
50
|
Before acting:
|
|
51
|
-
1. Check `atris/
|
|
51
|
+
1. Check `atris/lessons.md` - short lessons from past mistakes
|
|
52
52
|
2. Check `atris/features/` - prior art for similar work
|
|
53
53
|
3. Check journal history - `grep -i "<keyword>" atris/logs/**/*.md`
|
|
54
54
|
|
|
@@ -72,7 +72,7 @@ Act (execute with focus)
|
|
|
72
72
|
↓
|
|
73
73
|
Check (output match intent?)
|
|
74
74
|
↓
|
|
75
|
-
Learn (add to
|
|
75
|
+
Learn (add to lessons.md if miss)
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
## Anti-patterns
|
|
@@ -88,7 +88,7 @@ Learn (add to LESSONS if miss)
|
|
|
88
88
|
## Skill evolution
|
|
89
89
|
|
|
90
90
|
Skills improve from your mistakes:
|
|
91
|
-
1. After REVIEW, log learnings to `
|
|
91
|
+
1. After REVIEW, log learnings to `atris/lessons.md` (one line per lesson)
|
|
92
92
|
2. Repeated lessons (2-3x) get promoted into the relevant skill
|
|
93
93
|
3. Next agent loads the improved skill
|
|
94
94
|
|
package/bin/atris.js
CHANGED
|
@@ -47,7 +47,7 @@ const {
|
|
|
47
47
|
requireAccountBound,
|
|
48
48
|
refuseAccountGlobal,
|
|
49
49
|
} = require('../lib/account-bound');
|
|
50
|
-
const { isHelpToken, argsWantHelp, hasYesFlag } = require('../lib/noninteractive');
|
|
50
|
+
const { isHelpToken, argsWantHelp, hasYesFlag, envForcesNonInteractive } = require('../lib/noninteractive');
|
|
51
51
|
|
|
52
52
|
// State detection for smart default
|
|
53
53
|
const { detectWorkspaceState, loadContext } = require('../lib/state-detection');
|
|
@@ -577,7 +577,7 @@ function showHelpAll() {
|
|
|
577
577
|
console.log(' watch - Turn one sentence into an always-on background watcher');
|
|
578
578
|
console.log(' ctop - Show a process-first live agent CPU/memory view');
|
|
579
579
|
console.log(' doctor - Node/task/auth/workspace readiness (--json for agents)');
|
|
580
|
-
console.log(' doc-health - workspace document size, navigation, and
|
|
580
|
+
console.log(' doc-health - workspace document size, navigation, freshness, and map line refs (--json, --fix-refs)');
|
|
581
581
|
console.log(' launchpad - Show the next action from local brain, task, mission, and proof state');
|
|
582
582
|
console.log(' brief - Show the one-glance operator brief');
|
|
583
583
|
console.log(' status - See local work and completions (`atris status <business>` for remote)');
|
|
@@ -1248,6 +1248,7 @@ if (!command || !knownCommands.includes(command)) {
|
|
|
1248
1248
|
error: command ? `unknown command: ${command}` : 'unknown command',
|
|
1249
1249
|
command: command || null,
|
|
1250
1250
|
input: rawNaturalArgs.join(' '),
|
|
1251
|
+
suggestions: suggestCommands(command),
|
|
1251
1252
|
usage: 'atris help',
|
|
1252
1253
|
}, null, 2));
|
|
1253
1254
|
process.exit(2);
|
|
@@ -1299,8 +1300,7 @@ function useInteractiveAtrisUi() {
|
|
|
1299
1300
|
return Boolean(
|
|
1300
1301
|
process.stdin.isTTY
|
|
1301
1302
|
&& process.stdout.isTTY
|
|
1302
|
-
&& !
|
|
1303
|
-
&& process.env.ATRIS_NONINTERACTIVE !== '1'
|
|
1303
|
+
&& !envForcesNonInteractive()
|
|
1304
1304
|
);
|
|
1305
1305
|
}
|
|
1306
1306
|
|
|
@@ -1464,7 +1464,7 @@ async function interactiveEntry(userInput, options = {}) {
|
|
|
1464
1464
|
: (context.inProgressFeatures || []).length;
|
|
1465
1465
|
|
|
1466
1466
|
// Pull active missions (durable goals), these outrank dev-pipeline state
|
|
1467
|
-
// because a mission with an unverified verifier is
|
|
1467
|
+
// because a mission with an unverified verifier is an owner commitment
|
|
1468
1468
|
// commitment that hasn't been closed yet.
|
|
1469
1469
|
const activeMissions = loadActiveMissions(workspaceDir);
|
|
1470
1470
|
const liveMissionsCount = activeMissions.length;
|
|
@@ -2959,7 +2959,7 @@ if (command === 'guide') {
|
|
|
2959
2959
|
const subcommand = process.argv[3];
|
|
2960
2960
|
const args = process.argv.slice(4);
|
|
2961
2961
|
Promise.resolve(require('../commands/member').memberCommand(subcommand, ...args))
|
|
2962
|
-
.then(() => process.exit(0))
|
|
2962
|
+
.then(() => process.exit(subcommand === 'run' ? (process.exitCode || 0) : 0))
|
|
2963
2963
|
.catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
|
|
2964
2964
|
} else if (command === 'app') {
|
|
2965
2965
|
const subcommand = process.argv[3];
|
|
@@ -3289,6 +3289,8 @@ if (command === 'guide') {
|
|
|
3289
3289
|
.then(() => process.exit(0))
|
|
3290
3290
|
.catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
|
|
3291
3291
|
}
|
|
3292
|
+
} else if (command === 'host') {
|
|
3293
|
+
require('../commands/host').hostCommand();
|
|
3292
3294
|
} else if (command === 'social') {
|
|
3293
3295
|
require('../commands/social').socialCommand()
|
|
3294
3296
|
.then(() => process.exit(0))
|
package/commands/aeo.js
CHANGED
|
@@ -15,32 +15,29 @@
|
|
|
15
15
|
* atris aeo discover <source> [...] # discovery audit
|
|
16
16
|
* atris aeo audit <source> [...] # agent-usability audit
|
|
17
17
|
*
|
|
18
|
-
* Local workspace resolution: $ATRIS_AEO_ROOT or
|
|
18
|
+
* Local workspace resolution: $ATRIS_AEO_ROOT or the configured backend root.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
const fs = require('fs');
|
|
22
22
|
const os = require('os');
|
|
23
23
|
const path = require('path');
|
|
24
24
|
const { spawnSync } = require('child_process');
|
|
25
|
+
const { BACKEND_ROOT_HINT, resolveBackendRoot: resolveConfiguredBackendRoot } = require('../utils/backend-root');
|
|
25
26
|
const { loadCredentials, abortOnAuthFailure } = require('../utils/auth');
|
|
26
27
|
const { apiRequestJson } = require('../utils/api');
|
|
27
28
|
const { loadBusinesses, saveBusinesses } = require('./business');
|
|
28
29
|
|
|
29
|
-
function
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
for (const root of candidates) {
|
|
35
|
-
if (fs.existsSync(path.join(root, 'atris', 'features', 'aeo'))) return root;
|
|
36
|
-
}
|
|
37
|
-
return null;
|
|
30
|
+
function resolveAeoRoot() {
|
|
31
|
+
const explicit = process.env.ATRIS_AEO_ROOT;
|
|
32
|
+
if (explicit && fs.existsSync(path.join(explicit, 'atris', 'features', 'aeo'))) return explicit;
|
|
33
|
+
const backend = resolveConfiguredBackendRoot();
|
|
34
|
+
return backend && fs.existsSync(path.join(backend, 'atris', 'features', 'aeo')) ? backend : null;
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
function requireBackendRoot() {
|
|
41
|
-
const root =
|
|
38
|
+
const root = resolveAeoRoot();
|
|
42
39
|
if (!root) {
|
|
43
|
-
console.error(
|
|
40
|
+
console.error(`cannot find local aeo workspace; ${BACKEND_ROOT_HINT}`);
|
|
44
41
|
process.exit(1);
|
|
45
42
|
}
|
|
46
43
|
return root;
|
package/commands/analytics.js
CHANGED
|
@@ -68,17 +68,14 @@ function analyticsAtris() {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// Parse timestamps for productivity hours. Match the journal heading
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
hourCounts[hour] = (hourCounts[hour] || 0) + 1;
|
|
80
|
-
}
|
|
81
|
-
});
|
|
71
|
+
// formats `### Title -- HH:MM`, `### Title - HH:MM`, and legacy `**HH:MM:SS**`.
|
|
72
|
+
// Only heading lines count, so a prose range like "10:00 - 12:30" is not a timestamp.
|
|
73
|
+
const timestampPattern = /^#{2,4} .*(?:\u2014|--| - )\s*(\d{2}):\d{2}(?::\d{2})?\s*$|\*\*(\d{2}):\d{2}(?::\d{2})?\*\*/gm;
|
|
74
|
+
for (const match of content.matchAll(timestampPattern)) {
|
|
75
|
+
const hour = parseInt(match[1] || match[2], 10);
|
|
76
|
+
if (Number.isFinite(hour) && hour >= 0 && hour < 24) {
|
|
77
|
+
hourCounts[hour] = (hourCounts[hour] || 0) + 1;
|
|
78
|
+
}
|
|
82
79
|
}
|
|
83
80
|
});
|
|
84
81
|
|
package/commands/autopilot.js
CHANGED
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
runnerAvailabilityFailureMessage,
|
|
19
19
|
} = require('../lib/runner-command');
|
|
20
20
|
const { findStalePages, findStaleTasks, healBrokenMapRefs } = require('./clean');
|
|
21
|
+
const { mapNotesPath } = require('../lib/map-refs');
|
|
21
22
|
const {
|
|
22
23
|
buildScorecardData,
|
|
23
24
|
readScorecards,
|
|
@@ -25,14 +26,15 @@ const {
|
|
|
25
26
|
detectEndgameCompletion
|
|
26
27
|
} = require('../lib/scorecard');
|
|
27
28
|
const { REWARD_CONFIG, REWARD_CHECKSUM } = require('../lib/reward-config');
|
|
29
|
+
const { ownerIdentity } = require('../utils/owner-identity');
|
|
28
30
|
|
|
29
31
|
const pkg = require('../package.json');
|
|
30
32
|
|
|
31
33
|
const PHASE_TIMEOUT = 600000; // 10 min per phase
|
|
32
34
|
|
|
33
35
|
function looksOwnerClaimed(claimed) {
|
|
34
|
-
const text = String(claimed || '').toLowerCase();
|
|
35
|
-
return /\
|
|
36
|
+
const text = String(claimed || '').trim().toLowerCase();
|
|
37
|
+
return /\b(owner|human|operator)\b/.test(text) || ownerIdentity().names.includes(text.split(/\s+/)[0]);
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
function looksOwnerGatedTitle(title) {
|
|
@@ -482,19 +484,73 @@ function execPhaseCommandSync(cmd, opts = {}) {
|
|
|
482
484
|
}
|
|
483
485
|
}
|
|
484
486
|
|
|
487
|
+
// plan, do, and review run as the navigator, executor, and validator. When
|
|
488
|
+
// the roster names an engine for that member, the phase spawns it. A runner
|
|
489
|
+
// set in the environment still wins, and with no roster line behind the
|
|
490
|
+
// member the configured runner runs exactly as before.
|
|
491
|
+
const AUTOPILOT_PHASE_MEMBERS = Object.freeze({ plan: 'navigator', do: 'executor', review: 'validator' });
|
|
492
|
+
const RUNNER_ENV_OVERRIDES = Object.freeze([
|
|
493
|
+
'ATRIS_RUNNER_PROFILE',
|
|
494
|
+
'ATRIS_RUNNER_COMMAND_TEMPLATE',
|
|
495
|
+
'ATRIS_RUNNER_BIN',
|
|
496
|
+
'ATRIS_CLAUDE_COMMAND_TEMPLATE',
|
|
497
|
+
'ATRIS_CLAUDE_BIN',
|
|
498
|
+
]);
|
|
499
|
+
|
|
500
|
+
function autopilotPhaseEngine(phase, cwd = process.cwd()) {
|
|
501
|
+
const member = AUTOPILOT_PHASE_MEMBERS[phase];
|
|
502
|
+
if (!member) return null;
|
|
503
|
+
if (RUNNER_ENV_OVERRIDES.some((name) => String(process.env[name] || '').trim())) return null;
|
|
504
|
+
const { memberRosterEngine } = require('../lib/member-engine');
|
|
505
|
+
return memberRosterEngine(member, cwd, { requireMember: false });
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// The phase's launch command and, when its roster line sets "max 20 min",
|
|
509
|
+
// the time cap in ms (null means the caller's own timeout stands).
|
|
510
|
+
function buildPhaseRunner(phase, promptFile, cwd = process.cwd(), allowedTools = 'Bash,Read,Write,Edit,Glob,Grep') {
|
|
511
|
+
const picked = autopilotPhaseEngine(phase, cwd);
|
|
512
|
+
if (!picked) return { command: buildRunnerCommand({ promptFile, allowedTools }), timeoutMs: null };
|
|
513
|
+
if (process.env.ATRIS_ROUTER_EXPLAIN !== '0') console.error(picked.reason);
|
|
514
|
+
const previous = process.env.ATRIS_RUNNER_PROFILE;
|
|
515
|
+
process.env.ATRIS_RUNNER_PROFILE = picked.engine.id;
|
|
516
|
+
try {
|
|
517
|
+
const command = buildRunnerCommand({
|
|
518
|
+
promptFile,
|
|
519
|
+
allowedTools,
|
|
520
|
+
...(picked.model ? { model: picked.model } : {}),
|
|
521
|
+
...(picked.engine.roster_effort ? { effort: picked.engine.roster_effort } : {}),
|
|
522
|
+
});
|
|
523
|
+
const maxSeconds = Number(picked.engine.roster_max_seconds) || 0;
|
|
524
|
+
return { command, timeoutMs: maxSeconds > 0 ? maxSeconds * 1000 : null };
|
|
525
|
+
} finally {
|
|
526
|
+
if (previous === undefined) delete process.env.ATRIS_RUNNER_PROFILE;
|
|
527
|
+
else process.env.ATRIS_RUNNER_PROFILE = previous;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function buildPhaseRunnerCommand(phase, promptFile, cwd = process.cwd()) {
|
|
532
|
+
return buildPhaseRunner(phase, promptFile, cwd).command;
|
|
533
|
+
}
|
|
534
|
+
|
|
485
535
|
/**
|
|
486
536
|
* Run a phase via the configured runner subprocess.
|
|
487
537
|
*/
|
|
488
538
|
function executePhaseDetailed(phase, context, options = {}) {
|
|
489
|
-
const { verbose = false
|
|
539
|
+
const { verbose = false } = options;
|
|
540
|
+
let timeout = options.timeout || PHASE_TIMEOUT;
|
|
490
541
|
|
|
491
542
|
const prompt = buildPrompt(phase, context, options);
|
|
492
543
|
const tmpFile = path.join(process.cwd(), '.autopilot-prompt.tmp');
|
|
493
544
|
fs.writeFileSync(tmpFile, prompt);
|
|
494
545
|
|
|
495
546
|
try {
|
|
496
|
-
|
|
497
|
-
|
|
547
|
+
let cmd = options.cmdOverride;
|
|
548
|
+
if (!cmd) {
|
|
549
|
+
const runner = buildPhaseRunner(phase, tmpFile);
|
|
550
|
+
cmd = runner.command;
|
|
551
|
+
// A roster time cap stops the phase at that time.
|
|
552
|
+
if (runner.timeoutMs) timeout = runner.timeoutMs;
|
|
553
|
+
}
|
|
498
554
|
const env = { ...process.env };
|
|
499
555
|
delete env.CLAUDECODE;
|
|
500
556
|
const output = execPhaseCommandSync(cmd, {
|
|
@@ -624,6 +680,8 @@ function getContextFiles(phase, options = {}) {
|
|
|
624
680
|
agentSpec && fs.existsSync(path.join(cwd, agentSpec)) ? agentSpec : null,
|
|
625
681
|
'atris/PERSONA.md',
|
|
626
682
|
'atris/MAP.md',
|
|
683
|
+
// Exact file:line refs live beside the short routing map.
|
|
684
|
+
mapNotesPath(cwd),
|
|
627
685
|
'atris/TODO.md',
|
|
628
686
|
fs.existsSync(path.join(cwd, 'atris/lessons.md')) ? 'atris/lessons.md' : null,
|
|
629
687
|
(() => { const { logFile } = getLogPath(); return fs.existsSync(logFile) ? path.relative(cwd, logFile) : null; })(),
|
|
@@ -640,7 +698,7 @@ function getContextFiles(phase, options = {}) {
|
|
|
640
698
|
// checkout is forbidden (COORDINATION.md Rule 4). Interpolated into the
|
|
641
699
|
// default and self-heal do prompts, never the benchmark prompt (it never
|
|
642
700
|
// commits).
|
|
643
|
-
const SHARED_CHECKOUT_GIT_CONTRACT = `- Shared-checkout git safety (COORDINATION.md Rule 4): edits to any repo OTHER than this tick's cwd (
|
|
701
|
+
const SHARED_CHECKOUT_GIT_CONTRACT = `- Shared-checkout git safety (COORDINATION.md Rule 4): edits to any repo OTHER than this tick's cwd (including sibling repos) go through a per-tick worktree, start with \`atris worktree start --member <member> --task "<task>"\`, land with \`atris worktree ship --message "<msg>" --verify "<cmd>"\`. Never edit a sibling repo's shared checkout directly.
|
|
644
702
|
- On a shared checkout, \`git reset\`, \`git checkout --\`, \`git clean\`, and stashing other agents' work are FORBIDDEN, concurrent ticks' uncommitted work lives there.`;
|
|
645
703
|
|
|
646
704
|
/**
|
|
@@ -1356,15 +1414,25 @@ function parseProposedBlock(lines) {
|
|
|
1356
1414
|
return Object.keys(proposed).length ? proposed : null;
|
|
1357
1415
|
}
|
|
1358
1416
|
|
|
1417
|
+
// Plan review is review work, so it runs on the review pick like the review
|
|
1418
|
+
// phase, with that line's model, effort, and time cap. With no roster line
|
|
1419
|
+
// behind it, the configured runner runs exactly as before.
|
|
1420
|
+
function buildPlanReviewRunner(promptFile, cwd = process.cwd(), timeout = 180000) {
|
|
1421
|
+
const runner = buildPhaseRunner('review', promptFile, cwd, 'Bash,Read,Grep,Glob');
|
|
1422
|
+
return { command: runner.command, timeoutMs: runner.timeoutMs || timeout };
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1359
1425
|
/**
|
|
1360
|
-
* Default executor for plan-review: spawn a fresh
|
|
1426
|
+
* Default executor for plan-review: spawn a fresh runner call on the review pick.
|
|
1361
1427
|
* Kept thin so tests can inject a stub via options.planReviewExec.
|
|
1362
1428
|
*/
|
|
1363
1429
|
function defaultPlanReviewExecutor(prompt, { cwd, timeout = 180000 } = {}) {
|
|
1364
1430
|
const tmpFile = path.join(cwd, '.autopilot-plan-review.tmp');
|
|
1365
1431
|
fs.writeFileSync(tmpFile, prompt);
|
|
1366
1432
|
try {
|
|
1367
|
-
const
|
|
1433
|
+
const runner = buildPlanReviewRunner(tmpFile, cwd, timeout);
|
|
1434
|
+
const cmd = runner.command;
|
|
1435
|
+
timeout = runner.timeoutMs;
|
|
1368
1436
|
const env = { ...process.env };
|
|
1369
1437
|
delete env.CLAUDECODE;
|
|
1370
1438
|
const output = execPhaseCommandSync(cmd, {
|
|
@@ -3871,6 +3939,9 @@ module.exports = {
|
|
|
3871
3939
|
isPhaseKillError,
|
|
3872
3940
|
execPhaseCommandSync,
|
|
3873
3941
|
executePhaseDetailed,
|
|
3942
|
+
buildPhaseRunnerCommand,
|
|
3943
|
+
buildPhaseRunner,
|
|
3944
|
+
buildPlanReviewRunner,
|
|
3874
3945
|
buildAutopilotGlassLog,
|
|
3875
3946
|
writeAutopilotGlassLog,
|
|
3876
3947
|
lessonSlug
|
package/commands/avail.js
CHANGED
|
@@ -308,7 +308,7 @@ async function slotsAvail(argv) {
|
|
|
308
308
|
if (!username) {
|
|
309
309
|
const identity = await apiRequestJson('/identity/me', { method: 'GET', token });
|
|
310
310
|
if (!identity.ok || !identity.data?.username) {
|
|
311
|
-
console.error('
|
|
311
|
+
console.error('could not resolve username; pass --user <username>.');
|
|
312
312
|
return 1;
|
|
313
313
|
}
|
|
314
314
|
username = identity.data.username;
|
package/commands/brain.js
CHANGED
|
@@ -3,6 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const { spawnSync } = require('child_process');
|
|
5
5
|
const { refreshNowFile } = require('./now');
|
|
6
|
+
const { ownerIdentity } = require('../utils/owner-identity');
|
|
6
7
|
const escapeRegExp = require('../lib/escape-regexp');
|
|
7
8
|
const { hasRenderedSections, isOpenSection, isDoneSection } = require('../lib/todo-sections');
|
|
8
9
|
const { isNonInteractive } = require('../lib/noninteractive');
|
|
@@ -794,7 +795,8 @@ function memberNextMove(member, state = null) {
|
|
|
794
795
|
if (member.slug === 'justin' || /justin/i.test(member.name || '')) {
|
|
795
796
|
return `${name}: run one customer-moving GTM rep, update the relevant workspace state within 10 minutes, and leave a scorecard.`;
|
|
796
797
|
}
|
|
797
|
-
|
|
798
|
+
const ownerNames = ownerIdentity().names;
|
|
799
|
+
if (ownerNames.includes(String(member.slug || '').toLowerCase()) || ownerNames.includes(String(member.name || '').toLowerCase())) {
|
|
798
800
|
return `${name}: act as Customer 0: choose one allocation, narrative, system, or talent leverage move; leave proof and route the next owner.`;
|
|
799
801
|
}
|
|
800
802
|
if (/opus|overnight/i.test(identity)) {
|