# TOOLS.md — command and tool reference On-demand reference for agent sessions (conductors, bootstrapping agents, reviewers). `AGENTS.md` routes here; this file carries the depth: usage, inputs/outputs, exit codes, and safety notes for every entry point. Reading guide: system entry points are `scripts/*.sh` (bash) or invoked via `node scripts/mosaic-task.mjs` (node). Host-side helpers under `tools/` (tmux messaging, watchers, prose checker) are covered under Tools (host-side) below. Every script fails closed — missing or invalid configuration/policy refuses the operation with a nonzero exit and changes nothing. ## Lifecycle | Command | Purpose | Notes | |---|---|---| | `scripts/bootstrap.sh` | Create `~/.config/mosaic-dev/config.json` if absent | Idempotent; existing config validated, never rewritten | | `scripts/build.sh` | Build the release image | Tag derived from `RELEASE` + pinned pi version | | `scripts/hello.sh` | One-shot startup request | Prints model response on stdout | | `scripts/verify.sh` | Full gated test | Exit 0 only on exact `MOSAIC_HELLO_OK`; `EXPECTED_MARKER` overrides for negative drills | ## Tasks (missions, runs, evidence) | Command | Purpose | Notes | |---|---|---| | `scripts/run-task.sh run ` | Execute a task | Immutable run record under `/runs/` | | `scripts/run-task.sh validate ` | Strict validation | Writes nothing | | `node scripts/mosaic-task.mjs show ` | Inspect a run | Full record + snapshots + artifacts | | `node scripts/mosaic-task.mjs list` | List runs | task/workspace/session columns | | `node scripts/mosaic-task.mjs retry ` | Re-execute a run's snapshot | New run dir; `retriedFrom` lineage recorded | | `node scripts/mosaic-task.mjs prune [--keep=N] [--yes]` | Retention | Dry-run default; receipt in `runs/.pruned.log` | | `node scripts/mosaic-task.mjs resolve-role ` | Validate a role contract | Prints `MOSAIC_ROLE_TOOLS` / `MOSAIC_ROLE_NETWORK`; config-free | Task fields: `prompt` (required), `mission` (path), `expectExact`, `timeoutSeconds` (5–600), `workspace` (`:run` or named), `capabilities.tools` (allowlist: read write edit bash grep find ls), `session`, `sessionForkFrom` (requires `session`). Mission fields: `objective`, `directives[]`, optional governing `capabilities.tools`. Policy: a task may narrow a mission's tools, never widen; empty intersection = tool-free run. ## Agent (interactive TUI) ```bash scripts/agent.sh [--mission ] [--workspace ] [--session ] [--tools ] ``` Launches an interactive pi TUI inside the container with the four immutable contracts + optional mission + agent identity as its system prompt, persistent named session, optional workspace. Exit with `/quit`. A seat role (`agent.json` `role`) binds to `roles/.json` (M18): the contract's tools are a ceiling the seat definition or `--tools` may narrow, never escalate past. Missing/invalid contract refuses the launch; empty intersection = loud tool-free seat. An explicit `MOSAIC_AGENTS_DIR` override that cannot resolve the named seat also refuses (#46) — unset the override for the M13 plain governed TUI. `--auth ` injects `auth..json` (beside the active credential file) as the launch's `PI_AUTH_FILE`; a missing/invalid account refuses (M19). For native repository development, opt in with a **leading** `--host-dev`: ```bash scripts/agent.sh --host-dev darkwing [--fresh] [--check] [--soul FILE] [--constitution FILE] [--user FILE] ``` This mode delegates to `scripts/agent-host-dev.sh`, uses host Pi and repository tools/skills plus the development goal extension, and keeps its own sessions under `.pi/state//`. It uses native Pi authentication and does not run container release alignment or apply managed seat role ceilings. It is a host development session, not a sandboxed worker. Container-only flags such as `--auth`, `--mission`, and `--tools` are rejected in this mode. Omitting `--host-dev` retains the existing container lifecycle and policy checks; container failures never trigger a host fallback. Darkwing's agent-local shim selects host development explicitly. See `agents/darkwing/README.md`. ## Auth (credentials) Credential checkpoint over pi's auth model (one `auth.json` keyed by provider; resolution order `--api-key` → `auth.json` → env → models.json). No credential material is ever printed — provider names, credential types, and env var NAMES only. Ownership rule (#48): `~/.pi` is read-only to the stack, permanently. The only interaction is the existing read-only container mount of the default credential (`PI_AUTH_FILE`, default `~/.pi/agent/auth.json`). Mosaic-managed accounts live under the data root: `/auth/.json`, perms 0600 (mirroring `scripts/gitea-api.sh` hygiene — loose perms are flagged in listings and refused by `--auth`). | Command | Purpose | Notes | |---|---|---| | `scripts/auth.sh status` | Report both credential sources | Default harness credential (read-only) + mosaic-managed accounts; never prints material | | `scripts/auth.sh accounts` | List mosaic-managed accounts | Under the data root; marks the active one; flags non-0600 | `agent.sh --auth ` injects `/auth/.json` as the launch's `PI_AUTH_FILE`; missing/symlinked/non-0600 accounts refuse. Headless task runs keep the default credential. ## Release | Command | Purpose | Notes | |---|---|---| | `scripts/release.sh package` | Build + tag the release image | Tag: `mosaic-poc-agent:-r` | | `scripts/release.sh activate` | Health gate → atomic pointer swap | `--fault-injection` proves the refusal path | | `scripts/release.sh rollback` | Health-gated return to previous | Refuses if image missing | | `scripts/release.sh status` | Release, tag, active pointer, log | Safe on empty state | | `scripts/release.sh ensure` | Self-determination: align active pointer to `RELEASE` | Fast path restores a missing/mismatched pointer without a gate; slow path packages + health-gates first. Invoked automatically at launch | ## Conductor (worker patches) ```bash scripts/conductor-apply.sh [--dry-run] ``` Auto-applies a worker's patch under `roles/conductor-policy.json`: succeeded run → clean target tree → path allowlist → syntax gates → apply → policy suites → attribution commit. Any failure reverts. Push is never automatic. ## Maintenance | Command | Purpose | Notes | |---|---|---| | `scripts/reset.sh` | Delete the data root | Triple-safety-checked (path, symlink, ownership marker) | | `scripts/test-config.sh` | Config selftests (no Docker) | 24 cases | | `scripts/test-task.sh` | Task selftests + live cases | 90 cases | | `scripts/test-release.sh` | Release selftests | 14 cases | | `scripts/test-conductor.sh` | Auto-apply selftests (sandboxed) | 17 cases | | `scripts/test-auth.sh` | Auth checkpoint selftests (no Docker) | 13 cases | | `scripts/gitea-api.sh [body]` | Gitea API helper | Token never on argv/stdout | ## Tools (host-side) Host-side helpers under `tools/`, outside the `scripts/` command surface. Per-tool READMEs: `tools/tmux/README.md` and `tools/unslop-hook/README.md`. | Command | Purpose | Notes | |---|---|---| | `tools/tmux/agent-send.sh` | Inter-agent tmux message with addressing preamble | Reliable submit (bracketed paste, Enter flush, draft detection); ships `send-message.sh` over ssh for remote panes (remote needs only bash + tmux + base64) | | `tools/agent-watch/agent-watch.sh` | Condition watcher per agent seat | One transient systemd `--user` timer + service per watch; fires `agent-send.sh` when the condition command exits 0 | | `node tools/unslop-hook/unslop-check.js ` | Mechanical AI-tell prose check | Dependency-free node CLI + module driven by `lists.json`; `extension.ts` is the pi extension wrapper | `agent-send.sh` prepends the preamble `[: -> :]`; `-C`/`--class` adds a ` class=` token (`terminal-log`, `actionable`, `human`, `reaction`, `digest`; consumers treat an absent class as `actionable`). Flags: `-s` dst session (required) · `-H` ssh target for a remote pane · `-L` named tmux socket · `-n` dst hostname for the preamble · `-m`/`-f`/stdin message body · `-S` source-label override · `-r N` Enter-flush attempts (default 2) · `-v` verbose · `-h` help. Exit codes: `0` delivered/queued · `1` target not found · `2` still draft · `3` usage error · `4` ambiguous socket (the session exists on more than one tmux server; disambiguate with `-L` or `MOSAIC_TMUX_SOCKET`). `agent-watch.sh` subcommands: `start --name --session --when '' --message ` with `--class`, `--interval` (default 30), `--timeout` (default 3600), `--repeat`, `--quiet-timeout`, `--socket` · `list` · `status [--json]` · `stop ` · `log ` · `meta-install [--interval 300] [--unit-name ]` · `meta-remove [--unit-name ]`. Interval floor is 10s (a watcher is a fallback cadence, never a tight poll); hidden `_tick`/`_scan` subcommands run inside the systemd services. `status` exit codes: `0` clean · `3` any stale watch or dead meta-watch · `6` systemd user bus unreachable. Delivery goes through `agent-send.sh`; rc `2` means the text reached the pane as an unsubmitted draft, which counts as delivered and is not retried (other failures retry twice, then the watch gives up). Watches are one-shot by default; `--repeat` re-arms. Notices carry a `[watch:]` prefix. `unslop-check.js` checks a file (or stdin) against the word, phrase, punctuation-density, and pattern lists in `lists.json`, stripping fenced and inline code first so a quoted mention never flags. Invocation: `node tools/unslop-hook/unslop-check.js `; `UNSLOP_LISTS=` overrides the lists location. Exit codes: `0` clean · `1` violations (findings printed as JSON on stdout) · `2` gate broken (invalid lists or unreadable input; error on stderr, never a clean verdict). ## Exit-code convention `0` success · `1` operation failed · `2` invalid data/configuration · `3` configuration missing for a read operation · `4` usage/file/environment problem. Scripts print diagnostics on stderr; model responses (and only model responses) on stdout.