agents-cli (monorepo)
September 2, 2026 · View on GitHub
A monorepo housing the agents CLI plus its shared libraries and native
helpers. Install, configure, run, and dispatch AI coding agents (Claude, Codex,
Cursor, OpenCode, OpenClaw, Grok, Droid, …) from one place.
Phoenix Labs · FSL-1.1-Apache-2.0.
The main project here is the agents CLI — cli, the
published @phnx-labs/agents-cli. Everything else (native/computer-*,
packages/*) is a helper app / library for one feature, not a main project.
AGI EXT, the VS Code extension, lives in its own repo:
phnx-labs/agi-ext (private; split out
2026-08-25, RUSH-3189). It consumes this CLI; its thin-client contract lives in
that repo's AGENTS.md.
This file is the repo map + repo-wide policy — it deliberately stays shallow.
Read the nearest component AGENTS.md (recursively) before working in it — for
Claude that's CLAUDE.md, a symlink to the same file — and keep going down: the
component file, not this one, is where component-specific detail lives. Every
component with a real AGENTS.md carries CLAUDE.md/GEMINI.md symlinks to it.
Purpose — keep agents running, land work end to end
agents-cli is a power user's control plane for running many coding agents at once and
driving each one to a landed result (merged, shipped, verified), not just started.
Starting an agent is the easy part. The hard part is that agents stall: they stop
mid-task, ask a question and idle, make a statement ("I won't continue…") and sit, fail
to reach for the browser or a secret they already have, or hand work back instead of
finishing it. Every reliability surface in this repo — the daemon watchdog,
needs-you detection, resume/restore, session-status truth, and the AGI EXT
Fleet panel (in phnx-labs/agi-ext) —
exists for one job: notice an agent that has stopped making progress
and get it moving again, so work lands end to end without a human babysitting every
session.
Design consequence — rank by progress, not by liveness. A running session is
making progress and needs nothing from the operator. The sessions that need a human are
the ones that have stopped progressing: blocked on a real prompt, stalled on a
statement, idle mid-task, or crashed. Idle-but-unfinished work is the
highest-risk state, not the lowest, because it is the most likely to be silently
abandoned with no progress ever made. So any status or attention surface (the Fleet
panel, sessions, notifications) surfaces not-progressing work first and collapses
the healthy running set; it never buries idle work below running work. done is a
distinct terminal state from idle: an idle session that is genuinely finished is safe
to fold away, while an idle session that is unfinished is exactly the one to raise.
Repo map
apps/
cli/ @phnx-labs/agents-cli — the `agents`/`ag` CLI (the published npm package)
native/
computer-mac/ Swift daemon behind `agents computer` (Accessibility + screen capture)
computer-win/ C#/.NET daemon behind `agents computer` on Windows (UI Automation)
packages/
session-tracker/ @agents/session-tracker — SessionStart hook that WRITES live-session state
agi-cli/ @phnx-labs/agi-cli — DEPRECATED alias; re-exports the canonical @phnx-labs/agents-cli
swarmify-mirror/ legacy npm-redirect stub (@companion/agents-cli → @phnx-labs/agents-cli)
assets/ website/ Brand + launch demo (under assets/demo/), landing (repo-root, not shipped in any tarball)
| Component | What it is | Read |
|---|---|---|
cli | The CLI — version mgmt, config sync, sessions, teams, cloud, browser, computer, secrets | AGENTS.md · README.md |
| phnx-labs/agi-ext | AGI EXT VS Code extension (own repo) — agent terminals as tabs, Fleet dashboard, dispatch | that repo's AGENTS.md |
native/computer-mac | macOS agents computer backend (Swift) | AGENTS.md · README.md |
native/computer-win | Windows agents computer backend (C#/.NET) | AGENTS.md · README.md |
packages/session-tracker | Live-session writer (SessionStart hook) | AGENTS.md · README.md |
packages/agi-cli | Deprecated alias — re-exports the canonical CLI | README.md |
packages/swarmify-mirror | Deprecated npm-redirect stub | README.md |
No JS workspaces. Each package self-installs (bun install inside it). There is
deliberately no root workspaces field — adding one changed bun's hoisting and broke
@inquirer/core resolution under --frozen-lockfile. Don't add it back. There are no
cross-package imports except the CLI resolving the native helpers by relative path.
Core concepts
What agents-cli actually is: one engine that installs the resources an agent needs,
runs the agent, and extends it with real-world tools, sessions, teams, and
other machines. Deep reference: cli/docs/concepts.md
and architecture.md.
- Resources — the typed things an agent needs, one kind per subdirectory of a
DotAgents repo (
ResourceKindincli/src/lib/resources.ts):rules(thisAGENTS.md→CLAUDE.md/GEMINI.md/…),commands,skills,hooks,mcp,clis,permissions,subagents,workflows,profiles,routers,secrets. Installed once in~/.agents/and synced into each agent's native format. Resolution is layered — project → user → extra repos → system; the highest layer wins a name collision, the rest union (resolveResource,listResources).pluginsis a capability/sync kind (stalenessALL_RESOURCE_KINDS), not aResourceKindsubdirectory. - One execution engine. Every agent invocation goes through one path —
buildExecEnv→execAgent/runWithFallbackincli/src/lib/exec.ts, entered viaagents run. Each agent version runs in an isolated version home (HOMEswapped before exec) so configs never bleed between versions. - Real-world tool surfaces.
agents browser(web) andagents computer(native desktop, backed by thenative/computer-*daemons) are the essential tools that let an agent act on real UIs — the difference between talking about a task and doing it. - Sessions. Two things wear the name: a durable transcript (on disk, indexed in
sessions.db, read byagents sessions) and an ephemeral live identity (which pid is which session right now, surfaced by--active). Transcripts sync across the fleet, so a session is searchable and resumable cross-device. - Teams.
agents teamsruns several agents in parallel on one task, each isolated in its own worktree — the multi-agent surface. - Devices & hosts. agents-cli runs commands on other machines over SSH, no daemon:
devices are the Tailscale fleet (
agents devices), hosts are dispatch targets (agents hosts);-D/--device <name>routes a command to any of them. This is the cross-device fabric under sessions, teams, run, and cloud. - One engine, many consumers.
cliowns the state — the session index, the pid→id registry,sessions/teams/run/cloud, and the SSH fan-out. AGI EXT (phnx-labs/agi-ext) is a consumer: the VS Code UI layer projectsagents sessions watch --jsonand invokes the owning CLI nouns for one-shot reads and actions. It holds presentation state, not duplicate session/device/team/ticket/watchdog mechanisms. Fix a mechanism in the CLI and every consumer benefits. - One scheduler, one executor — fleet-affecting features never run twice. Anything
that can act on this machine or another fleet device — launch/resume/kill a session,
fire a routine or monitor, inject into a terminal, rotate an account — has exactly ONE
scheduler and ONE executor: the agents-cli daemon (
agents __daemon-run) or a CLI command it drives. UI surfaces (the ext, the menubar) are thin wrappers: they render state and offer controls that call the CLI; they MUST NOT own a timer, watcher, or loop that detects a condition and acts on it. Detection and decision live in the CLI, which holds the first-party state (sessions.db, usage snapshots, the device registry). Where an action needs a UI-owned surface, the UI exposes a narrow endpoint the CLI drives (the/injectverb is the precedent) — the trigger stays in the CLI. Routines are covered by the same rule:agents routines+ the daemon's pid-claimed scheduler (cli/src/lib/daemon/daemon.ts) are the only cron that fires them; a UI button may request a run, never schedule one. Multiple devices are fine — shared queues are not. Every device runs its own daemon, and an unrestricted routine MAY fire on all of them when its input is the firing device's own state (its repos, sessions, caches). But a job that consumes shared input (a ticket tracker, a PR queue, the feed, a sync bucket) MUST have exactly one executor per work item: an owner pin (agents routines devices <name> --set <one>), an atomic claim per item (the feed'sO_EXCLprecedent), or verified idempotency — otherwise two daemons pick the same task and run it twice. Violations are the double-fire bug class — the 2026-08-03 incident (the ext's watchdog rotate loop racing the daemon, spawning resume-tabs every 120s into exhausted accounts) is the canonical example; the consolidation (PR #1914) is the canonical fix. The normative contract is §Scheduling & execution singularity. - Credentials are keyed on device role, and the two roles never cross (owner
rule). A personal/desktop (headed) device — the machine a human works on,
e.g.
zion— authenticates ONLY with the harness's normal interactive OAuth (native) login, and is where the durable token is minted. A worker device authenticates with the durable, identity-blind long-term setup-token synced from the account bundle. A headed device MUST NOT fall back to the setup-token when its native login dies — the fix for that is re-running the native OAuth flow, never the token. Provisioning follows one non-reversible flow: mint every interactive login on the laptop; for a token-bearing harness (claude/codex/gemini/grok/opencode/droid) copy the durable credential out to workers where it auto-injects; a token-less harness (kimi, antigravity) is logged in per box. This is a standing, non-negotiable owner requirement; the full statement iscli/docs/credential-management.mdinvariant 7
CLI surface conventions
How the agents command surface is shaped. Coding agents invoke these commands under
token pressure, so the surface has to read like the task and teach its own use. These are
design rules for new or changed commands; the reviewer flags a new surface that ignores
them (see §Code review conventions).
- Nest by relatedness, not dogma. Put a command under the group that owns its noun;
a free-standing top-level command is right when nothing owns the concept. Navigate by
noun then action —
agents sessions resume <id>, notagents resume --session <id>. Flags refine an action, they don't stand in for the group. Don't force a command under the wrong parent just to deepen the tree, and don't flatten a verb that collides with an owned noun. - Intuitive surfaces over clever flags. A command reads like the task it performs: the
primary object sits in the path when there is one, verbs stay consistent across groups
(
list/add/remove/start/done), and every command that emits data takes--jsonfor machine callers. Flag soup burns tokens and produces wrong invocations. browserandcomputerare similar tool surfaces. Both drive real UIs (web / native desktop, local / remote / cloud) as thin CLI surfaces, not thick SDKs. They need not share an identical API — the backends differ (CDP vs Accessibility/UIA) — but they share a shape so an agent learns one mental model: pick a target/session, act, observe, clean up. When you add an action to one, reuse the analogous verb on the other.- Help teaches agents workflows, not man-page flag dumps. A non-trivial command sets
an
examplesblock (andnotesfor prereqs and follow-ups) viasetHelpSections(cli/src/lib/help.ts), so--helprenders an ordered happy-path sequence before the flag list. An agent reading help mid-task needs a three-line playbook, not 40 alphabetized options. Don't leave a non-trivial tool on commander's default help.
CI and release latency are correctness requirements
OWNER REQUIREMENTS — stated 2026-08-17, re-affirmed 2026-09-01, binding, do not weaken
These five are the owner's own words, recorded verbatim in intent. The 60s bar dates to the 2026-08-17 directive captured in
release-latency-breakdown.md, not to the day it was written down here. No agent may relax a number, delete a row, or mark one "not applicable" to make a change fit. A PR that cannot meet one states so explicitly in its description and links the owner's decision to accept it — silently regressing a row is a blocking review failure (§Code review conventions).
# Requirement Bar Status 2026-09-01 R1 Required CI check, event → terminal state < 60 s p50 120 s · p90 133 s (26 green runs on main)R2 Ordinary release, start → registry visible + install smoke < 60 s never completes unattended — wedges on a missing producer (PHNX-3696) R3 A CLI release rebuilds nothing but the CLI — no menubar, no computer helper, no signing, no notarization on the ordinary path absolute held and pinned — cli/scripts/release.test.ts§"an ordinary release is CLI-only" behaviorally asserts no helper-manifest touch and--with-helpersdefaulting OFF; the no-rebuild/no-notarize claim is pinned separately in §"release.sh attestation promotion (RUSH-2666)"R4 AGI Menu and the computer helpers release separately, on their own cadence and their own tags absolute held — menubar/v*,keychain/v*,computer-mac/v*,computer-win/v*with floors incli/src/lib/helper-versions.tsR5 An installed CLI and its installed helpers auto-update from the public channel absolute held — the CLI checks registry.npmjs.orgonce per 24h and self-installs (cli/src/lib/self-update.ts, entered frombootstrap.tscheckForUpdates(), opt out withAGENTS_CLI_DISABLE_AUTO_UPDATE=1;agents upgrade --yesis the non-interactive path). Helpers self-download against the floors incli/src/lib/helper-versions.ts. Homebrew is not a channel today; npm isR1 and R2 are hard ceilings, not averages. R3/R4/R5 are structural and have no percentile — they either hold or the build is wrong. The rendered plan tracing these to their implementation is
.agents/artifacts/2026-09-01/plan-release-under-60s.md.
Status: targets not yet met. As of 2026-09-01 the required Tests workflow runs a
p50 of 120 s and a p90 of 133 s (measured over the last 26 green runs on main) —
down from the 6.1 min / 15.8 min baseline of 2026-08-15, and still 2x the R1 bar. See
.agents/artifacts/2026-08-15/plan-ci-release-near-instant.md for that baseline and
the RUSH-2666 plan, and the 2026-09-01 plan above for the remaining delta. Treat this
section as the acceptance bar new CI/release work is judged against, not a description
of what CI does today.
The required pull-request check has a hard end-to-end P99 of 60 seconds (R1), measured from the GitHub event timestamp until the single required check reaches a terminal state. Ten seconds is the cache-hit target. A required job that cannot fit inside the 60-second budget must be split, rewritten, removed as duplicate ceremony, or moved to post-merge/nightly coverage. It must not silently expand the pull-request gate.
- Run checks for the affected module and its declared reverse dependencies, not the whole monorepo. Every source area owns an explicit test/project boundary; an unmapped changed file fails impact analysis immediately.
- Keep one app-bound required check identity. The workflow always starts; job-level conditions report successful skips. Do not add required workflow path filters, duplicate status contexts, or a matrix of independently required shards.
- Execute the fast lane on already-online capacity. Queueing, runner assignment, checkout, dependency preparation, tests, and status upload all count toward the 60-second P99.
- The required pull-request check runs on GitHub-hosted runners; do not move it
onto self-managed capacity. The owner declined operating an executor
(2026-09-01), and it would not address the bottleneck anyway: a required check
spends ~19s on a cold
bun installand ~0.27s executing tests, so runner speed is not the lever — caching the setup is. Staying hosted also satisfies the fork-isolation rule below for free, which is load-bearing because the repo is PUBLIC with forks.scripts/ci-runner/(Crabbox + Firecracker) is retained machinery, not the direction; see.agents/artifacts/2026-09-01/plan-ci-github-hosted.md. This scopes the PR gate only. Post-merge and scheduled lanes MAY use self-hosted tailnet capacity where the work genuinely needs it — the live precedent is.github/workflows/tests-windows-host-e2e.yml, which driveswin-miniover the tailnet on[self-hosted, crabbox-ci, tailnet]and deliberately carries nopull_requesttrigger for exactly the fork reason. Keep it that way: the rule is about untrusted PR code, not about never using your own machines. - Fork code never executes on a persistent host and never writes trusted caches. Fork jobs receive no durable credentials, host sockets, tailnet access, or host filesystem access.
- Slow integration, broad regression, mutation, packaging, and rare-platform suites remain valuable but run after merge or nightly. They do not block the required PR result or consume fast-lane capacity.
- Windows is not a required pull-request or ordinary-release platform.
.github/workflows/tests.ymlruns a best-effort Windows smoke only on push tomain, withcontinue-on-error, and the requiredTests / testjob does not wait on it. Remove Windows-only code and the supported-platform claim when no demonstrated usage justifies the maintenance cost. - Keep only tests that protect a distinct product invariant or regression. Delete duplicate assertions, implementation-detail tests, constant/trivial-guard tests, and tests whose removal does not reduce meaningful mutation or defect coverage.
An ordinary release has a hard P99 of 60 seconds (R2), measured from release start to registry visibility plus a clean-prefix install smoke. Release promotes the exact tested package artifact; it does not rebuild or rerun the monorepo.
Every gate on the release path ships with the thing that satisfies it, in the same
PR. A required proof — an attestation, a manifest, a signature — whose producer is
manual, out-of-band, or "interim" is an unfinished change, not a policy: it converts
every release into a human errand. This is not hypothetical. RUSH-2666 (bfa1b4eed,
PR #2751) made a release-commit-tree attestation mandatory and shipped no producer, so
every release.sh --apply since has wedged at missing exact attestation key and
required a hand-run script. It passed review because the release tests assert against
release.sh as text rather than running it (cli/scripts/release.test.ts:
grep -cE 'expect\(\(RELEASE_SH|waitFunction\)' = 41 source-text assertions vs
grep -c 'runRelease(' = 9 real invocations), so they proved the gate was wired and
never that it could be satisfied. A new release gate therefore needs a test that
executes the path, not one that greps the script.
A CLI release rebuilds only the CLI (R3). No menubar build, no computer-helper
build, no codesign, no notarization on the ordinary path. Native helpers are
content-addressed and independently versioned on their own tags (R4), so unchanged
helpers are reused and a helper release is its own train
(publish-computer-helper-mac.sh, publish-computer-win.sh). Apple
signing/notarization runs only when a helper's own inputs change and is outside the
ordinary release path entirely. The release train remains the only publisher.
The installed CLI and its helpers update themselves (R5). Both halves already hold
and must not regress. The CLI checks registry.npmjs.org on a 24h cache window and
self-installs (cli/src/lib/self-update.ts, entered from bootstrap.ts
checkForUpdates()); helpers resolve on demand against the floors in
cli/src/lib/helper-versions.ts. A user who installed once keeps getting fixes without
being told to run anything. Removing or gating either path is a blocking regression.
npm is the channel; adding Homebrew would be an addition, not a replacement.
Entry points — always build and release through the scripts
Never hand-roll a build or a release. A bare tsc / bun run build / npm publish /
vsce publish skips the version stamping, gates (tests + semver + CHANGELOG), and
sign/notarize + tap/marketplace steps these scripts own — a green local compile that
ships broken. Each component's scripts/ dir is the contract — the table below is
the canonical entry point per task; add a scripts/<verb>.sh there rather than a
one-off command in a PR.
| Task | Script | Contract |
|---|---|---|
| CLI build | cli/scripts/build.sh [<version>] [--clean] | builds into cli/dist |
| CLI dev install | cli/scripts/install.sh [--bounce-daemon] | side-by-side dev build at ~/.local/agents-cli-dev, invoked as agents-dev (and ag-dev); never creates or touches ~/.local/bin/{agents,ag,browser} |
| CLI tests | cli/scripts/test.sh [--shard <n>] [--devices a,b,c] [--device <box>|auto] [--crabbox] [--here] | the full vitest suite. Auto-picks a fleet worker by default — agents devices pick resolves the least-loaded reachable POSIX box from the same auto pool agents run --device auto uses, so role=worker/role=personal marks govern it. --device <box> names one, --crabbox uses a disposable crabbox via sandbox.sh, --here pins this machine. --shard <n> fans the suite across n workers and is the fastest option: the suite is throughput-bound (measured 3079s CPU at 11.5x parallelism on one box, so wall == CPU/workers), so dividing the CPU across machines is what shortens it — 3 boxes ≈ 93s, 9 ≈ 31s, against 269s on one. --shard takes a minimum of 2 (for a single worker use --device auto) — a floor --devices shares, so --devices onebox is refused too — and cannot be combined with --device/--here/--crabbox — conflicting flags fail loud rather than silently picking one by argument order. --devices a,b,c names the workers explicitly, which also skips the devices pick --json requirement. Needs agents ≥ 1.22.49 for devices pick --json. Never runs locally unless you pass --here, and fails loud rather than falling back. bun run test is the raw in-place runner the offload targets invoke; do not call it directly on a machine someone is using |
| CLI release | cli/scripts/release.sh <version> [--apply] | zero-config self-routing publish of @phnx-labs/agents-cli to npm: runnable from any fleet box with an empty environment — requires an exact-tree attestation (it runs no tests itself; release-attestation-produce.sh does, offloaded via test.sh), PR + CI, then a promote-only publish on the home base — any OS, mac-mini by default, overridable with --device <name> (RUSH-3026: the tarball no longer needs per-release signing); prints a [n/6] phase tracker. After publish it redeploys the managed share OG-cover Worker when worker-template.ts changed (--deploy-worker auto|on|off, default auto; PHNX-3403) so prod can't drift from the shipped template. Legacy @swarmify shim built for reference, not published |
| ext / agents-dbg build + release | in phnx-labs/agi-ext scripts/ | AGI EXT and the agents-dbg app moved with the extension repo (RUSH-3189) |
| computer-mac build | native/computer-mac/scripts/build.sh | Swift daemon |
| computer-win release | cli/scripts/publish-computer-win.sh <x.y.z> [--apply] | cuts computer-win/v<x.y.z>, which is what triggers the exe build + upload. Dry-run by default; refuses an existing tag (helper releases are immutable — the upload uses --clobber). Symmetric with publish-computer-helper-mac.sh. Bump the computer-win floor in cli/src/lib/helper-versions.ts afterwards — the tag makes the build downloadable, the floor makes a CLI ask for it |
| computer-mac release | cli/scripts/publish-computer-helper-mac.sh <x.y.z> | builds + signs + notarizes the helper and cuts computer-mac/v<x.y.z>. The version is the HELPER's and is required — it is deliberately not defaulted from cli/package.json, since the helper no longer shares the CLI's version line (PHNX-3228: it published to the CLI's v<version> tag, an address download.ts never requests). Refuses an existing tag; run it under agents secrets exec apple.com. Bump the computer-mac floor in cli/src/lib/helper-versions.ts afterwards |
Never install a dev build over the user's agents
This repo builds the agents command itself, so the usual "install it globally
and run it" advice is exactly wrong here — it overwrites the CLI the user (and
every other agent on the fleet) depends on. The general rule "no locally built
CLIs — install globally with npm i -g" does not apply to cli; this
paragraph overrides it for this repo.
To run your changes:
cd cli
bun run test # the suite, locally
scripts/test.sh # the suite, on an auto-picked fleet worker (default)
scripts/test.sh --device mark-1 # the suite, on an explicit fleet Linux box
scripts/test.sh --crabbox # the suite, on a disposable crabbox
scripts/install.sh --skip-tests # build + install this working tree
agents-dev sessions --active # drive YOUR build
agents sessions --active # the installed CLI, unaffected
Hard rules:
- Never
npm i -g/npm linkfrom the working tree. That writes over the registry install at$(npm root -g)/@phnx-labs/agents-cli. - Never create
~/.local/bin/{agents,ag,browser}. Those names belong to the registry install. A dev build answering toagentsmakes PATH order decide which code runs, and a cleaned dev prefix leaves the production command dangling.install.shpublishesagents-dev/ag-devinstead, and removes any such shadow link an older revision of it left behind. - The daemon is shared.
install.shleaves it on production code; pass--bounce-daemononly when you specifically need the secrets broker, browser IPC, and routines scheduler running your build — that affects the user's everydayagents, not justagents-dev. agents doctorreports abinary-shadowwarning when something has taken the name;agents fleet updatereports a dev-shadowed box as not upgraded.
The .agents/ workspace
The repo's own .agents/ dir is where agent working files go — use it instead of /tmp
or the repo root so the tree stays clean. What's committed vs gitignored is deliberate
(.gitignore):
| Path | Git | For |
|---|---|---|
.agents/worktrees/<slug>/ | ignored | PR-bound worktrees, one per change (see §Conventions) |
.agents/scratch/ | ignored | throwaway working files |
.agents/artifacts/<yyyy-mm-dd>/ | committed | every durable output — plans, reports, rendered visuals — filed under the day it was authored |
.agents/artifacts/private/ | ignored | durable output that must never land — personal data (contact lists, message-derived context, anything naming real people) |
Rule of thumb: ephemeral → the gitignored dirs; durable → .agents/artifacts/<yyyy-mm-dd>/.
One dated layout, no kind-based subdirs: a plan, a report, and a rendered visual authored on
the same day sit side by side in .agents/artifacts/2026-08-09/. Name the file for what it
is (plan-<slug>.html, <topic>-audit.md) and render HTML next to its Markdown source.
Everything committed here is public, so anonymize people, account handles, emails, device
names, session identifiers, local paths, tailnet addresses, and
absolute home paths before it lands. Never scatter scratch in /tmp or the repo root.
When the output is the personal data — a contact list, an outreach roster, anything
built from messages or an address book — anonymizing it would destroy it. Put that under
.agents/artifacts/private/, which .gitignore excludes, and keep it in the repo so the
work stays with the project instead of drifting into /tmp or a home directory.
Confidential GTM, monetization, pricing, revenue, and competitor material never lands in
public artifacts. Files whose names or content signal go-to-market strategy, pricing
models, revenue/ARR/MRR/churn figures, launch venues, GitHub stars playbooks, or competitor
intelligence belong in .agents/artifacts/private/ or a private repo — never in the
committed .agents/artifacts/<yyyy-mm-dd>/ tree. The required Linux PR check runs
scripts/guard-artifacts-confidential.ts and fails loud with the offending paths and the
right home if one is staged.
Conventions (repo-wide)
AGENTS.mdis the canonical memory file.CLAUDE.md/GEMINI.mdare symlinks to it (ls -la *.md). EditAGENTS.mdonly — a symlink target edited directly gets stomped on the next sync. This holds at the repo root and in every component.- Real services only — no mocking. Tests must exercise the actual critical path.
Test file sits next to source (
read.ts→read.test.ts); integration tests in each package'stests/. - PRs are auto-reviewed by
prix/code-reviewer(.github/rush.yml) — it reviews every PR tomainand posts its verdict as theprix-cloudcomment. That is the non-author review: rely on it and merge on green, don't spawn a redundant subagent reviewer. Review manually only ifprix-cloudhasn't posted after CI settles or flags something to dig into. (It's a cloud reviewer configured in.github/rush.yml, not a.github/workflows/Action.) The reviewer reads this file before every review and enforces the conventions in §Code review conventions — that block is what it checks the diff against, not just prose for humans.- Currently PAUSED (#1767). Since 2026-08-02 every run crashed on startup and each
failure minted + logged a live 1-year Anthropic token, so the trigger in
.github/rush.ymlis disabled until the upstream Rush Cloud agent-host capture bug is fixed. Until it is restored, the non-author review is a subagent reviewer (spawn one, have it verify the diff and post its verdict as a PR comment, then merge on green) — the documented fallback, not a redundant extra pass. Do not re-enable the trigger until #1767 is resolved.
- Currently PAUSED (#1767). Since 2026-08-02 every run crashed on startup and each
failure minted + logged a live 1-year Anthropic token, so the trigger in
- The default branch is untouchable. Every change is a git worktree + PR — never
edit or commit on
main. Worktrees live under.agents/worktrees/<slug>/. - Query GitHub over the REST API, not GraphQL — the GraphQL limit is fleet-shared
and cheap to exhaust.
gh pr view/list --json <field>andgh pr checksresolve through GitHub's GraphQL API (a 5,000-point/hr budget shared across every agent and machine on the account); a status poll loop drains it in minutes and then every agent'sghcalls 401 withAPI rate limit already exceeded, plus a separate secondary anti-abuse limit that trips on bursty calls. Use the REST API instead — a distinct 5,000-request/hr pool, and cacheable:- PR state / mergeability →
gh api repos/{owner}/{repo}/pulls/{n} --jq '{state,mergeable,mergeable_state}' - CI checks →
gh api repos/{owner}/{repo}/commits/{sha}/check-runs - review verdict →
gh api repos/{owner}/{repo}/pulls/{n}/reviewsReuse the canonical REST helpers incli/src/lib/github/rest.tsrather than hand-rolling:prHead(resolves the head SHA — PR state/mergeable itself comes from the rawpulls/{n}read above),rollupForSha/pendingCheckSuites(checks), andisRateLimitError(to back off). Do NOT copycli/src/lib/github/pr-mergeable.ts'slistMergeableRefs— it still callsgh pr list --json …statusCheckRollup, the exact GraphQL pattern this rule bans (migrating it ontorest.tsis part of PHNX-3557). NEVER poll in a tight loop — arm a daemon monitor (agents monitors add, 10-min cadence) or a single spaced check; on a rate-limit error, back off, do not retry. Mutations (gh pr merge,gh pr create, andgh pr comment) also resolve through GraphQL, not REST — but each is a single call, so it does not drain the budget the way a poll loop does. It will still fail once the shared GraphQL budget is already exhausted, so when the budget is tight prefer the REST equivalent — e.g. post a PR comment withgh api repos/{owner}/{repo}/issues/{n}/comments -f body='…'rather thangh pr comment. Making theghoverload auto-routepr view/list/pr checksto REST so no agent has to remember this is PHNX-3557.
- PR state / mergeability →
- The
swarm-ext://URI authority is the extension idswarmify.swarm-ext. The extension itself lives in phnx-labs/agi-ext (its frozen publish identity is documented there), but the CLI emits into that URI (cli/src/lib/terminal/inject.ts,backends/vscodium-agent.ts) — a CLI change must never assume a different extension id.
Code review conventions (the reviewer must enforce these)
prix/code-reviewer reads this section on every PR and flags any violation with a
file:line reference. These are blocking unless the PR description explicitly justifies
the exception.
- No stubs, placeholders, or unimplemented paths. A function that returns a canned
value,
throw new Error("not implemented"), an empty body where behavior is expected, a hardcoded mock standing in for a real call, or a// TODO/// FIXMEthat defers the actual work — none of these merge. Flag every one withfile:lineand the concrete behavior that's missing. Real implementation or nothing; a stub is a bug the diff is hiding, not progress. (If work genuinely must be deferred, it carries a linked tracking ticket in the comment and the PR says so — an intent-only// TODOwith no ticket does not qualify.) - Harness parity for cross-agent features. The CLI integrates many agent harnesses —
Claude, Codex, Gemini, Cursor, OpenCode, OpenClaw, Grok, Droid, Copilot, Goose,
Antigravity, Kimi, Warp (Oz), Forge. When a change adds or extends a capability that applies across
harnesses (subagents, hooks, MCP, allowlists, config sync, skills, workflows), it should
cover every harness the capability applies to — or the PR states which are out of
scope and why. Flag a diff that wires up two or three agents and silently skips the rest.
The registry-driven integrations are the pattern to follow (one table entry, e.g.
SUBAGENT_TARGETSincli/src/lib/subagents-registry.ts, gated bycapableAgents(...)— not near-identicalelse if (agent === '...')arms), and the completeness tests that pin the registry to the capability list must still pass. - The capability table stays truthful, in lockstep with the code. A harness that lacks
a capability must read as unsupported in its registry/map before any write path assumes
it, and a capability flips to supported only in the same PR that lands its real code
path — never ahead of it. A map asserting a capability the code doesn't implement is a
lying table; flag it with
file:line. - Surface parity for propagation / cross-cutting features. When a change adds data
that must ride the exec env or a spawn — actor/provenance, identity, session lineage,
credentials — it must be wired through every exec boundary that data is meant to
reach: the local spawn (
buildExecEnv),--deviceSSH dispatch,agents sshpassthrough, teams (local and remote teammates), and routines/cron — or the PR states which boundaries are out of scope and why. The tell is an absence at a remote call site (noSetEnv/--envforwarding across the SSH hop), so check the remote dispatch builders (cli/src/lib/hosts/dispatch.ts,hosts/remote-cmd.ts), not just the changed files — a diff that wires only the local path and silently drops the data at the first SSH boundary is incomplete. (RUSH-2028 fixed exactly this gap for actor provenance, which PR #1525 shipped local-only.) - Docs stay in sync with behavior. A change to a flag, command, config key, or
user-visible behavior updates the docs that cover it — the relevant component
AGENTS.md, itsREADME.md, andcli/docs/. Flag a diff that adds or changes a surface but leaves the docs describing the old behavior, and flag examples/command names in docs that the change has made stale. Exempt: pure internal refactors, test-only changes, self-evident renames. - Core command groups stay in sync with fleet guidance. A change to a core
group such as
sessions,devices,teams,run,secrets, orbrowserMUST audit the hooks, skills, commands, and rules in the companionphnx-labs/.agentsrepo (formerly.agents-system) that invoke or teach that group. Land the relevant companion edits in the same delivery and link both PRs; when the audit finds no consumer, state that explicitly in the agents-cli PR. A CLI surface is incomplete while the fleet guidance still teaches its old shape. - README / feature list for core features. A new core capability (a new top-level command or a substantial subsystem) updates the README and any feature/command index so it's discoverable — shipping it code-only, invisible to users, is incomplete.
- CHANGELOG for user-visible changes.
cliships as the published@phnx-labs/agents-clinpm package. A change to a flag, command, or behavior adds a CHANGELOG entry under the next version. Same exemptions as docs. - No fallback band-aids. Reject "just in case" branches, defensive lookups that paper over a data-shape inconsistency, or a second code path added to tolerate bad input. Standardize at the source — every fallback is a bug being hidden.
- Fail loud at boundaries. At an integration boundary (a harness the code path doesn't handle, an unsupported target, a missing prerequisite) the code raises a clear error or skips with a stated reason, never a silent no-op or a wrong path that looks like success. Flag any branch that swallows an unsupported case and returns as if it worked.
- New commands follow the CLI surface conventions. Flag a
new or changed command that flattens a verb colliding with an owned noun, leans on flag
soup where an object-in-path reads clearer, or ships a non-trivial tool on commander's
default help instead of a workflow-first
setHelpSectionsblock. - No second scheduler. A PR that adds a timer, watcher, or polling loop in
a UI surface (the AGI EXT repo, the menubar) that acts — spawns, resumes, kills, injects,
dispatches, rotates, fires a routine — rather than polling read-only state for
rendering is a double-fire bug in waiting. Flag it with
file:line. The action belongs in the CLI daemon or a CLI command; the UI may only render the state and wire controls to CLI calls. (Canonical incident: the ext watchdog rotate loop, 2026-08-03; canonical fix: PR #1914. See §Scheduling & execution singularity.) - Owner latency/packaging requirements R1-R5 are not negotiable in a diff. A PR
that raises a latency ceiling, deletes or softens a row in the
OWNER REQUIREMENTS table,
adds a rebuild of the menubar/computer helpers to the ordinary CLI release path (R3),
merges a helper release back into the CLI train (R4), or removes an auto-update path
(R5) is blocking unless the PR description states the regression outright and
links the owner's decision to accept it. Flag it with
file:line. Restating a bar as "aspirational", moving it to a comment, or marking it not-applicable counts as deleting it. - A required gate ships with its producer, in the same PR. Flag any diff that adds a mandatory check — attestation, manifest, signature, proof artifact — on the release or CI path without the automated thing that satisfies it. "Interim producer an operator runs by hand" is the anti-pattern (RUSH-2666 → PHNX-3696); it makes every future release a manual errand. A test that greps the script's source text does not count as covering the gate — the test must execute the path.
- No dead or commented-out code. Removed logic is deleted, not commented out "for later." git history is the archive.
- Tests exercise the real path. New behavior ships with a test that hits the actual critical path (no mocking — see the repo-wide rule above); a bugfix ships with a test that reproduces the bug. Flag new behavior or a fix that lands without one.
Security
No sensitive data in any DotAgents repo — all three (project / user / system)
are designed to be safely version-controlled. Use agents secrets (macOS
Keychain-backed, metadata only, never raw credentials on disk). Committed a secret by
accident? Rotate immediately — git history persists.
Never attach a raw session transcript. Before linking a session from a PR, issue,
or ticket, run agents sessions render <id> -o /tmp/session.md and attach or place
that redacted Markdown file in a secret gist. The renderer masks credential-shaped
values and local home paths by default. --no-redact output is local-only and must
never be shared.
Assets & voice
Only if you touch assets/ (incl. assets/demo/) or website/. Visual language is terminal-coded —
#0a0a0a bg, #a3e635 lime accent, JetBrains Mono for the wordmark + code, Inter for
prose. Voice is direct-developer: verb + artifact, no marketing claims — closer to a
man page than a landing pitch. (AGI EXT keeps its own swarmify publish identity,
documented in phnx-labs/agi-ext.)
Detailed design
cli/docs/ is the source-grounded reference. Start
with architecture.md for the CLI/extension layering
and the session mechanisms, then concepts.md for
the resource model and resolution semantics of the CLI.
Normative contract. The major subsystems carry a source-of-truth spec
(RFC-2119 MUST/SHOULD + Given/When/Then, cited to file:line) that a change MUST
NOT silently deviate from — cli/docs/specifications.md
(§Sessions ·
§Secrets ·
§Agent execution ·
§Scheduling & execution singularity ·
§Watchdog). Its
coverage inventory names every
other command group as documented-elsewhere or unspecified — check it before assuming
a surface has a contract.