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 CLIcli, 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)
ComponentWhat it isRead
cliThe CLI — version mgmt, config sync, sessions, teams, cloud, browser, computer, secretsAGENTS.md · README.md
phnx-labs/agi-extAGI EXT VS Code extension (own repo) — agent terminals as tabs, Fleet dashboard, dispatchthat repo's AGENTS.md
native/computer-macmacOS agents computer backend (Swift)AGENTS.md · README.md
native/computer-winWindows agents computer backend (C#/.NET)AGENTS.md · README.md
packages/session-trackerLive-session writer (SessionStart hook)AGENTS.md · README.md
packages/agi-cliDeprecated alias — re-exports the canonical CLIREADME.md
packages/swarmify-mirrorDeprecated npm-redirect stubREADME.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 (ResourceKind in cli/src/lib/resources.ts): rules (this AGENTS.mdCLAUDE.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). plugins is a capability/sync kind (staleness ALL_RESOURCE_KINDS), not a ResourceKind subdirectory.
  • One execution engine. Every agent invocation goes through one path — buildExecEnvexecAgent / runWithFallback in cli/src/lib/exec.ts, entered via agents run. Each agent version runs in an isolated version home (HOME swapped before exec) so configs never bleed between versions.
  • Real-world tool surfaces. agents browser (web) and agents computer (native desktop, backed by the native/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 by agents 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 teams runs 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. cli owns 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 projects agents sessions watch --json and 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 /inject verb 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's O_EXCL precedent), 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 is cli/docs/credential-management.md invariant 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>, not agents 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 --json for machine callers. Flag soup burns tokens and produces wrong invocations.
  • browser and computer are 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 examples block (and notes for prereqs and follow-ups) via setHelpSections (cli/src/lib/help.ts), so --help renders 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).

#RequirementBarStatus 2026-09-01
R1Required CI check, event → terminal state< 60 sp50 120 s · p90 133 s (26 green runs on main)
R2Ordinary release, start → registry visible + install smoke< 60 snever completes unattended — wedges on a missing producer (PHNX-3696)
R3A CLI release rebuilds nothing but the CLI — no menubar, no computer helper, no signing, no notarization on the ordinary pathabsoluteheld and pinnedcli/scripts/release.test.ts §"an ordinary release is CLI-only" behaviorally asserts no helper-manifest touch and --with-helpers defaulting OFF; the no-rebuild/no-notarize claim is pinned separately in §"release.sh attestation promotion (RUSH-2666)"
R4AGI Menu and the computer helpers release separately, on their own cadence and their own tagsabsoluteheld — menubar/v*, keychain/v*, computer-mac/v*, computer-win/v* with floors in cli/src/lib/helper-versions.ts
R5An installed CLI and its installed helpers auto-update from the public channelabsoluteheld — the CLI checks registry.npmjs.org once per 24h and self-installs (cli/src/lib/self-update.ts, entered from bootstrap.ts checkForUpdates(), opt out with AGENTS_CLI_DISABLE_AUTO_UPDATE=1; agents upgrade --yes is the non-interactive path). Helpers self-download against the floors in cli/src/lib/helper-versions.ts. Homebrew is not a channel today; npm is

R1 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 install and ~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 drives win-mini over the tailnet on [self-hosted, crabbox-ci, tailnet] and deliberately carries no pull_request trigger 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.yml runs a best-effort Windows smoke only on push to main, with continue-on-error, and the required Tests / test job 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.

TaskScriptContract
CLI buildcli/scripts/build.sh [<version>] [--clean]builds into cli/dist
CLI dev installcli/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 testscli/scripts/test.sh [--shard <n>] [--devices a,b,c] [--device <box>|auto] [--crabbox] [--here]the full vitest suite. Auto-picks a fleet worker by defaultagents 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 releasecli/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 + releasein phnx-labs/agi-ext scripts/AGI EXT and the agents-dbg app moved with the extension repo (RUSH-3189)
computer-mac buildnative/computer-mac/scripts/build.shSwift daemon
computer-win releasecli/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 releasecli/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 link from 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 to agents makes PATH order decide which code runs, and a cleaned dev prefix leaves the production command dangling. install.sh publishes agents-dev / ag-dev instead, and removes any such shadow link an older revision of it left behind.
  • The daemon is shared. install.sh leaves it on production code; pass --bounce-daemon only when you specifically need the secrets broker, browser IPC, and routines scheduler running your build — that affects the user's everyday agents, not just agents-dev.
  • agents doctor reports a binary-shadow warning when something has taken the name; agents fleet update reports 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):

PathGitFor
.agents/worktrees/<slug>/ignoredPR-bound worktrees, one per change (see §Conventions)
.agents/scratch/ignoredthrowaway working files
.agents/artifacts/<yyyy-mm-dd>/committedevery durable output — plans, reports, rendered visuals — filed under the day it was authored
.agents/artifacts/private/ignoreddurable 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.md is the canonical memory file. CLAUDE.md / GEMINI.md are symlinks to it (ls -la *.md). Edit AGENTS.md only — 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.tsread.test.ts); integration tests in each package's tests/.
  • PRs are auto-reviewed by prix/code-reviewer (.github/rush.yml) — it reviews every PR to main and posts its verdict as the prix-cloud comment. That is the non-author review: rely on it and merge on green, don't spawn a redundant subagent reviewer. Review manually only if prix-cloud hasn'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.yml is 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.
  • 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> and gh pr checks resolve 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's gh calls 401 with API 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}/reviews Reuse the canonical REST helpers in cli/src/lib/github/rest.ts rather than hand-rolling: prHead (resolves the head SHA — PR state/mergeable itself comes from the raw pulls/{n} read above), rollupForSha / pendingCheckSuites (checks), and isRateLimitError (to back off). Do NOT copy cli/src/lib/github/pr-mergeable.ts's listMergeableRefs — it still calls gh pr list --json …statusCheckRollup, the exact GraphQL pattern this rule bans (migrating it onto rest.ts is 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, and gh 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 with gh api repos/{owner}/{repo}/issues/{n}/comments -f body='…' rather than gh pr comment. Making the gh overload auto-route pr view/list/pr checks to REST so no agent has to remember this is PHNX-3557.
  • The swarm-ext:// URI authority is the extension id swarmify.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/// FIXME that defers the actual work — none of these merge. Flag every one with file:line and 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 // TODO with 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_TARGETS in cli/src/lib/subagents-registry.ts, gated by capableAgents(...) — not near-identical else 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), --device SSH dispatch, agents ssh passthrough, 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 (no SetEnv/--env forwarding 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, its README.md, and cli/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, or browser MUST audit the hooks, skills, commands, and rules in the companion phnx-labs/.agents repo (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. cli ships as the published @phnx-labs/agents-cli npm 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 setHelpSections block.
  • 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.mdSessions · §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.