{"_id":"0x2f","_rev":"7-7b73260dbbbca1a1e1c8b7043128fb3f","name":"0x2f","dist-tags":{"latest":"0.1.3"},"versions":{"0.1.0":{"name":"0x2f","version":"0.1.0","keywords":["cli","tasks","coding-agents","local-first","claude-code","deepseek-harness","acp","gemini-cli"],"license":"MIT","_id":"0x2f@0.1.0","maintainers":[{"name":"bogart91","email":"alexander.s.malinin@gmail.com"}],"homepage":"https://github.com/AleksandrMalinin/0x2f#readme","bugs":{"url":"https://github.com/AleksandrMalinin/0x2f/issues"},"bin":{"2f":"src/cli.mjs"},"dist":{"shasum":"19ceb0bb1507c5a5d6655c8440d958088f8493f7","tarball":"https://registry.npmjs.org/0x2f/-/0x2f-0.1.0.tgz","fileCount":67,"integrity":"sha512-CN7qmi7WC0xnekrGNKO+8ieLOW4CYi8pstcLQeB0MBdFWEEaYIe47X3n+/8YpoBJrKIewKrLWP0WsYZIY/Sfdw==","signatures":[{"sig":"MEUCIQCcfots8mw/+br9+O9J8rSl1yWuCSrbk0RWouQa+plXywIgNJCvtX4f+NmfEsZ0R6pI/yqLRoWKb6eyiA9jgQA2XeA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":848674},"type":"module","engines":{"node":">=20"},"gitHead":"3779f9f5f1953d379166d5394abd233d3950a67e","scripts":{"test":"node --test \"test/*.test.mjs\"","check":"find src test relay scripts deploy -name node_modules -prune -o \\( -name '*.mjs' -o -name '*.js' \\) -exec node --check {} \\; && python3 -c \"import ast,glob,sys; [ast.parse(open(f, encoding='utf-8').read(), f) for f in glob.glob('test/tui-pty/*.py')]\"","start":"node src/cli.mjs","test:tui":"node --test test/tui-pty-e2e.test.mjs","review:tui":"node scripts/review-tui.mjs","dogfood:tui":"node scripts/dogfood-tui.mjs","build:client":"node deploy/client/build.mjs","verify:phone":"node scripts/verify-phone.mjs"},"_npmUser":{"name":"bogart91","email":"alexander.s.malinin@gmail.com"},"repository":{"url":"git+https://github.com/AleksandrMalinin/0x2f.git","type":"git"},"_npmVersion":"11.9.0","description":"0x2F — manage engineering work, not AI sessions. A task-native wrapper around coding agents with a local-first Web UI.","directories":{},"_nodeVersion":"24.14.0","dependencies":{"ws":"^8.21.3","@noble/hashes":"^1.8.0","@noble/ciphers":"^1.3.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/0x2f_0.1.0_1787873564735_0.3060958269762897","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"0x2f","version":"0.1.1","keywords":["cli","tasks","coding-agents","local-first","claude-code","deepseek-harness","acp","gemini-cli"],"license":"MIT","_id":"0x2f@0.1.1","maintainers":[{"name":"bogart91","email":"alexander.s.malinin@gmail.com"}],"homepage":"https://github.com/AleksandrMalinin/0x2f#readme","bugs":{"url":"https://github.com/AleksandrMalinin/0x2f/issues"},"bin":{"2f":"src/cli.mjs"},"dist":{"shasum":"015039758ed1179a14a43bcecd32d9ea0fc0f1a2","tarball":"https://registry.npmjs.org/0x2f/-/0x2f-0.1.1.tgz","fileCount":67,"integrity":"sha512-f5LrcQTF0rspiUbmvhDXcn8vy+fHcQzdVLIItOONfEXhDQeuLTXEZ1TGG9gT/qJQVe3/dO+cBT+cuBZlk1oEAQ==","signatures":[{"sig":"MEYCIQCQzD5uYsUz+XzGiUMGw6xI6rUhun/9NpslJ9ANJig3awIhAPmJtbeXGcKewYf4XtrJEURJ0S4/XP4WuFzMhQDageVP","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":848704},"type":"module","engines":{"node":">=20"},"gitHead":"b8cd998e15360b847008953a78000b00dfa05cfa","scripts":{"test":"node --test \"test/*.test.mjs\"","check":"find src test relay scripts deploy -name node_modules -prune -o \\( -name '*.mjs' -o -name '*.js' \\) -exec node --check {} \\; && python3 -c \"import ast,glob,sys; [ast.parse(open(f, encoding='utf-8').read(), f) for f in glob.glob('test/tui-pty/*.py')]\"","start":"node src/cli.mjs","test:tui":"node --test test/tui-pty-e2e.test.mjs","review:tui":"node scripts/review-tui.mjs","dogfood:tui":"node scripts/dogfood-tui.mjs","build:client":"node deploy/client/build.mjs","verify:phone":"node scripts/verify-phone.mjs"},"_npmUser":{"name":"bogart91","email":"alexander.s.malinin@gmail.com"},"repository":{"url":"git+https://github.com/AleksandrMalinin/0x2f.git","type":"git"},"_npmVersion":"11.9.0","description":"0x2F — manage engineering work, not AI sessions. A task-native wrapper around coding agents with a local-first Web UI.","directories":{},"_nodeVersion":"24.14.0","dependencies":{"ws":"^8.21.3","@noble/hashes":"^1.8.0","@noble/ciphers":"^1.3.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/0x2f_0.1.1_1787919126714_0.6339405596264935","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"0x2f","version":"0.1.2","keywords":["cli","tasks","coding-agents","local-first","claude-code","deepseek-harness","acp","gemini-cli"],"license":"MIT","_id":"0x2f@0.1.2","maintainers":[{"name":"bogart91","email":"alexander.s.malinin@gmail.com"}],"homepage":"https://github.com/AleksandrMalinin/0x2f#readme","bugs":{"url":"https://github.com/AleksandrMalinin/0x2f/issues"},"bin":{"2f":"src/cli.mjs"},"dist":{"shasum":"c1b8b921f38d9ebcc3e53e1667d8dfd19a73d80c","tarball":"https://registry.npmjs.org/0x2f/-/0x2f-0.1.2.tgz","fileCount":71,"integrity":"sha512-EzX4T6p88oaGHHZ6k5W/5d3W1vzmAG8ndgNJPulbZgbGrK8XQAA+HGz8ZeFthpIdSwiF6/pviE+b8DemCXG1vA==","signatures":[{"sig":"MEUCIA0ovh56+tmERamP40Z8AFhkeUyFyUYmasrPkYEMsYSpAiEAgxdTyJ25D+fJviIfS11zXUw+GXK1aD7A7pvyhL30pjU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":885526},"type":"module","engines":{"node":">=20"},"gitHead":"82290acb9f43a0178949f6183887851f9619f8f7","scripts":{"test":"node --test \"test/*.test.mjs\"","check":"find src test relay scripts deploy -name node_modules -prune -o \\( -name '*.mjs' -o -name '*.js' \\) -exec node --check {} \\; && python3 -c \"import ast,glob,sys; [ast.parse(open(f, encoding='utf-8').read(), f) for f in glob.glob('test/tui-pty/*.py')]\"","start":"node src/cli.mjs","test:tui":"node --test test/tui-pty-e2e.test.mjs","review:tui":"node scripts/review-tui.mjs","dogfood:tui":"node scripts/dogfood-tui.mjs","build:client":"node deploy/client/build.mjs","verify:phone":"node scripts/verify-phone.mjs"},"_npmUser":{"name":"bogart91","email":"alexander.s.malinin@gmail.com"},"repository":{"url":"git+https://github.com/AleksandrMalinin/0x2f.git","type":"git"},"_npmVersion":"11.9.0","description":"0x2F — manage engineering work, not AI sessions. A task-native wrapper around coding agents with a local-first Web UI.","directories":{},"_nodeVersion":"24.14.0","dependencies":{"ws":"^8.21.3","@noble/hashes":"^1.8.0","@noble/ciphers":"^1.3.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/0x2f_0.1.2_1788011380684_0.21560925036227196","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"0x2f","version":"0.1.3","description":"A local work router for coding agents. Tasks persist across runs, so a task can be continued through a different harness without losing prior results or decisions.","type":"module","license":"MIT","keywords":["cli","tasks","coding-agents","local-first","claude-code","deepseek-harness","acp","gemini-cli"],"bin":{"2f":"src/cli.mjs"},"engines":{"node":">=20"},"repository":{"type":"git","url":"git+https://github.com/probnaya-work/0x2f.git"},"bugs":{"url":"https://github.com/probnaya-work/0x2f/issues"},"homepage":"https://0x2f.space/","scripts":{"start":"node src/cli.mjs","check":"find src test relay scripts deploy -name node_modules -prune -o \\( -name '*.mjs' -o -name '*.js' \\) -exec node --check {} \\; && python3 -c \"import ast,glob,sys; [ast.parse(open(f, encoding='utf-8').read(), f) for f in glob.glob('test/tui-pty/*.py')]\"","test":"node --test \"test/*.test.mjs\"","test:tui":"node --test test/tui-pty-e2e.test.mjs","dogfood:tui":"node scripts/dogfood-tui.mjs","review:tui":"node scripts/review-tui.mjs","build:client":"node deploy/client/build.mjs","verify:phone":"node scripts/verify-phone.mjs"},"dependencies":{"@noble/ciphers":"^1.3.0","@noble/hashes":"^1.8.0","ws":"^8.21.3"},"_id":"0x2f@0.1.3","_integrity":"sha512-imiUB0tOmVmGI1KcvtCf8dEaodipVGHwh4CLkKYoUwcaOQyIF5lJbt8eTKb4WHBtznl65Z8GcigmZsBLvllbWg==","_resolved":"/Users/bogart/Dev/work-cli-v0/0x2f-0.1.3.tgz","_from":"file:0x2f-0.1.3.tgz","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-imiUB0tOmVmGI1KcvtCf8dEaodipVGHwh4CLkKYoUwcaOQyIF5lJbt8eTKb4WHBtznl65Z8GcigmZsBLvllbWg==","shasum":"20f2e042206211cb7d976157ad3e068c319fa39c","tarball":"https://registry.npmjs.org/0x2f/-/0x2f-0.1.3.tgz","fileCount":71,"unpackedSize":885661,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDYwDwRvT1Qq/kksAgLHo9BVdMfpW4Uf1PT0qz3nTHAFgIge82hk5rm2u/vCP048P3go1DLVtZoBZCaFJuVAuKgs+M="}]},"_npmUser":{"name":"bogart91","email":"alexander.s.malinin@gmail.com"},"directories":{},"maintainers":[{"name":"bogart91","email":"alexander.s.malinin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/0x2f_0.1.3_1788287959734_0.8173799190774105"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-27T23:32:44.693Z","modified":"2026-09-01T18:39:20.065Z","0.0.1":"2016-01-13T11:01:25.730Z","0.1.0":"2026-08-27T23:32:44.922Z","0.1.1":"2026-08-28T12:12:06.863Z","0.1.2":"2026-08-29T13:49:40.861Z","0.1.3":"2026-09-01T18:39:19.904Z"},"bugs":{"url":"https://github.com/probnaya-work/0x2f/issues"},"license":"MIT","homepage":"https://0x2f.space/","keywords":["cli","tasks","coding-agents","local-first","claude-code","deepseek-harness","acp","gemini-cli"],"repository":{"type":"git","url":"git+https://github.com/probnaya-work/0x2f.git"},"description":"A local work router for coding agents. Tasks persist across runs, so a task can be continued through a different harness without losing prior results or decisions.","maintainers":[{"name":"bogart91","email":"alexander.s.malinin@gmail.com"}],"readme":"PROBNAYA / 0x2F\nINSTRUMENT\n\nIndependent Computational Laboratory\n\n# 0x2F\n\n0x2F is a local work router for coding agents. Give it engineering work —\n\"Inspect why retries restart the whole run\" — and it routes the task to a\ncoding harness on your machine, runs it in the background, and tracks it\nthrough the lifecycle until you close it. **You work in terms of tasks, not\nagent sessions.**\n\n`0x2F` is hex for the ASCII `/` (47). The CLI is `2f`.\n\nNode.js ≥ 20, one dependency (`ws`), no build step, local-first.\n\n```text\n                          0x2F\n\n   Desktop                          Mobile\n   2f CLI · Web UI             phone browser\n        \\                     (remote control,\n         \\   one task,          via your relay —\n          \\  many runs           outbound WS)\n           \\                      /\n            └──────────┬─────────┘\n                       │\n        ┌──────────────┴──────────────┐\n        │         Task runtime        │\n        │   state · runs · events     │\n        │   permissions · decisions   │\n        └──────────────┬──────────────┘\n                       │  one worker per run\n        ┌──────────────┼──────────────┐\n        ▼              ▼              ▼              ▼\n   claude-code      codex          deepseek-harness   gemini\n                        │ any ACP / command agent\n        └──────────────┼──────────────┘\n                       ▼\n              your local project\n```\n\nThe task is the persistent unit of work. Each run is one disposable agent\nsession through one harness. 0x2F keeps the task coherent across runs, exposes\nwhat the agent is doing, surfaces decisions when human attention is required,\nand lets you intervene — without treating every agent session as a separate\npiece of work.\n\n## Why it exists\n\nA coding harness gives you an agent session: you start one, watch it, and\neither accept its output or start another session that has forgotten\neverything. 0x2F inverts that. The **task** is the object — a description of\nengineering work that persists in the repository. Agent sessions are\ndisposable execution underneath it. A task can be run again through a\ndifferent harness, corrected with notes, and continued with prior results in\ncontext — because the task, not the session, is what 0x2F keeps.\n\n## Install\n\nRequires **Node.js ≥ 20** and at least one coding harness on your PATH:\n[Claude Code](https://code.claude.com/docs) (`claude`) is the built-in\ndefault, [Codex](https://github.com/openai/codex) (`codex`),\n[DeepSeek Harness](https://github.com/deepseek-ai/DeepSeek-Harness) (`dsh`)\nand [Gemini CLI](https://github.com/google-gemini/gemini-cli) (`gemini`)\nare also built in, and any ACP-compatible agent or headless executable\ncan be added per project (see [Providers](#providers)). 0x2F itself is one\nsmall package — `ws` is its only dependency, no build step, no accounts, no\ndaemon.\n\n```bash\nnpm install -g 0x2f        # the `2f` command lands on your PATH\n```\n\n`npx --yes 0x2f ...` works for a one-off, but install globally for regular\nuse — `2f` is a local app you invoke repeatedly, not a one-shot script.\n\n## Quick start\n\nInside the repository you want to work on:\n\n```bash\n2f init                          # create .work/ with project.md and rules.md\n2f new \"Investigate why retries restart the whole run\"\n2f                               # list tasks\n2f open 1                        # run detail, history, result\n2f tui                           # open the terminal client (full screen)\n2f ui                            # open the Web UI (starts the local runtime)\n```\n\nTo run a task with the built-in Codex provider, install the\n[Codex CLI](https://github.com/openai/codex), sign in, and select it explicitly:\n\n```bash\ncodex login\n2f providers                     # confirm: codex  native  yes\n2f new \"Audit the auth flow\" --provider codex\n```\n\nCodex runs headlessly with workspace-write sandboxing by default and supports\nsame-thread resume. Its structured-event and permission boundaries are\ndocumented in [`docs/codex-capability-map.md`](docs/codex-capability-map.md).\n\nTo run a task with the built-in Gemini CLI provider, install the\n[Gemini CLI](https://github.com/google-gemini/gemini-cli), authenticate once\n(run `gemini` interactively, or set `GEMINI_API_KEY` / Vertex credentials),\nand select it explicitly:\n\n```bash\ngemini          # first run: sign in interactively\n2f providers    # confirm: gemini  native  yes\n2f new \"Audit the auth flow\" --provider gemini\n```\n\nGemini runs headlessly (`-p --skip-trust -o stream-json`), auto-approves\nfile edits (`--approval-mode auto_edit`; override with `GEMINI_APPROVAL_MODE`),\nand supports same-session resume by UUID. Its structured-event, permission\nand resume boundaries are documented in\n[`docs/gemini-capability-map.md`](docs/gemini-capability-map.md).\n\n`2f init` creates `.work/` with `project.md`, `rules.md`, `knowledge.md`,\n`decisions.md` and `providers/` — edit `project.md` and `rules.md` once;\nevery task prompt is built from them. It also tells you which provider will\nrun, or what to install if none is available.\n\nIf `2f new` refuses with \"Execution provider ... is unavailable\", no harness\nis on PATH — install one, or configure a provider (see\n[Providers](#providers)).\n\n## How a task works\n\n```text\n                    ┌────────────────────────────────────┐\n                    ▼                                    │\n WORKING ───────────────► READY / FAILED ────────────────┘\n    │                                (2f close → DONE)\n    │  the agent needs a human\n    ▼\n NEEDS YOU\n   ├─ PERMISSION  → 2f allow | 2f reject\n   │                the same run/session continues\n   └─ DECISION    → 2f answer (recorded with the task)\n                    rerun the task to continue with it in context\n```\n\nA task that hits something the agent cannot decide alone becomes **NEEDS YOU**.\n`2f open <id>` (or the Web UI) shows what it is asking for. There are two\ndistinct interactions:\n\n- **PERMISSION** — a concrete operation needing authorization (an edit, a\n  command). `2f allow <id>` / `2f reject <id>` answers it and the same run\n  continues.\n- **DECISION** — the agent cannot continue without your judgment.\n  `2f answer <id> \"<your answer>\"` records your answer with the task. A\n  decision is never allow/rejected.\n\nThe decision request is a machine-read protocol, not prose. Agents signal one\nby ending with:\n\n```text\n## Needs human decision\nREQUIRED: yes\nQUESTION: <the concrete question a human must answer>\n```\n\nAnything else — a bare heading, \"None\", \"No decision required\", or any prose —\nis treated as **no** decision, so a finished run completes READY instead of\ninterrupting you for work that did not need you.\n\n`2f close <id>` (or **CLOSE** in the Web UI) removes any task from active\nattention — a wrong NEEDS YOU, a non-resumable one, a FAILED or READY run you\nno longer want. It never resumes the provider and never starts a new\nexecution.\n\n## One task, many runs\n\n\n<img width=\"920\" height=\"506\" alt=\"Screenshot 2026-08-29 at 14 00 41\" src=\"https://github.com/user-attachments/assets/10a1dc37-7439-4d75-a024-d68e35a62275\" />\n\nEvery execution is recorded as a **run** under the task, so the same task can\nbe run again — through a different harness, for comparison:\n\n```text\nTask  \"Investigate why retries restart the whole run\"\n  ├── run 01 · claude-code         a fresh session\n  ├── run 02 · deepseek-harness    a fresh session\n  └── run 03 · claude-code         a fresh session, with your notes\n                                   and prior results in context\n```\n\n```bash\n2f rerun 1 --provider deepseek-harness   # run 02 under task #1\n2f open 1 --run 2                        # one run's factual detail\n```\n\nA new run is a **continuation of the task, not a blank attempt**: its input\n(`runs/<n>/prompt.md`) is rebuilt from current task state — the original\nrequest plus your constraints/answers and prior runs' results, verification\nand changed files — and handed to a fresh provider session. The task is\npersistent; provider sessions are disposable. Add a constraint with\n`2f note <id> \"<constraint>\"` (or `2f answer` on a decision block); it becomes\npart of the next run's context, with no manual copying. The original\n`prompt.md` is never overwritten.\n\n## The terminal client\n\n`2f tui` is the full-screen surface: the whole ledger on the left, the\nselected task in full on the right, and — pinned to the bottom of the detail\npane — the one action that task is waiting for.\n\n<img width=\"920\" alt=\"Screenshot 2026-08-29 at 14 26 01\" src=\"https://github.com/user-attachments/assets/3e87a04e-a233-49e8-8516-fcd8e66e194c\" />\n\n```bash\n2f tui                     # or: 2f tui --light\n```\n\nIt is a client, not a second runtime. It builds the same runtime every other\ncommand builds, calls the same shared actions, and tails the same event logs\nthe Web server tails — so a run finishing in the background, a `2f allow`\ntyped in another terminal and a tap on your phone all land in it live, and\nanything you do in it lands everywhere else.\n\nThe keymap is the surface's own, not a file manager's:\n\n| key | what it does |\n| --- | --- |\n| `j` `k` | move between tasks (`g` / `G` jump to first / last) |\n| `J` `K` | scroll the task detail |\n| `tab` | filter — all · needs you · failed · ready · working |\n| `/` | search by title, brief or number |\n| `↵` | the one action this task is waiting for (shown bottom-left) |\n| `x` | the alternative — reject · save only · send back · drop |\n| `d` | changes — the real diff of the working tree, or the planned write |\n| `c` | note or correct — kept on the task, carried into every later run |\n| `p` | point the next run at another provider |\n| `t` | expand the trace of the current run |\n| `n` | new task (`⇧↵` inserts a newline, `⌥↵` expands your note into a brief, `↵` starts it) |\n| `?` | the key list · `q` detaches — runs keep executing without you |\n\n`↵` is deliberately not \"open\": on a permission it ALLOWS, on a decision it is\nANSWER & CONTINUE, on a READY task it ACCEPTS, on a FAILED one it RETRIES. The\nalternative under `x` is the other half of the same pair. Both go through the\nshared actions, so the TUI can never allow something the CLI would refuse.\n\n## Desktop + mobile\n\nThe CLI, the TUI and the Web are three surfaces over the same core:\n\n```bash\n2f ui                      # or: 2f ui <port>, 2f ui --no-browser\n```\n\n`2f ui` behaves like opening a local application: if a 0x2F runtime is already\nhealthy on `http://127.0.0.1:4242` (localhost only) it reuses it; otherwise it\nstarts the runtime in the background, waits until it is healthy, and opens the\nUI. Runtime output lands in `.work/ui.log`. The browser calls the same shared\nactions over a local HTTP/SSE API and subscribes to the same normalized\nevents.\n\nControl 0x2F from your phone on the **same Wi-Fi** — the Mac keeps running\nthe work; the phone is a compact control surface:\n\n<img width=\"310\" height=\"552\" alt=\"Screenshot 2026-08-29 at 14 01 06\" src=\"https://github.com/user-attachments/assets/fb6311de-c662-4a4d-9097-d4cee465f826\" />\n\n\n```bash\n2f pair\n```\n\nNo flags, no IPs to find, no relay to configure. `2f pair` detects the Mac's\nprivate LAN address, enables the pairing surface on that interface (and only\nwhile pairing is active), and prints a phone-openable URL **plus a one-time\npairing code**:\n\n```\n0x2F PAIR\n\nsame Wi-Fi required\n\n  http://192.168.1.163:4242/pair?relay=…&token=…&device=…\n\ncode  ZEPQQ-N4WH8-NG4D\n```\n\nOpen the URL on your phone and type the code into the trusted page (served by\nthe Mac itself). The phone then speaks the same Web UI against the Mac: see\nNEEDS YOU / WORKING / READY / FAILED, open a task, and ANSWER / ALLOW /\nREJECT / NOTE / SEND BACK / ACCEPT. Every command, ack, event and snapshot is\nend-to-end encrypted (AES-256-GCM keyed by the pairing code) — the same\nencrypted channel the hosted relay uses, so a passive observer on the Wi-Fi\nsees only ciphertext. While the Mac is offline the phone shows its own\nlast-known state with a **MAC OFFLINE** banner and disables actions; commands\nare never queued, and a retried command reuses its `requestId` so it can\nnever execute twice.\n\nPairing tokens are one-time and expire in 10 minutes; phone sessions live 30\ndays and are revoked by `2f pair --off` (a real revocation — the LAN surface\ncloses within a second, and normal `2f` / `2f ui` stay loopback-only the whole\ntime) or by re-pairing, which also rotates the Mac's credential and the E2E\nkey. The LAN surface serves only the pairing client + relay protocol on\nprivate-LAN addresses (RFC 1918) — the normal local API is never reachable\nfrom other devices on the network.\n\nFor **remote control away from the LAN** (future use, or your own\ndeployment), the hosted relay path is unchanged: `2f pair --relay https://…`\n/ `--client https://…`, or the `0X2F_RELAY_URL` / `0X2F_CLIENT_ORIGIN` env\nvars (see [`deploy/README.md`](deploy/README.md)). The hosted relay is\n**private infrastructure, not part of the local product**: a small standalone\napp that forwards encrypted envelopes, holds no task state, and is never\nshipped in the npm package. Deployment details live in\n[`relay/README.md`](https://github.com/probnaya-work/0x2f/blob/main/relay/README.md);\na full setup-and-test walkthrough is in\n[`docs/remote-control.md`](https://github.com/probnaya-work/0x2f/blob/main/docs/remote-control.md).\n\n## Where your work lives\n\nEverything 0x2F knows about a project lives in `.work/` inside that\nrepository — nothing is stored globally, and nothing leaves your machine\nunless you opt into remote pairing:\n\n| What | Where |\n| --- | --- |\n| task state + run history | `.work/tasks/<slug>/` |\n| project context every prompt is built from | `project.md` · `rules.md` · `knowledge.md` · `decisions.md` |\n| routing policy (optional — you author it; `2f init` does not create it) | `.work/routing.json` |\n| extra providers | `.work/providers/*.json` |\n| UI runtime log | `.work/ui.log` |\n| pairing credentials (only if you pair) | `.work/relay.json` |\n\nDelete `.work/` to remove 0x2F's state from a project — your source files\nare never touched.\n\n## Updating and uninstalling\n\n```bash\nnpm update -g 0x2f              # or: npm install -g 0x2f@latest\nnpm uninstall -g 0x2f           # removes the CLI; project .work/ stays\n```\n\nProject `.work/` state survives uninstalls — it belongs to the project, not\nto the install.\n\n## Providers\n\n0x2F has three provider integration paths behind one contract:\n\n| Integration | What it is | Use |\n| --- | --- | --- |\n| **Native** | Deep adapter for one harness's specific capabilities | `claude-code` (permissions → `needs_you` → same-session resume), `codex` (structured exec events + thread resume), `deepseek-harness`, `gemini` (structured stream events + UUID session resume) |\n| **ACP** | One generic provider speaking the [Agent Client Protocol](https://agentclientprotocol.com) v1 over stdio | Any ACP-compatible agent — Cursor, OpenCode — configured by manifest |\n| **Command** | One generic provider for headless executables | Any CLI that takes a prompt and prints a result — configured by manifest |\n\n`claude-code`, `codex`, `deepseek-harness` and `gemini` are built in.\nEverything else is added **declaratively**: drop one JSON manifest into\n`.work/providers/` and it becomes a provider — no source changes:\n\n```json\n{\n  \"id\": \"cursor\",\n  \"displayName\": \"Cursor\",\n  \"transport\": \"acp\",\n  \"command\": [\"agent\", \"acp\"]\n}\n```\n\nACP manifests may set `\"permissions\"`: `\"interactive\"` (default — a permission\nrequest pauses the run and asks you), `\"deny\"`, or `\"approve\"` (headless\nauto-resolution). Command manifests must pass the task through the `{prompt}`\nplaceholder. Commands are spawned as argv arrays, never through a shell.\nVerified example manifests live in [`examples/providers/`](https://github.com/probnaya-work/0x2f/blob/main/examples/providers/README.md);\n`2f providers` lists every provider with its integration type and availability.\n\nBy default `2f new` uses the configured routing default. When it is `auto`,\n0x2F picks the harness deterministically: available providers first, then the\nids listed in `.work/routing.json` in order, then registry order. It does not\nread the task text and never claims a provider is \"best\" — the decision is\npersisted with the run and shown by `2f open` / the Web UI. Override any time:\n\n```bash\n2f new \"Audit the auth flow\" --provider auto        # deterministic routing\n2f new \"Audit the auth flow\" --provider claude-code # explicit\n```\n\n```json\n// .work/routing.json\n{\n  \"default\": \"auto\",                       // \"auto\" or a provider id\n  \"prefer\": [\"claude-code\", \"deepseek-harness\"]\n}\n```\n\n`routing.json` is optional and hand-authored. `2f init` does not create it,\nand `2f new` runs fine without it — the routing default (or an explicit\n`--provider`) applies until you write the file yourself. No command edits it;\nedit the JSON directly if you want non-default routing policy.\n\n## Architecture\n\n```text\nCLI · TUI · Web · phone\n    │   shared actions + normalized events\n    ▼\nWork Core          lifecycle · actions · runs · events · store\n    │\n    ▼\nRouter             AUTO: availability + routing config (deterministic)\n    │\n    ▼\nExecution node     local machine (spawns the detached worker)\n    │\n    ▼\nProvider           native · ACP · command\n    │\n    ▼\nCoding harness     claude-code · codex · dsh · gemini · cursor · any command\n```\n\nNo surface implements lifecycle or provider logic — the CLI, the terminal\nclient (`src/tui/`) and the browser all call the same shared actions (`src/core/actions.mjs`) and read the same\nnormalized events (`src/core/events.mjs`). Everything a task needs persists\nunder `.work/` in the repository you run 0x2F in: task state and run history\n(`.work/tasks/<slug>/`), the project context every prompt is built from\n(`project.md`, `rules.md`, `knowledge.md`, `decisions.md`), routing\n(`routing.json`), and configured providers (`providers/*.json`).\n\nThree distinctions that help read the code:\n\n- **Task ≠ provider session.** A task persists; a session is metadata under\n  one of its runs.\n- **Provider ≠ execution node.** A harness runs on a machine; the node owns\n  where execution happens (only `local` today).\n- **Provider ≠ model.** A harness runs many models; 0x2F routes to harnesses.\n\n```text\nYOUR MACHINE                          PRIVATE INFRASTRUCTURE (yours)\n─────────────                         ─────────────────────────────\nlocal project       ◄── outbound ──►  relay (optional, for mobile)\nagent processes          WebSocket    · pairing + forwarding only\ntask runtime                           · no task state, no credentials\ndesktop UI\n```\n\nExecution is local-only (the API binds to `127.0.0.1`, is token-authenticated\nand refuses cross-site browser requests); remote control is an outbound\ncontrol layer, not remote execution.\n\n## Current limitations\n\n- **Execution is local-only.** There is no remote/mini-PC node yet. Remote\n  control is an outbound control layer, not remote execution.\n- **Remote control is v1.** No push notifications (the phone works while the\n  app is open), no offline command queue by design; one phone at a time per\n  Mac (re-pairing revokes the previous phone's session).\n- **AUTO is deterministic policy routing**, not semantic selection — and there\n  is no automatic failover (a routed run that fails is `failed`, not secretly\n  retried elsewhere).\n- **Runs of one task are strictly sequential.** No concurrent or\n  multi-agent orchestration of a single task.\n- **No evaluation.** Run history is for inspection — no scores, no winners,\n  no recommendations.\n\n## Development\n\nNo dependencies, no build step — the repo runs as-is:\n\n```bash\nnpm test          # the test suite (node --test, 300+ tests)\nnpm run check     # syntax-check every source and test file\nnpm start         # run the CLI: node src/cli.mjs\n```\n\nSee [`docs/development.md`](https://github.com/probnaya-work/0x2f/blob/main/docs/development.md)\nfor the repository layout and how the pieces fit;\n[`docs/architecture.md`](https://github.com/probnaya-work/0x2f/blob/main/docs/architecture.md)\ntraces a task through the whole system.\n\n## License\n\n[MIT](LICENSE)\n","readmeFilename":"README.md"}