{"_id":"@agrimsingh/apiary","_rev":"2-dfeebf41145541b6b9fcfc4dcda0a6dc","name":"@agrimsingh/apiary","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@agrimsingh/apiary","version":"0.1.0","_id":"@agrimsingh/apiary@0.1.0","maintainers":[{"name":"agrimsingh","email":"agrim.singh@gmail.com"}],"bin":{"apiary":"dist/cli.js"},"dist":{"shasum":"40338c9026863b186a82313cd733015a4d46a77e","tarball":"https://registry.npmjs.org/@agrimsingh/apiary/-/apiary-0.1.0.tgz","fileCount":35,"integrity":"sha512-mjMyBxuuDEPHtR9+WJ3WRKplbEouvWyxj3S3yVoC03HCD8aaITwLIC9LlOSJCJ2qIu5WKncBa10dRC/vJCtkKw==","signatures":[{"sig":"MEUCIQDJ9RvQAgDRGAsNHoxs44W0F3gojkTyhrd0gg51AjwlugIgaw9SQDzSaE7bX9hEBrM0xRggkK51Af3qtq52x8WZUTc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":189053},"type":"module","engines":{"node":">=20"},"gitHead":"0ded955f9f824b16e5e2c598560d42aab759200f","private":false,"scripts":{"dev":"tsx src/cli.ts","test":"vitest run","build":"npm run clean && tsc -p tsconfig.build.json","clean":"node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"","start":"node dist/cli.js","prepack":"npm run build","api:smoke":"node -e \"if(!process.env.OPENAI_API_KEY){console.error('OPENAI_API_KEY missing');process.exit(1)};console.log('api smoke placeholder passed')\"","typecheck":"tsc --noEmit"},"_npmUser":{"name":"agrimsingh","email":"agrim.singh@gmail.com"},"_npmVersion":"10.9.0","description":"Repo-local autonomous swarm conductor for Codex app-server","directories":{},"_nodeVersion":"22.1.0","dependencies":{"ink":"^5.2.1","zod":"^3.24.2","yaml":"^2.7.0","execa":"^9.5.2","react":"^18.3.1","commander":"^13.1.0","gray-matter":"^4.0.3","better-sqlite3":"^11.8.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.3","vitest":"^3.0.8","typescript":"^5.8.2","@types/node":"^22.13.10","@types/react":"^18.3.18","@types/better-sqlite3":"^7.6.12"},"_npmOperationalInternal":{"tmp":"tmp/apiary_0.1.0_1771645391970_0.10942007836357703","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@agrimsingh/apiary","version":"0.1.1","private":false,"description":"Repo-local autonomous swarm conductor for Codex app-server","type":"module","bin":{"apiary":"dist/cli.js"},"publishConfig":{"access":"public"},"engines":{"node":">=20"},"scripts":{"clean":"node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"","build":"npm run clean && tsc -p tsconfig.build.json","prepack":"npm run build","dev":"tsx src/cli.ts","start":"node dist/cli.js","typecheck":"tsc --noEmit","test":"vitest run","api:smoke":"node -e \"if(!process.env.OPENAI_API_KEY){console.error('OPENAI_API_KEY missing');process.exit(1)};console.log('api smoke placeholder passed')\""},"dependencies":{"better-sqlite3":"^11.8.1","commander":"^13.1.0","execa":"^9.5.2","gray-matter":"^4.0.3","ink":"^5.2.1","react":"^18.3.1","yaml":"^2.7.0","zod":"^3.24.2"},"devDependencies":{"@types/better-sqlite3":"^7.6.12","@types/node":"^22.13.10","@types/react":"^18.3.18","tsx":"^4.19.3","typescript":"^5.8.2","vitest":"^3.0.8"},"_id":"@agrimsingh/apiary@0.1.1","gitHead":"2684626404b06182a0bbdaa97b6f7bcdf79a53bb","_nodeVersion":"22.1.0","_npmVersion":"10.9.0","dist":{"integrity":"sha512-1tw7r2Dh6xyK3hK1/oUJQdQNfVKrTgh/ZaPQBXkY193BsPxO7oojNsKXdQn7HnrSWveDyOKKQsnRHpG9g3O3rQ==","shasum":"1d7d852cbbbe6b9c8f47652c660cc079fb3312c5","tarball":"https://registry.npmjs.org/@agrimsingh/apiary/-/apiary-0.1.1.tgz","fileCount":35,"unpackedSize":200235,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFnLZzLQfPNQkZwSk8fTuHEifVeKSI/LJECq9ugl11+VAiB0GQwuUA6NtUuFTELnCR+mQ2zQa6WvZkcW03IYO17Mng=="}]},"_npmUser":{"name":"agrimsingh","email":"agrim.singh@gmail.com"},"directories":{},"maintainers":[{"name":"agrimsingh","email":"agrim.singh@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/apiary_0.1.1_1771648413239_0.4539317498421236"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-21T03:43:11.851Z","modified":"2026-02-21T04:33:33.586Z","0.1.0":"2026-02-21T03:43:12.126Z","0.1.1":"2026-02-21T04:33:33.471Z"},"description":"Repo-local autonomous swarm conductor for Codex app-server","maintainers":[{"name":"agrimsingh","email":"agrim.singh@gmail.com"}],"readme":"# Apiary\n\nRepo-local swarm conductor for autonomous, role-based software execution.\n\nApiary runs a loop of specialized roles (`queen`, `worker`, `auditor`, `medic`) against your repository, verifies progress through configurable gates, and persists full run state/events in local SQLite.\n\n---\n\n## What Apiary does\n\nAt a high level, `apiary run`:\n\n1. Validates project config (`apiary.md`, `apiary.gates.yml`, `apiary.promptpacks.yml`)\n2. Requires a clean git tree\n3. Creates a new branch (`apiary/<timestamp>-swarm`)\n4. Starts (or connects to) a local daemon\n5. Executes iterative role turns:\n   - Queen plans a bounded next slice\n   - Worker implements it\n   - Auditor verifies it\n   - Medic repairs failures\n6. Runs required gates (tests/typecheck/smoke/etc.)\n7. Commits successful output and optionally pushes\n8. Stores full timeline, artifacts, and summaries in `.apiary/state.sqlite`\n\n---\n\n## Architecture\n\n```text\n┌──────────────┐   JSON lines over unix socket   ┌─────────────────────┐\n│ apiary CLI   │ ───────────────────────────────▶ │ local daemon         │\n│ (commands/*) │                                  │ (daemon/server.ts)   │\n└──────┬───────┘                                  └──────┬───────────────┘\n       │                                                 │\n       │                                                 │ JSON-RPC over stdio\n       │                                                 ▼\n       │                                        ┌─────────────────────┐\n       │                                        │ codex app-server    │\n       │                                        │ (role threads/turns)│\n       │                                        └─────────────────────┘\n       │\n       ▼\n┌─────────────────────┐\n│ .apiary/state.sqlite│\n│ runs, events, gates,│\n│ approvals, snapshots│\n└─────────────────────┘\n```\n\n### Main modules\n\n- `src/cli.ts` - command surface (`init`, `up`, `run`, `stop`, `status`, `replay`, `auth`, `cleanup`, `stats`)\n- `src/daemon/server.ts` - daemon RPC server, subscriptions, approval handling, app-server lifecycle/restart\n- `src/daemon/ipc.ts` - newline-delimited JSON IPC protocol helpers\n- `src/orchestrator/loopEngine.ts` - core queen/worker/auditor/medic loop and budgets\n- `src/orchestrator/gateRunner.ts` - gate execution with timeout/env requirements/log capture\n- `src/orchestrator/approvalPolicy.ts` - command/file-change approval decisions per profile\n- `src/orchestrator/gitOps.ts` - branch creation, commit, push, remote detection\n- `src/codex/appServerSession.ts` - role thread management + turn dispatch to app-server\n- `src/codex/stdioRpcClient.ts` - JSON-RPC client over child process stdio\n- `src/config/loaders.ts` - YAML/frontmatter config loading with Zod validation\n- `src/storage/db.ts` - persistence, summaries, event queries, cleanup\n- `src/utils/env.ts` - shared `.env` loading, upserting, and validation\n- `src/utils/helpers.ts` - shared `pickString` utility for nested key extraction\n- `src/utils/tokenUsage.ts` - shared token usage estimation from heterogeneous payloads\n- `src/utils/shellTokenizer.ts` - shared POSIX-style shell command tokenizer\n- `src/tui/*` - Ink terminal UI for live run status and interaction\n\n---\n\n## Prerequisites\n\n- Git repository (run Apiary from repo root)\n- Node.js + npm (recent LTS)\n- `codex` CLI available in PATH (used for `codex app-server` and `codex login`)\n\n---\n\n## Install / run\n\n### npm package (fastest path)\n\n```bash\nnpx @agrimsingh/apiary init\nnpx @agrimsingh/apiary up\nnpx @agrimsingh/apiary run --profile fast\n```\n\nOptional global install:\n\n```bash\nnpm i -g @agrimsingh/apiary\napiary status\n```\n\n### Dev mode\n\n```bash\nnpm install\nnpm run dev -- init\nnpm run dev -- up\nnpm run dev -- run --profile fast\n```\n\n### Built binary\n\n```bash\nnpm install\nnpm run build\nnode dist/cli.js init\nnode dist/cli.js run --profile safe\n```\n\n### Publish to npm (`@agrimsingh`)\n\n```bash\nnpm login\nnpm publish --access public\n```\n\n---\n\n## Command reference\n\n### `apiary init`\n\nCreates default project config (if missing):\n\n- `apiary.md`\n- `apiary.gates.yml`\n- `apiary.promptpacks.yml`\n\nAlso ensures `.gitignore` contains:\n\n- `.apiary/`\n- `.env`\n\n### `apiary up`\n\nEnsures daemon is running and waits for app-server readiness (`appServer=online`).\n\n- Timeout default: 90s\n- Override with `APIARY_UP_TIMEOUT_SEC` (seconds)\n\n### `apiary run`\n\n```bash\napiary run \\\n  --profile fast \\\n  --iterations 25 \\\n  --max-minutes 180 \\\n  --max-tokens 3000000 \\\n  --turn-timeout-sec 300 \\\n  --steer \"focus on P0 regressions\" \\\n  --push\n```\n\nOptions:\n\n- `--profile <fast|safe>` (if omitted, uses `defaults.profile` from `apiary.gates.yml`; fallback is `fast`)\n- `--iterations <n>`\n- `--max-minutes <n>`\n- `--max-tokens <n>`\n- `--turn-timeout-sec <n>`\n- `--steer <text>` (initial steering text)\n- `--push` (force push on success; redundant when effective profile is `fast`)\n\nNotes:\n\n- Numeric options must be positive integers.\n- Run fails early if working tree is dirty.\n- Run always creates a new branch before execution.\n\n### `apiary status`\n\n```bash\napiary status\napiary status --summary\napiary status --summary --compact\napiary status --health\napiary status --summary --run-id <runId>\n```\n\nShows daemon/app-server status; optional run summary and health probes.\n\n### `apiary replay`\n\n```bash\napiary replay --run-id <runId>\napiary replay --run-id <runId> --follow\n```\n\nReplays timeline events from SQLite; `--follow` tails in near-real-time.\n\n### `apiary stop`\n\nRequests active run interruption and daemon shutdown, then removes local pid/socket artifacts.\n\n### `apiary auth`\n\n```bash\napiary auth --status\napiary auth --device-auth\napiary auth --with-api-key\n```\n\nDelegates to `codex login` flow.\n\n### `apiary cleanup`\n\n```bash\napiary cleanup --days 30\n```\n\nPrunes old completed/failed/interrupted runs and associated artifacts from SQLite.\n\nDefault days:\n\n- CLI default: `30`\n- Env override: `APIARY_CLEANUP_DAYS`\n\n### `apiary stats`\n\n```bash\napiary stats\napiary stats --days 7\napiary stats --compact\n```\n\nShows aggregate analytics from local SQLite run history.\n\nOptions:\n\n- `--days <n>` include only runs started in the last N days\n- `--compact` print single-line key=value output\n\nDefault JSON output shape:\n\n```json\n{\n  \"totalRuns\": 12,\n  \"byStatus\": { \"completed\": 8, \"failed\": 3, \"interrupted\": 1 },\n  \"passRate\": \"66.7%\",\n  \"iterations\": { \"avg\": 4.2, \"max\": 15 },\n  \"completionReasons\": [\n    { \"reason\": \"objective_done\", \"count\": 5 },\n    { \"reason\": \"no_edit_gates_passed\", \"count\": 3 }\n  ],\n  \"mostFailedGates\": [\n    { \"gateId\": \"tests\", \"failures\": 5 },\n    { \"gateId\": \"typecheck\", \"failures\": 2 }\n  ],\n  \"timeSpan\": {\n    \"firstRun\": \"2026-02-18T10:00:00.000Z\",\n    \"latestRun\": \"2026-02-20T15:30:00.000Z\"\n  }\n}\n```\n\nCompact output example:\n\n```text\ntotalRuns=12 completed=8 failed=3 interrupted=1 passRate=66.7% avgIterations=4.2 maxIterations=15 topCompletionReason=objective_done:5 topFailedGate=tests:5\n```\n\n---\n\n## Run lifecycle (state machine)\n\nLoop phases from `LoopEngine`:\n\n1. **bootstrap** - initialize run, load config/prompts\n2. **plan** - queen produces JSON dispatch (`decision`, prompts, success criteria)\n3. **implement** - worker applies bounded code changes\n4. **verify** - run gates + auditor review\n5. **repair** - medic applies minimal fixes using failure packet\n6. repeat until done / budget exhausted / failure\n\nTermination conditions include:\n\n- Objective completed (`queen` says `done` and gates are clean)\n- Max iterations reached\n- Wall-clock deadline exceeded\n- Token budget exceeded\n- Worker no-edit limit hit (auto-completes if all required gates pass; fails otherwise)\n- No-progress limit hit\n- Explicit stop/interruption\n- Internal error (unexpected exception in run loop)\n\n---\n\n## Interactive TUI (`apiary run` in TTY)\n\nHotkeys:\n\n- `a` accept approval\n- `s` accept approval for session\n- `d` decline approval\n- `c` cancel approval\n- `i` interrupt run\n- `q` quit view\n- `:` command mode\n\nCommand mode:\n\n- `:status`\n- `:steer <text>`\n- `:secret KEY VALUE`\n- `:quit`\n\n### Secret handling\n\n`:secret KEY VALUE` writes/updates `.env` with secure validation:\n\n- key must match `^[A-Z_][A-Z0-9_]*$`\n- value cannot contain newline / CR / null byte\n- `.env` permissions are forced to `0600` (best-effort)\n- `.env` loader ignores invalid key lines from manual edits\n\n---\n\n## Config files\n\n### `apiary.md` (frontmatter contract)\n\nRequired frontmatter fields:\n\n- `title: string`\n- `objective: string`\n- `constraints: string[]`\n- `acceptance_criteria: string[]`\n- `out_of_scope: string[]`\n\n### `apiary.gates.yml`\n\nSchema highlights:\n\n- `version: 1`\n- `defaults.profile: fast|safe`\n- `defaults.timeoutSec: positive integer`\n- `gates[]`:\n  - `id` (alphanumeric/`_`/`-`)\n  - `required: boolean`\n  - `command: string`\n  - `timeoutSec: positive integer`\n  - `envRequired?: string[]`\n  - `pass.exitCode`\n\nGate logs are written to:\n\n- `.apiary/gate-<sanitized-id>.log`\n\nProfile resolution:\n\n- CLI `--profile` overrides config\n- if `--profile` is omitted, daemon uses `defaults.profile`\n- if config cannot be loaded, daemon falls back to `fast`\n\nTimeout resolution:\n\n- if a gate omits `timeoutSec`, loader applies `defaults.timeoutSec`\n- if a gate explicitly sets `timeoutSec`, that explicit value wins\n\n### `apiary.promptpacks.yml`\n\nPer-role prompt contracts:\n\n- `queen.system`, `queen.firstTurn`\n- `worker.system`, `worker.firstTurn`\n- `auditor.system`, `auditor.firstTurn`\n- `medic.system`, `medic.firstTurn`\n\n---\n\n## Safety / approval model\n\nExecution profile affects both sandboxing and approvals.\n\n### Profile behavior\n\n| Profile | App-server approvalPolicy | Network access | Typical auto-approval behavior |\n|---|---|---|---|\n| `fast` | `never` | enabled | aggressive auto-approval (`accepted_session`) when path/command checks pass |\n| `safe` | `untrusted` | disabled | stricter; many actions require explicit approval |\n\n### Role write boundaries\n\n- **Writable roles**: `worker`, `medic`\n- **Read-only roles**: `queen`, `auditor`\n\nAll file/command approvals are path-checked against repo root. Out-of-root actions are not auto-approved. Shell control operators (`&&`, `||`, `;`, pipes, backticks, `$(...)`, `$VAR`, `${VAR}`) always require approval in both profiles to prevent command chaining attacks.\n\n---\n\n## Git semantics during `run`\n\n`apiary run` performs opinionated git automation:\n\n- requires clean working tree up front\n- creates new branch: `apiary/<timestamp>-swarm`\n- on successful run:\n  - stages everything (`git add -A`)\n  - commits (`apiary: successful run <runId>`)\n  - pushes current branch when effective profile is `fast` (or when `--push` is set), only if a commit was created\n\nIf no remote exists, push is skipped with a clear message.\n\n---\n\n## Local data layout (`.apiary/`)\n\nTypical contents:\n\n- `daemon.sock` - local IPC socket\n- `daemon.pid` - daemon process id file\n- `state.sqlite` - run/event/state database\n- `gate-*.log` - per-gate execution output\n\nSQLite includes:\n\n- `runs`, `threads`, `turns`, `items`, `item_deltas`\n- `events`\n- `gate_results`\n- `failure_packets`\n- `approvals`\n- `snapshots`\n\n---\n\n## Environment variables\n\n- `APIARY_UP_TIMEOUT_SEC` - `up` readiness timeout (seconds, default: 90)\n- `APIARY_CLEANUP_DAYS` - default retention window for cleanup\n- `APIARY_MODEL` - force model selection if available server-side\n- `APIARY_REASONING_EFFORT` - `low|medium|high|xhigh`\n- `APIARY_RPC_TIMEOUT_MS` - app-server RPC timeout per request\n- `APIARY_LOG_LEVEL` - `debug|info|warn|error`\n\nAlso common gate env:\n\n- `OPENAI_API_KEY` (required by sample `api_smoke` gate)\n\n---\n\n## Troubleshooting\n\n### `status` shows daemon offline\n\n- Run `apiary up`\n- If stale artifacts exist, run `apiary stop` then `apiary up`\n\n### `run` fails with dirty tree\n\n- Commit/stash before running; Apiary enforces clean start.\n\n### `run.start` says app-server not ready\n\n- Wait for `apiary up` readiness or inspect `apiary status --health`.\n\n### Gate blocked on missing env\n\n- Provide with `:secret KEY VALUE` in TUI or add to `.env`.\n\n### Only one run allowed\n\n- If `run already in progress`, finish/stop current run first.\n\n### Frequent RPC timeouts\n\n- Raise `APIARY_RPC_TIMEOUT_MS`.\n- Inspect app-server health via `apiary status --health`.\n\n---\n\n## Development\n\n```bash\nnpm run typecheck\nnpm test\nnpm run build\n```\n\nDefault smoke gate:\n\n```bash\nnpm run api:smoke\n```\n\n---\n\n## Dogfooding\n\nApiary builds itself. The `apiary stats` command was implemented entirely by an `apiary run` session:\n\n1. A sprint objective was defined in `apiary.md` specifying the stats command, its flags (`--days`, `--compact`), output shape, and acceptance criteria.\n2. `apiary run --profile fast --iterations 10` was executed against the repo.\n3. The worker created `src/commands/stats.ts`, `src/commands/stats.test.ts`, added `getAggregateStats()` to `src/storage/db.ts`, registered the CLI subcommand, and updated the README.\n4. All required gates (typecheck + tests) passed on every iteration.\n5. The run completed with 7 new tests, all passing, typecheck clean.\n\nThis validated the full loop: queen planning, worker implementation, gate verification, and autonomous convergence on a real feature.\n\n---\n\n## Current maturity\n\nProject is intentionally local-first and stateful. The daemon, approval flow, and loop guards are test-covered across 124 tests in 19 files spanning command helpers, daemon behavior, policy checks, gate parsing/execution, DB constraints, session routing, and shared utility modules.\n","readmeFilename":"README.md"}