{"_id":"2heads","_rev":"4-505ef00e6af4096f683e7d3bfd440c25","name":"2heads","dist-tags":{"latest":"0.1.3"},"versions":{"0.1.0":{"name":"2heads","version":"0.1.0","keywords":["cli","tmux","claude","codex","ai","repl","decision-making"],"license":"MIT","_id":"2heads@0.1.0","maintainers":[{"name":"jsanzola","email":"jsanzolac@gmail.com"}],"homepage":"https://github.com/jsanzolac/2heads#readme","bugs":{"url":"https://github.com/jsanzolac/2heads/issues"},"bin":{"2heads":"dist/cli.js"},"dist":{"shasum":"7b6990be775506c61aec864fbb60326d49961df6","tarball":"https://registry.npmjs.org/2heads/-/2heads-0.1.0.tgz","fileCount":51,"integrity":"sha512-saGjV1I8b8A7EImYk25KmYpXYgK5fnuiTNKCpdzxxSWsXXJA3po+tBmvZPiPHAzNqYkARwpHFPsbqHQmpy4YJg==","signatures":[{"sig":"MEUCIC0MW1VE4rpWKuP8m5sR7TUqdyf7qPVUuQ8a0K1x58fxAiEAhf/laz84ZLrAh6J3D1S9YNiqHMytANgsBJt1iDfwhQ8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":157705},"type":"module","engines":{"node":">=22"},"gitHead":"3c8e26f6d25317fef6a8e2accc9b35bc85cbabec","scripts":{"dev":"npm run build && node dist/cli.js","test":"vitest run","build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"jsanzola","email":"jsanzolac@gmail.com"},"repository":{"url":"git+https://github.com/jsanzolac/2heads.git","type":"git"},"_npmVersion":"11.8.0","description":"A tmux-backed terminal REPL for making Claude and Codex challenge each other before you decide.","directories":{},"_nodeVersion":"24.13.1","dependencies":{"execa":"^9.6.0","commander":"^14.0.2","picocolors":"^1.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.0.8","typescript":"^5.9.3","@types/node":"^24.0.0"},"_npmOperationalInternal":{"tmp":"tmp/2heads_0.1.0_1777147720611_0.9479091368514088","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"2heads","version":"0.1.1","keywords":["cli","tmux","claude","codex","ai","repl","decision-making"],"license":"MIT","_id":"2heads@0.1.1","maintainers":[{"name":"jsanzola","email":"jsanzolac@gmail.com"}],"homepage":"https://github.com/jsanzolac/2heads#readme","bugs":{"url":"https://github.com/jsanzolac/2heads/issues"},"bin":{"2heads":"dist/cli.js"},"dist":{"shasum":"01bc23788a1381f1d028f9cef61baa0143b30bd1","tarball":"https://registry.npmjs.org/2heads/-/2heads-0.1.1.tgz","fileCount":54,"integrity":"sha512-QcKI/etC4ud+TVESndOOHMeeT+mm6pkh3NDV+HOcPGPnHOe9J+QQNFk66olNlCSN8KtzVsAVxtFTCyND5uHbCw==","signatures":[{"sig":"MEQCIGfsDj2N8dAUhbE+W5+GSBKuAAaQN5+wR8bZt1mPEjI3AiBCiWcI7VuaYvZ1RrDEMacH1yuP3k9fvIvuwNPBtge/lg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":169988},"type":"module","engines":{"node":">=22"},"gitHead":"fcf0cacb1da7df1e2fc0cec5c8465d1a008dc906","scripts":{"dev":"npm run build && node dist/cli.js","test":"vitest run","build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"jsanzola","email":"jsanzolac@gmail.com"},"repository":{"url":"git+https://github.com/jsanzolac/2heads.git","type":"git"},"_npmVersion":"11.8.0","description":"A tmux-backed terminal REPL for making Claude and Codex challenge each other before you decide.","directories":{},"_nodeVersion":"24.13.1","dependencies":{"execa":"^9.6.0","commander":"^14.0.2","picocolors":"^1.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.0.8","typescript":"^5.9.3","@types/node":"^24.0.0"},"_npmOperationalInternal":{"tmp":"tmp/2heads_0.1.1_1777159322115_0.09979403504752371","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"2heads","version":"0.1.2","keywords":["cli","tmux","claude","codex","ai","repl","decision-making"],"license":"MIT","_id":"2heads@0.1.2","maintainers":[{"name":"jsanzola","email":"jsanzolac@gmail.com"}],"homepage":"https://github.com/jsanzolac/2heads#readme","bugs":{"url":"https://github.com/jsanzolac/2heads/issues"},"bin":{"2heads":"dist/cli.js"},"dist":{"shasum":"c90721ebb8996fe3e0f863e9139b4914c4d8ab1d","tarball":"https://registry.npmjs.org/2heads/-/2heads-0.1.2.tgz","fileCount":54,"integrity":"sha512-Q88ULAkxUphgomQ6BWUE/a7E5ZMvJUvOMf54f/J2gCUlfHdjMj6hin0JPZOWas82XVudE1zuA4JaLzIHTX7O3g==","signatures":[{"sig":"MEMCIDHx5LEjhQLX5xDFxYLQKsSpK1Y2lXZv2WDkFGhtF+IkAh9/VtDI0FugwnJEeFcnJFNHTQ0bO7WwqGIa299ZB9nI","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":189179},"type":"module","engines":{"node":">=22"},"gitHead":"5e8434ed40b3fffa747a8421729d7c53d5e0b354","scripts":{"dev":"npm run build && node dist/cli.js","test":"vitest run","build":"tsc -p tsconfig.json","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"jsanzola","email":"jsanzolac@gmail.com"},"repository":{"url":"git+https://github.com/jsanzolac/2heads.git","type":"git"},"_npmVersion":"11.8.0","description":"A tmux-backed terminal REPL for making Claude and Codex challenge each other before you decide.","directories":{},"_nodeVersion":"24.13.1","dependencies":{"execa":"^9.6.0","commander":"^14.0.2","picocolors":"^1.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.0.8","typescript":"^5.9.3","@types/node":"^24.0.0"},"_npmOperationalInternal":{"tmp":"tmp/2heads_0.1.2_1777160369094_0.8800423613005797","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"2heads","version":"0.1.3","description":"A tmux-backed terminal REPL for making Claude and Codex challenge each other before you decide.","type":"module","license":"MIT","bin":{"2heads":"dist/cli.js"},"keywords":["cli","tmux","claude","codex","ai","repl","decision-making"],"repository":{"type":"git","url":"git+https://github.com/jsanzolac/2heads.git"},"bugs":{"url":"https://github.com/jsanzolac/2heads/issues"},"homepage":"https://github.com/jsanzolac/2heads#readme","publishConfig":{"access":"public"},"scripts":{"build":"tsc -p tsconfig.json","dev":"npm run build && node dist/cli.js","prepublishOnly":"npm run typecheck && npm test && npm run build","test":"vitest run","typecheck":"tsc -p tsconfig.json --noEmit"},"engines":{"node":">=22"},"dependencies":{"commander":"^14.0.2","execa":"^9.6.0","picocolors":"^1.1.1"},"devDependencies":{"@types/node":"^24.0.0","typescript":"^5.9.3","vitest":"^4.0.8"},"gitHead":"d65cb597def4bb10a7bdce08ab7e5d30cd921716","_id":"2heads@0.1.3","_nodeVersion":"24.13.1","_npmVersion":"11.8.0","dist":{"integrity":"sha512-sf3+vNfeCNSrnFRP3Ohr6S3OF23zhd3hoBh4107qGaJ0LEzw8GEtrKC7+VvH79sNSw4zQ9RD4o2Rh54R5fek8g==","shasum":"7c787bf4f1d67037b65de497142835092a4b78dd","tarball":"https://registry.npmjs.org/2heads/-/2heads-0.1.3.tgz","fileCount":54,"unpackedSize":189175,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCNVu18MijbjF/tyoBIym9fXP9WH0kcoEaVQk3RphXgCwIhAIc0llTgB2cE0T/xqyhriBnoGTSrmiUs7QMB+X+apiqh"}]},"_npmUser":{"name":"jsanzola","email":"jsanzolac@gmail.com"},"directories":{},"maintainers":[{"name":"jsanzola","email":"jsanzolac@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/2heads_0.1.3_1777165962586_0.8335179877642287"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-25T20:08:40.461Z","modified":"2026-04-26T01:12:42.859Z","0.1.0":"2026-04-25T20:08:40.745Z","0.1.1":"2026-04-25T23:22:02.298Z","0.1.2":"2026-04-25T23:39:29.233Z","0.1.3":"2026-04-26T01:12:42.734Z"},"bugs":{"url":"https://github.com/jsanzolac/2heads/issues"},"license":"MIT","homepage":"https://github.com/jsanzolac/2heads#readme","keywords":["cli","tmux","claude","codex","ai","repl","decision-making"],"repository":{"type":"git","url":"git+https://github.com/jsanzolac/2heads.git"},"description":"A tmux-backed terminal REPL for making Claude and Codex challenge each other before you decide.","maintainers":[{"name":"jsanzola","email":"jsanzolac@gmail.com"}],"readme":"# 2heads\n\n2HEADS helps you think through complex decisions by forcing two models to reason in public, challenge each other's assumptions, and expose trade-offs before you commit.\n\n`2heads` is a tmux-backed terminal REPL that passes a prompt between Claude and Codex for a fixed number of rounds, then asks a separate recap worker to summarize the exchange.\n\n## Usage\n\n```sh\nnpm install\nnpm run build\nnode dist/cli.js --rounds 1\n```\n\nThe CLI requires `tmux`, `claude`, and `codex` on `PATH`. This repository does not install those tools automatically.\n\nInside the REPL:\n\n- `:quit` exits and cleans up the tmux session.\n- `:rounds <n>` changes the number of two-agent rounds.\n- `:first claude|codex` changes which agent starts.\n- `:attach` attaches to the background tmux session.\n- `:last` prints the last recorded model answer.\n- `:help` shows available commands.\n\nYou can tag files in a prompt with `@path`, for example:\n\n```text\nCompare the CLI flow in @src/cli.ts with the worker protocol in @src/worker.ts\n```\n\nUse quotes for paths with spaces:\n\n```text\nReview @\"docs/decision note.md\"\n```\n\nTagged files are resolved relative to the active workdir. Their contents are sent to Claude and Codex only in the seeded prompts for the current exchange; later model-to-model handoffs still pass only the latest answer.\n\nIn an interactive terminal, typing `@` opens a live file picker in the bottom composer. Continue typing to filter files, use Up/Down to move through matches, and press Tab to insert the selected path. Directory matches end in `/`, so you can keep navigating deeper before tagging a file.\n\n## Architecture\n\n`2heads` has one foreground process and three background workers.\n\nThe foreground CLI owns the terminal UI, command parsing, transcript writing, and turn orchestration. It starts a dedicated tmux session for each run, then opens three panes inside it:\n\n- Claude worker: runs Claude turns.\n- Codex worker: runs Codex turns.\n- Recap worker: runs the final summary in a separate model session.\n\nThe workers communicate with the CLI through JSON files under `.2heads/sessions/<session-id>/`. For each turn, the CLI writes a request file, the worker picks it up, streams events back into event files, and writes a final response file. The CLI records those events and final answers into the transcript.\n\nWithin one REPL run, Claude and Codex keep persistent model sessions. The first handoff seeds a model with the user prompt and any tagged file context; later turns send only the latest model answer, relying on that model's existing session context instead of resending the whole transcript. Each handoff explicitly asks the next model to push back on the previous answer before building on it. Starting the CLI again creates fresh tmux and model sessions.\n\nThe recap worker is intentionally separate from the active Claude/Codex exchange. It receives the full back-and-forth once, after the exchange is done, and produces the final recap without contaminating either active debate session.\n\nOn exit, `2heads` kills the tmux server for the run and removes its socket unless you start with `--keep-tmux`.\n\nAgents are prompted to use Markdown math notation for formulas: `$...$` inline and `$$...$$` for display formulas. The terminal UI highlights those formulas while preserving the raw notation in transcripts.\n\nThe terminal output is rendered as full-width chat bubbles. Claude, Codex, and the recap all align left while keeping distinct background colors. Model answers are buffered per turn so each bubble can be wrapped cleanly. Basic Markdown is formatted inside bubbles, including headings, bold, italic, inline code, bullets, numbered lists, horizontal rules, and simple pipe tables.\n\nIn an interactive terminal, the bottom composer is rendered as a fixed textbox-style panel. It stays active while agents are thinking, so you can type the next message during an exchange; `2heads` queues it and runs it after the current exchange finishes. The composer shows a small spinner with the active agent and turn count so it is clear whether Claude, Codex, or the recap worker is currently running.\n\nTranscripts are saved under `.2heads/sessions/`.\n","readmeFilename":"README.md"}