{"_id":"@anmho/symphony","_rev":"3-a0580e6e9f83ae44d2f6a3ad31a7247e","name":"@anmho/symphony","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@anmho/symphony","version":"0.1.0","license":"UNLICENSED","_id":"@anmho/symphony@0.1.0","maintainers":[{"name":"anmho","email":"andyminhtuanho@gmail.com"}],"bin":{"symphony":"dist/src/index.js"},"dist":{"shasum":"f68545f362d0232c71fa56b6574616859b659d6a","tarball":"https://registry.npmjs.org/@anmho/symphony/-/symphony-0.1.0.tgz","fileCount":45,"integrity":"sha512-4a/jZIuWAe0MjqcPLKJ7aBS25z634UM+2AIbE+k2T214snOA929GZMGbpjFqg4EsFGDpR06FK++rgFAbPfQyhw==","signatures":[{"sig":"MEUCIC4xzeFN6JxWemHX9GbHiGpFb2EARv/OL51nDzQcBCosAiEAka+dsHpE2p0v5F0Z6cYZVnleTpAqlblPk+h7TBbcKsY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":136756},"type":"commonjs","engines":{"node":">=22"},"scripts":{"dev":"tsx src/index.ts run","run":"tsx src/index.ts run","stop":"tsx src/index.ts stop","test":"vitest run","build":"tsc -p tsconfig.build.json","start":"tsx src/index.ts start","status":"tsx src/index.ts status","validate":"bun run typecheck && bun run test","typecheck":"tsc --noEmit","install:global":"bun install && bun run build && bun link","validate-config":"tsx src/index.ts validate-config"},"_npmUser":{"name":"anmho","email":"andyminhtuanho@gmail.com"},"_npmVersion":"11.9.0","description":"TypeScript implementation of a Symphony-style Codex orchestration service.","directories":{},"_nodeVersion":"24.14.0","dependencies":{"zod":"^4.1.13","pino":"^10.1.0","yaml":"^2.8.2","liquidjs":"^10.27.0","commander":"^14.0.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.6","vitest":"^4.0.14","typescript":"^5.9.3","@types/node":"^24.10.1"},"_npmOperationalInternal":{"tmp":"tmp/symphony_0.1.0_1779503393722_0.40689033315517853","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@anmho/symphony","version":"0.1.1","keywords":["codex","linear","symphony","worktrees"],"license":"UNLICENSED","_id":"@anmho/symphony@0.1.1","maintainers":[{"name":"anmho","email":"andyminhtuanho@gmail.com"}],"homepage":"https://www.npmjs.com/package/@anmho/symphony","bugs":{"url":"https://github.com/anmho/symphony/issues"},"bin":{"symphony":"dist/src/index.js"},"dist":{"shasum":"b48b5e9dc7f26da66a49c6c6b7f251fa282f8744","tarball":"https://registry.npmjs.org/@anmho/symphony/-/symphony-0.1.1.tgz","fileCount":51,"integrity":"sha512-8AgcZSTi2ke2tqfkWzwZp4RRuxnmnwyLhwV9i+1mlAhmle0pdA3dLJN/i959LgKq+CNorOzsLsR4SbKfIJmReg==","signatures":[{"sig":"MEQCIDLz+e2wrnGS0UcKghZxLY3F9zyxxGB53L66H92wHIl0AiBEY8ZSm7+xtk+xHrUPW44Ij5IAGdpHvL6CS6y5Ig8rJA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":257032},"type":"module","engines":{"node":">=22"},"gitHead":"83cd09627ca06436dbefdb3e7f77551882646743","scripts":{"dev":"tsx src/index.ts run","run":"tsx src/index.ts run","stop":"tsx src/index.ts stop","test":"vitest run","build":"tsc -p tsconfig.build.json","start":"tsx src/index.ts start","watch":"tsx src/index.ts watch","resume":"tsx src/index.ts resume","status":"tsx src/index.ts status","validate":"bun run typecheck && bun run test","typecheck":"tsc --noEmit","install:global":"bun install && bun run build && bun link","validate-config":"tsx src/index.ts validate-config"},"_npmUser":{"name":"anmho","email":"andyminhtuanho@gmail.com"},"repository":{"url":"git+https://github.com/anmho/symphony.git","type":"git"},"_npmVersion":"11.9.0","description":"Local TypeScript Symphony runner for Linear-driven Codex app-server work orchestration.","directories":{},"_nodeVersion":"24.14.0","dependencies":{"zod":"^4.1.13","pino":"^10.1.0","yaml":"^2.8.2","liquidjs":"^10.27.0","commander":"^14.0.2","@opentui/core":"0.2.15"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"bun@1.3.14","devDependencies":{"tsx":"^4.20.6","vitest":"^4.0.14","typescript":"^5.9.3","@types/node":"^24.10.1"},"_npmOperationalInternal":{"tmp":"tmp/symphony_0.1.1_1779574731738_0.3102704177658544","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@anmho/symphony","version":"0.1.2","description":"Local TypeScript Symphony runner for Linear-driven Codex app-server work orchestration.","type":"module","license":"UNLICENSED","packageManager":"bun@1.3.14","repository":{"type":"git","url":"git+https://github.com/anmho/symphony.git"},"homepage":"https://www.npmjs.com/package/@anmho/symphony","bugs":{"url":"https://github.com/anmho/symphony/issues"},"keywords":["codex","linear","symphony","worktrees"],"bin":{"symphony":"dist/src/index.js"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p tsconfig.build.json","dev":"tsx src/index.ts run","install:global":"bun install && bun run build && bun link","run":"tsx src/index.ts run","start":"tsx src/index.ts start","stop":"tsx src/index.ts stop","status":"tsx src/index.ts status","resume":"tsx src/index.ts resume","watch":"tsx src/index.ts watch","validate-config":"tsx src/index.ts validate-config","test":"vitest run","typecheck":"tsc --noEmit","validate":"bun run typecheck && bun run test"},"dependencies":{"@opentui/core":"0.2.15","commander":"^14.0.2","liquidjs":"^10.27.0","pino":"^10.1.0","yaml":"^2.8.2","zod":"^4.1.13"},"devDependencies":{"@types/node":"^24.10.1","tsx":"^4.20.6","typescript":"^5.9.3","vitest":"^4.0.14"},"engines":{"node":">=22"},"gitHead":"a56fdd1f2d83ceba53b4f88d292c7b2f098ce9ec","_id":"@anmho/symphony@0.1.2","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-VSG2/Xn4Lr4PvjzhiJCA+FaIY2xLvPOnIsijqqRYNIlVwl4qWpNH9CP62GqRC9xTXuM9S4SXURuhdpomPy1Omg==","shasum":"a476dfe7e58f300e619bf657c5671496295a0b3d","tarball":"https://registry.npmjs.org/@anmho/symphony/-/symphony-0.1.2.tgz","fileCount":63,"unpackedSize":330113,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICI4V3GymNReA7cUgfBvbyuctCqFLkpqQ/DXYg25t4b8AiBNymyQDow+BpqJsQiUjGxHo88Ps6Y6IQVFI10km9UfCw=="}]},"_npmUser":{"name":"anmho","email":"andyminhtuanho@gmail.com"},"directories":{},"maintainers":[{"name":"anmho","email":"andyminhtuanho@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/symphony_0.1.2_1779594240610_0.2375314014910952"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-23T02:29:53.611Z","modified":"2026-05-24T03:44:00.850Z","0.1.0":"2026-05-23T02:29:53.870Z","0.1.1":"2026-05-23T22:18:51.894Z","0.1.2":"2026-05-24T03:44:00.755Z"},"bugs":{"url":"https://github.com/anmho/symphony/issues"},"license":"UNLICENSED","homepage":"https://www.npmjs.com/package/@anmho/symphony","keywords":["codex","linear","symphony","worktrees"],"repository":{"type":"git","url":"git+https://github.com/anmho/symphony.git"},"description":"Local TypeScript Symphony runner for Linear-driven Codex app-server work orchestration.","maintainers":[{"name":"anmho","email":"andyminhtuanho@gmail.com"}],"readme":"# Symphony\n\n[GitHub: anmho/symphony](https://github.com/anmho/symphony) · [npm: @anmho/symphony](https://www.npmjs.com/package/@anmho/symphony)\n\nLocal TypeScript Symphony runner for Linear-driven Codex app-server work orchestration.\n\nThis repo is a runnable TypeScript implementation of a Symphony-style harness:\n\n- Linear supplies work.\n- A single local runner owns in-memory scheduling, retries, and rate-limit parking.\n- Git worktrees isolate each issue.\n- Local `codex app-server` performs the implementation work inside each worktree.\n- `WORKFLOW.md` owns repo-specific policy, hooks, and the agent prompt.\n\nThe default posture is high-trust local execution. Symphony is intended for repos and machines where unattended Codex work is acceptable.\n\n## Docs\n\n- [Symphony smoke](docs/symphony-smoke.md)\n\n## Requirements\n\n- Node 22+\n- Bun\n- Codex CLI with `codex app-server`\n- Linear API key\n\n## Quick Start\n\nInstall dependencies:\n\n```sh\nbun install\n```\n\nCreate a workflow file:\n\n```sh\ncp WORKFLOW.example.md WORKFLOW.md\n```\n\nThis repo also includes a real root `WORKFLOW.md` for ANM team execution. It polls ANM tickets, but only dispatches issues explicitly labeled with both `symphony` and a configured `repo:<key>` route label.\n\nSet your local environment, or put these values in `~/.config/symphony/config.json`:\n\n```sh\nexport LINEAR_API_KEY=lin_api_...\nexport PROJECTS_ROOT=/Users/andrewho/repos/projects\n```\n\nExample user config:\n\n```json\n{\n  \"workflow\": \"/Users/andrewho/repos/projects/symphony/WORKFLOW.md\",\n  \"env\": {\n    \"PROJECTS_ROOT\": \"/Users/andrewho/repos/projects\"\n  },\n  \"secrets\": {\n    \"LINEAR_API_KEY\": {\n      \"command\": \"vault kv get -mount=secret -field=api_key prod/providers/linear\"\n    }\n  }\n}\n```\n\nUser config lives under `~/.config/symphony` because it is machine-local configuration. The repo `.symphony/` directory is reserved for runtime workspaces and state.\n\nValidate the workflow config:\n\n```sh\nbun run validate-config -- --workflow WORKFLOW.md\n```\n\nInstall the `symphony` command globally:\n\n```sh\nbun run install:global\n```\n\nStart the runner:\n\n```sh\nsymphony start --workflow WORKFLOW.md\n```\n\nCheck workflow status:\n\n```sh\nsymphony status\n```\n\nMonitor agents in a k9s-style terminal UI:\n\n```sh\nsymphony watch\n```\n\n`symphony watch` reads the same local status endpoint as `symphony status`, but presents agents as a continuously refreshed resource table. It supports `j/k` or arrow navigation, `:` command mode, `/` filtering, `?` help, `d` describe, `l` logs, `s` steer, `r` retry/resume selected, `ctrl-r` refresh, and `q` quit. The watch UI expands to fill the terminal width and height (k9s-style flex columns and dynamic log viewport).\n\n### macOS menu bar monitor\n\nNative **Swift/SwiftUI** app (not Electron) — lightweight menu bar popover with clickable Linear ticket links.\n\n**Install:**\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/anmho/symphony/main/macos/SymphonyMenuBar/install.sh | bash\n```\n\nOr download a `.dmg` from [GitHub Releases](https://github.com/anmho/symphony/releases) (`menubar-v*` tags).\n\nSee [macos/SymphonyMenuBar/README.md](macos/SymphonyMenuBar/README.md) for build, Homebrew cask, and settings.\n\nFollow an agent's public work stream from the CLI:\n\n```sh\nsymphony logs ANM-123 --follow\nsymphony logs --all --follow\n```\n\nThe work stream includes public assistant message deltas, command/tool lifecycle events, runner events, errors, and rate-limit notices. It does not expose hidden chain-of-thought; reasoning appears only when Codex emits an explicit public summary.\n\nQueue guidance for the next turn on an issue:\n\n```sh\nsymphony steer ANM-123 \"focus on the keyboard regression before touching tests\"\n```\n\nIf Codex becomes available before a reported reset time, force a probe immediately:\n\n```sh\nsymphony resume\n```\n\nStop it:\n\n```sh\nsymphony stop\n```\n\n## Runtime Model\n\n`Orchestrator` reloads `WORKFLOW.md`, polls one Linear project, reconciles active runs, and dispatches issue workers up to `agent.max_concurrent_agents` defaulting to `5`.\n\nEach issue worker prepares a Git worktree, runs hooks, starts or resumes a Codex app-server thread, and loops while the Linear issue remains active. When Codex reports a rate limit, the runner pauses new Codex launches until the reset time in the current process.\n\nRuntime state is intentionally in-memory to match the OpenAI Symphony spec. Restart recovery is tracker/filesystem-driven: active Linear issues and existing worktrees determine what gets picked back up.\n\n`symphony start` launches a normal detached user process and writes pid/log files under `~/.symphony`. It does not install a LaunchAgent or auto-start on login.\n\n`symphony status`, `symphony watch`, and `symphony logs` expose the runner's observability surface: active issue runs, retry queue, Codex thread and turn IDs, app-server PIDs, event cursors, per-issue work-log paths, rate-limit parking, and config reload errors. Daemon process logs remain available under `~/.symphony/symphony-<port>.log`.\n\nRate-limit handling is intentionally different from ordinary failure retry. Symphony parks new launches until Codex's reported reset time, but also probes parked runs every `agent.rate_limit_probe_interval_ms` with per-issue jitter so work can resume if access returns earlier than the reported reset. The default probe interval is five minutes.\n\nThe committed ANM workflow stores issue worktrees under `.symphony/workspaces/<repo-key>/<issue-id>`. Per-issue public work streams are stored as JSONL under `.symphony/events/`, and queued steering state is stored under `.symphony/state/`. The `.symphony/` directory is ignored and used for local runtime state, not as the canonical workflow config.\n\n## Safety Notes\n\n- V1 does not destructively reset existing worktrees.\n- Terminal issues run `before_remove` and remove their managed worktree.\n- Runner comments in Linear are limited to operational breadcrumbs; Codex remains responsible for PR links and implementation handoff.\n- The default Codex approval policy is `never`, so use this only in trusted environments.\n\n## Validation\n\n```sh\nbun run validate\n```\n","readmeFilename":"README.md"}