octomux 1.0.26 → 1.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +137 -139
  2. package/bin/octomux-hook-bridge.js +183 -0
  3. package/bin/octomux-hook-bridge.test.ts +331 -0
  4. package/bin/octomux.js +48 -9
  5. package/cli/dist/client.js +51 -0
  6. package/cli/dist/commands/add-agent.js +16 -6
  7. package/cli/dist/commands/create-task.js +109 -7
  8. package/cli/dist/commands/hooks-install.js +109 -0
  9. package/cli/dist/commands/hooks-list.js +177 -0
  10. package/cli/dist/commands/init.js +144 -0
  11. package/cli/dist/commands/list-integrations.js +46 -0
  12. package/cli/dist/commands/post-review.js +56 -0
  13. package/cli/dist/commands/task-move.js +35 -0
  14. package/cli/dist/commands/task-note.js +20 -0
  15. package/cli/dist/commands/task-ref-add.js +45 -0
  16. package/cli/dist/commands/task-ref-rm.js +16 -0
  17. package/cli/dist/commands/task-summary.js +22 -0
  18. package/cli/dist/commands/task-updates.js +39 -0
  19. package/cli/dist/index.js +22 -0
  20. package/dist/assets/AgentEditor-QUlkrdA5.js +1 -0
  21. package/dist/assets/ChatPage-CALzjRsY.js +2 -0
  22. package/dist/assets/IntegrationsPage-DkG_es-f.js +1 -0
  23. package/dist/assets/ReviewDetailPage-C7fP9lo8.js +1 -0
  24. package/dist/assets/SetupPage-DfjpuFVM.js +1 -0
  25. package/dist/assets/SkillEditor-D3Nw8VdK.js +1 -0
  26. package/dist/assets/WorkspaceDetailPage-CspHhuC-.js +1 -0
  27. package/dist/assets/WorkspacesPage-DiKDrVBL.js +1 -0
  28. package/dist/assets/editor.worker-DyuDvpVp.js +26 -0
  29. package/dist/assets/index-CjGE0FA2.css +1 -0
  30. package/dist/assets/index-rvTUZ746.js +30 -0
  31. package/dist/assets/vendor-monaco-CdNyTACs.js +11 -0
  32. package/dist/assets/{vendor-react-B7E9rUeC.js → vendor-react-Dd26lTa7.js} +9 -9
  33. package/dist/assets/vendor-router-yJbofmar.js +12 -0
  34. package/dist/assets/vendor-ui-ChI1tM4e.js +17 -0
  35. package/dist/index.html +8 -5
  36. package/dist-server/agents-7NORWYDI.js +1 -0
  37. package/dist-server/chunk-5TX2AE6V.js +2 -0
  38. package/dist-server/chunk-6RXAD73K.js +312 -0
  39. package/dist-server/chunk-GBD6IX6H.js +1 -0
  40. package/dist-server/chunk-JJNLZ7U4.js +2 -0
  41. package/dist-server/chunk-L74QMQAV.js +1 -0
  42. package/dist-server/chunk-OJAAM2XK.js +1 -0
  43. package/dist-server/chunk-PE6WB7WG.js +36 -0
  44. package/dist-server/chunk-RGKFIAYK.js +2 -0
  45. package/dist-server/chunk-UZUF5CZR.js +1 -0
  46. package/dist-server/chunk-WFZH7745.js +2 -0
  47. package/dist-server/chunk-WSL26W3H.js +16 -0
  48. package/dist-server/chunk-XMCSUNYM.js +1 -0
  49. package/dist-server/claude-code-H6WHSYXB.js +1 -0
  50. package/dist-server/harnesses-DH3SOF4F.js +1 -0
  51. package/dist-server/hook-settings-ENUKEL6E.js +1 -0
  52. package/dist-server/hooks-install-KIDJPWBY.js +1 -0
  53. package/dist-server/index.js +326 -117
  54. package/dist-server/prefill-3FPWBCW4.js +14 -0
  55. package/dist-server/preflight-7M4CZVOF.js +13 -0
  56. package/dist-server/publish-review-7GG3AEZI.js +7 -0
  57. package/dist-server/registry-QDC56XWP.js +1 -0
  58. package/dist-server/resolve-env-GWFARUZQ.js +1 -0
  59. package/dist-server/settings-EYM2VUM7.js +1 -0
  60. package/dist-server/setup-status-SRJSWBUH.js +1 -0
  61. package/dist-server/startup.js +1 -1
  62. package/dist-server/store-6VCU4ZDL.js +1 -0
  63. package/package.json +7 -2
  64. package/scripts/demo-claude-welcome.sh +33 -0
  65. package/scripts/patch-docs-screenshots.ts +90 -0
  66. package/scripts/rollup-to-feat.sh +99 -0
  67. package/scripts/seed-docs-demo.ts +219 -0
  68. package/skills/add-agent/SKILL.md +71 -0
  69. package/skills/create-pr/SKILL.md +1 -1
  70. package/skills/create-task/SKILL.md +90 -6
  71. package/skills/review-orchestrator/SKILL.md +192 -0
  72. package/skills/update-task-status/SKILL.md +132 -0
  73. package/templates/hooks/jira-status/jira-status +25 -0
  74. package/templates/hooks/jira-status/jira-status.config.json +6 -0
  75. package/templates/hooks/jira-status/template.json +4 -0
  76. package/dist/assets/AgentEditor-BMirVLY6.js +0 -1
  77. package/dist/assets/OrchestratorPage-CdDThrSZ.js +0 -2
  78. package/dist/assets/SettingsPage-CMS7vQBd.js +0 -9
  79. package/dist/assets/SkillEditor-QV9JvpQN.js +0 -1
  80. package/dist/assets/TaskDetail-nrpoIbuk.js +0 -2
  81. package/dist/assets/TerminalView-CwdJ4h_g.js +0 -3
  82. package/dist/assets/index-CUA5BI9p.js +0 -2
  83. package/dist/assets/index-CjY4eMm_.css +0 -1
  84. package/dist/assets/use-editor-shortcuts-CMkuYcgB.js +0 -1
  85. package/dist/assets/vendor-monaco-Bz7hO8Jn.js +0 -11
  86. package/dist/assets/vendor-router-CDRByCDK.js +0 -12
  87. package/dist/assets/vendor-ui-Diw4ZhZG.js +0 -31
package/README.md CHANGED
@@ -4,185 +4,183 @@
4
4
 
5
5
  # octomux
6
6
 
7
- **Your local command center for autonomous Claude Code agents.**
7
+ > **Coding got faster. Managing agents didn't.**
8
8
 
9
- Pull tasks from Jira, GitHub, or any source your agent can read. Agents work in isolated worktrees. Get notified when they need you. PRs auto-link and tasks auto-close on merge. Review diffs with built-in lazygit. Survives restarts.
9
+ A local web app to **dispatch, watch, and review** parallel **Claude Code** and **Cursor** agents from one place. Kanban for fleet status. One inbox for every "allow this tool?" prompt. In-app diff review with **Ship**. No cloud. MIT.
10
10
 
11
- ![octomux dashboard](assets/screenshots/dashboard-hero.png)
12
-
13
- ## The Loop
14
-
15
- ```
16
- 1. INTAKE Pull Jira tickets or GitHub issues — auto-creates tasks with prompts
17
- 2. EXECUTE Each task gets its own worktree, branch, and Claude Code agents
18
- 3. SUPERVISE Live terminals + notifications when agents finish or need attention
19
- 4. REVIEW Built-in lazygit & lazyvim — check diffs without leaving octomux
20
- 5. MERGE PRs auto-detected and linked — tasks auto-close when PRs merge
21
- 6. RESUME Close your laptop. Reboot. Everything picks back up automatically.
11
+ ```bash
12
+ npm install -g octomux && octomux init && cd your-repo && octomux start
22
13
  ```
23
14
 
24
- > The orchestrator agent can drive this entire loop autonomously or you can control each step from the CLI and dashboard.
25
-
26
- ## Features
27
-
28
- ### Orchestrator: agents managing agents
15
+ Open [http://localhost:7777](http://localhost:7777) describe a task in the composer, pick **Claude Code** or **Cursor**, and watch agents work in place.
29
16
 
30
- A dedicated Claude Code instance that orchestrates your entire workflow. Create tasks, monitor status, add agents, create PRs — all via slash commands (`/create-task`, `/list-tasks`, `/status`, `/create-pr`).
17
+ ## From prompt to merged PR
31
18
 
32
- Wire it to Jira MCP, GitHub CLI, or any source — the orchestrator pulls context and creates properly named tasks with initial prompts. Ships with Claude Code skills (`create-task`, `create-pr`, `create-commit`) that any agent can use directly.
19
+ Three phases, one window:
33
20
 
34
- ![orchestrator](assets/screenshots/orchestrator.png)
21
+ - **01 — Dispatch.** Type a task. Pick Claude Code or Cursor. Hit go. The composer takes plain English, Jira links, or GitHub issue URLs. Drop a second agent on the same branch with one click.
22
+ - **02 — Watch.** See every agent work, live. Each task streams its own view — files the agent is editing, the diff as it grows, terminal output as it runs. When an agent needs permission, the prompt lands in your inbox so you don't have to babysit every pane.
23
+ - **03 — Review & Ship.** Diff review in the same window. File tree, per-file reviewed state, inline comments. Hit **Ship** and the PR auto-links to the task — closes itself when the PR merges.
35
24
 
36
- ### Intake from anywhere
25
+ Code never leaves your laptop. No telemetry, no cloud sync. Crash, reboot, close the lid — `octomux start` restores every task, branch, and session.
37
26
 
38
- - Any Claude Code agent can create tasks via `octomux create-task`
39
- - Works with Jira MCP, GitHub CLI, or anything your agent can read
40
- - Auto-generates task names and initial prompts from ticket context
41
- - Draft tasks: create in draft mode, edit title/prompt/branch before starting
27
+ ## Screenshots
42
28
 
43
- ### Auto-review from reviewer requests
29
+ | | |
30
+ | ----------------------------------------------------------------------------- | -------------------------------------------------------- |
31
+ | **Home inbox + composer** — permission prompts, recent activity, dispatch bar | ![Home](assets/screenshots/dashboard-hero.png) |
32
+ | **Command center** — kanban from backlog → done | ![Command center](assets/screenshots/command-center.png) |
33
+ | **Settings** — default harness, Cursor model & `--force` | ![Settings](assets/screenshots/settings-harnesses.png) |
34
+ | **Task cockpit** — agent tabs, live Claude session, Ship, Done | ![Task detail](assets/screenshots/task-detail.png) |
35
+ | **Diff review** — file tree, reviewed state, inline comments | ![Diff](assets/screenshots/diff-review.png) |
44
36
 
45
- - When you're added as a reviewer on a PR in any tracked repo (any repo that
46
- appears in `octomux recent-repos`), octomux stages a **draft** review task
47
- prefilled with `/review-pr <url>` and PR context
48
- - Tasks stay in draft until you approve — no agents start automatically
49
- - If you haven't approved and the request is withdrawn (review submitted / PR
50
- closed / reviewer removed), the draft is cleaned up on the next poll
51
- - If the review is already running when new commits arrive, the existing agent
52
- is nudged via a tmux message — no duplicate task is created
53
- - Owner identity is resolved once via `gh api user` and cached; override with
54
- `OCTOMUX_GITHUB_LOGIN` if you want octomux to watch a different account
55
-
56
- ### Isolated execution
37
+ ## Features
57
38
 
58
- - Each task gets its own git worktree, branch, and tmux session
59
- - Multiple agents per task, working in parallel
60
- - Custom base branches supported (work from `develop`, not just `main`)
61
- - Your main working tree stays untouched
39
+ - **Sessions inbox** every permission prompt and question lands in one place; reply once, agents keep going. Tab title shows `(N) octomux` when something needs you.
40
+ - **Command center** kanban for backlog → done; drag status, archive, workflow from draft → ship.
41
+ - **In-app diff review** compare to `main`, mark files reviewed, queue inline comments, open lazygit in-editor.
42
+ - **Dual harnesses** run **Claude Code** (`claude`) or **Cursor** (`cursor-agent`) per task; mix agents on one task via **Add agent**.
43
+ - **Worktrees keep agents off each other** — each task gets its own git worktree and `agents/<task-id>` branch; five agents can edit `auth.ts` at the same time without conflicts on your main tree.
44
+ - **Live task view** — see every agent work in real time: files edited, diff growing, terminal output streaming via xterm.js. Attach the same session from the CLI if you prefer.
45
+ - **Agents that dispatch agents** — `/create-task`, `/list-tasks`, `/send-agent-message` skills work inside any Claude Code window; recursive dispatch from inside an agent.
46
+ - **Integrations** — Jira wiring plus orchestrator skills for GitHub / auto-review intake.
47
+ - **CLI ↔ dashboard parity** — `octomux create-task`, `send-message`, `resume-task` — same tasks the UI shows.
48
+ - **Reboot-proof** — WAL SQLite + preserved worktrees across restarts.
49
+ - **Local-only** — no telemetry, no cloud sync, no analytics. Your `.env` stays on the host.
62
50
 
63
- ### Smart supervision
51
+ ## Patterns
64
52
 
65
- - Live terminals in the dashboard via xterm.js
66
- - Color-coded agent activity: green (active), gray (idle), amber (waiting for input)
67
- - Notifications when agents finish, stop unexpectedly, or hit permission prompts
68
- - Browser tab shows `(N) octomux` + red favicon dot when tasks need attention
69
- - Toast notifications with "View" button — one click to the relevant agent
70
- - Send messages to running agents via `octomux send-message`
71
- - Smart status: tasks needing attention surface before idle tasks
53
+ Three workflows octomux makes one-click:
72
54
 
73
- ![task detail](assets/screenshots/task-detail.png)
55
+ ### Verifier — two agents, two opinions
74
56
 
75
- ### Built-in review
57
+ Claude wrote it. Drop Cursor on the same branch for a second pass. Same-model self-review is just self-confirmation — a different model reads the diff without inheriting the first agent's assumptions. Catches missing nonce checks, off-by-one TTLs, and the kind of mistakes that pass type-checking but break in prod.
76
58
 
77
- - Lazygit and lazyvim integrated review diffs inside octomux
78
- - Open ad-hoc shell terminals in any task's worktree from the dashboard
79
- - No context switching to a separate terminal or IDE
80
- - Clean branches ready to push and PR when you're satisfied
59
+ > Finish a task with Claude hit **Add agent** → pick Cursor → reviews land as inline comments → you arbitrate, Ship.
81
60
 
82
- ![lazygit review](assets/screenshots/lazygit-review.png)
61
+ ### Sweep — five PRs by lunch
83
62
 
84
- ### Auto PR detection + merge
63
+ Paste a Jira filter or GitHub issue list into the composer. Each ticket gets its own worktree, branch, and agent. Mix Claude and Cursor across the batch so the model best at each kind of task ends up on it. Come back from standup to a kanban of ready-to-review PRs.
85
64
 
86
- - Background poller detects PRs on task branches via `gh pr list`
87
- - PR URLs auto-linked in task cards — visible from the dashboard
88
- - Tasks auto-close when their PRs are merged
89
- - Zero manual status updates from task creation to merged code
65
+ ### Operator one prompt becomes an epic
90
66
 
91
- ### Survives restarts
67
+ Give an agent the orchestrator skills. It plans the work, breaks down the spec, and dispatches subtasks — each one gets its own worktree, mergeable independently. Inside a Claude Code window, the agent itself becomes the user of octomux. You supervise from the dashboard.
92
68
 
93
- - Full state persistence in SQLite across reboots
94
- - On next `octomux start`, running tasks are recovered and agent sessions resume
95
- - Close your laptop, come back tomorrow, run `octomux start`, keep going
69
+ > `/create-task`, `/list-tasks`, `/send-agent-message` skills inside any Claude Code window. Recursive dispatch.
96
70
 
97
- ### Safety: graduated trust
71
+ ## Quick start
98
72
 
99
- Each task worktree gets a permission config (`.claude/settings.local.json`) with three tiers:
73
+ ```bash
74
+ brew install tmux git
75
+ npm install -g @anthropic-ai/claude-code # and/or Cursor CLI
76
+ npm install -g octomux
77
+ octomux init
78
+ cd your-project
79
+ octomux start
80
+ ```
100
81
 
101
- - **Denied**: `git push --force`, `rm -rf`, `git reset --hard` — always blocked
102
- - **Allowed**: read-only ops, safe writes, non-force `git push` — auto-approved
103
- - **Prompted**: everything else requires explicit user permission
82
+ ```bash
83
+ octomux create-task -t "Add OAuth login" -r .
84
+ octomux create-task -t "Spike with Cursor" -r . --harness cursor
85
+ ```
104
86
 
105
- Permission prompts surface in the dashboard with tool name and input details. Agents can't destroy things without asking first.
87
+ Step-by-step setup, Jira, and orchestrator skills: [ONBOARDING.md](./ONBOARDING.md)
106
88
 
107
- ## Quick Start
89
+ ## How it works
108
90
 
109
- ```bash
110
- npm install -g octomux
111
- cd your-project
112
- octomux start # opens dashboard at localhost:7777
113
- octomux create-task -t "Add auth flow" -r . # create a task from the CLI
91
+ ```
92
+ DISPATCH BRANCH → CODE → INBOX → REVIEW → MERGE
114
93
  ```
115
94
 
116
- Open [http://localhost:7777](http://localhost:7777) to watch agents work, or keep using the CLI.
117
-
118
- ## CLI Commands
119
-
120
- | Command | Description |
121
- | ------------------------------------------------- | ----------------------------------------------- |
122
- | `octomux start` | Launch the local web dashboard |
123
- | `octomux create-task` | Create a new task |
124
- | `octomux list-tasks` | List all tasks |
125
- | `octomux get-task <id>` | Get task details |
126
- | `octomux close-task <id>` | Stop agents and preserve the worktree |
127
- | `octomux delete-task <id>` | Fully clean up task state, branch, and worktree |
128
- | `octomux resume-task <id>` | Resume a previously closed task |
129
- | `octomux add-agent <task-id>` | Add an agent to an existing task |
130
- | `octomux send-message <task-id> <agent-id> "msg"` | Send a message to an agent |
131
-
132
- ## Why octomux?
133
-
134
- | | Without octomux | With octomux |
135
- | -------------------- | ------------------------ | ---------------------------------------- |
136
- | Git isolation | Manual worktrees | Automatic per task |
137
- | Agent visibility | Tab-switching | Single dashboard with activity dots |
138
- | Backlog intake | Copy-paste prompts | Agent-driven from Jira/GH |
139
- | PR tracking | Manual | Auto-detected and linked |
140
- | Task completion | Manual status updates | Auto-closes on PR merge |
141
- | After a reboot | Start over | Auto-resumes |
142
- | Reviewing changes | Switch to terminal + git | Built-in lazygit |
143
- | Agent safety | Hope for the best | Graduated trust: denied/allowed/prompted |
144
- | Lifecycle management | None | draft → running → closed |
145
-
146
- ## How It Works
95
+ | Phase | What happens |
96
+ | ------------ | --------------------------------------------------------------------------- |
97
+ | **Dispatch** | Composer, CLI, orchestrator skills, or Jira/GitHub drafts |
98
+ | **Branch** | Automatic git worktree + `agents/<task-id>` branch |
99
+ | **Code** | tmux session per task; harness launches `claude` or `cursor-agent` |
100
+ | **Inbox** | Every permission prompt or question collects in one place |
101
+ | **Review** | Diff tab, lazygit terminal, mark files reviewed, **Ship** / **Done** |
102
+ | **Merge** | PR poller links branches; tasks close when their PRs merge |
103
+ | _Recovery_ | DB + worktrees survive reboot — `octomux start` picks up where you left off |
104
+
105
+ ## CLI
106
+
107
+ | Command | Description |
108
+ | ------------------------------------ | -------------------------------------------------------- |
109
+ | `octomux start` | Dashboard at `:7777` |
110
+ | `octomux init` | Defaults wizard (Jira, base branch, harness prefs) |
111
+ | `octomux create-task` | New task (`--harness cursor` optional) |
112
+ | `octomux list-tasks` / `get-task` | Inspect tasks |
113
+ | `octomux close-task` / `delete-task` | Stop or fully remove |
114
+ | `octomux resume-task` | Resume a closed task |
115
+ | `octomux add-agent` | Another agent window |
116
+ | `octomux send-message` | Message a running agent — course-correct without restart |
117
+
118
+ ## Architecture
147
119
 
148
120
  ```mermaid
149
- graph LR
150
- O[Orchestrator Agent] -->|octomux create-task| B[Dashboard / API]
151
- A[Any Claude Code Agent] -->|octomux create-task| B
152
- B --> C[Git Worktree]
153
- B --> D[tmux Session]
154
- B --> H[(SQLite DB)]
155
- D --> E[Claude Code Agent]
156
- E -->|done / permission| F[Notifications]
157
- C --> G[lazygit Review]
158
- H -->|recovery on restart| D
159
- E -->|git push| P[GitHub PR]
160
- P -->|poller detects merge| B
121
+ flowchart LR
122
+ subgraph intake [Intake]
123
+ C[Composer]
124
+ CLI[CLI / skills]
125
+ end
126
+ subgraph core [octomux]
127
+ API[API + SQLite]
128
+ IN[Inbox]
129
+ BC[Command center]
130
+ end
131
+ subgraph run [Per task]
132
+ WT[Worktree]
133
+ TM[tmux]
134
+ H[Claude or Cursor]
135
+ end
136
+ C --> API
137
+ CLI --> API
138
+ API --> WT
139
+ API --> TM
140
+ TM --> H
141
+ H -->|hooks| API
142
+ API --> IN
143
+ API --> BC
144
+ WT --> DIFF[Diff review]
145
+ H --> GH[GitHub PR]
146
+ GH -->|poller| API
161
147
  ```
162
148
 
163
149
  ## Requirements
164
150
 
165
- - **macOS** (ARM64 or x64)
166
- - **Node.js 20+**
167
- - **tmux**: `brew install tmux`
168
- - **git**: `brew install git`
169
- - **Claude Code CLI**: `npm install -g @anthropic-ai/claude-code`
170
-
171
- > Xcode Command Line Tools (`xcode-select --install`) may be needed if native dependencies (`better-sqlite3`, `node-pty`) require local compilation.
151
+ - macOS (ARM64 or x64), Node.js 20+
152
+ - `tmux`, `git`
153
+ - At least one harness: **Claude Code** (`claude`) and/or **Cursor CLI** (`cursor-agent`)
154
+ - Recommended: `lazygit`, `neovim`
172
155
 
173
156
  ## Configuration
174
157
 
175
- | Option | Description | Default |
176
- | --------------- | ------------------------------- | ----------------------- |
177
- | `--port <port>` | Port for the dashboard | `7777` |
178
- | `--no-open` | Do not auto-open the browser | — |
179
- | `PORT` | Alternative to `--port` | `7777` |
180
- | `OCTOMUX_URL` | Server URL used by CLI commands | `http://localhost:7777` |
158
+ | Variable / flag | Purpose |
159
+ | ------------------------- | -------------------------------- |
160
+ | `OCTOMUX_PORT` / `--port` | Dashboard port (default `7777`) |
161
+ | `OCTOMUX_URL` | CLI API base URL |
162
+ | `OCTOMUX_DB_PATH` | Override task DB path |
163
+ | `OCTOMUX_GITHUB_LOGIN` | Reviewer-request polling account |
164
+
165
+ ## FAQ
166
+
167
+ **What's the difference between octomux and just running tmux + Claude Code?**
168
+ octomux adds the kanban, the inbox, and diff review on top. tmux is just plumbing underneath.
169
+
170
+ **Does it work with Cursor?**
171
+ Yes. Pick Claude Code or Cursor per task. Mix them on the same task with **Add agent**.
172
+
173
+ **What happens if two agents touch the same file?**
174
+ They can't — each task runs in its own git worktree on its own branch. Five agents can edit `auth.ts` at the same time without conflicts on your main tree.
175
+
176
+ **What if my laptop reboots or crashes?**
177
+ Run `octomux start`. Tasks, branches, terminals, and review state all come back.
178
+
179
+ **How do I track what each agent is costing me?**
180
+ Each agent's tmux session has its own session log; Claude Code and Cursor both emit token usage there. A first-class cost view in the dashboard is on the roadmap.
181
181
 
182
182
  ## Links
183
183
 
184
- - GitHub: [github.com/ShreyPaharia/octomux](https://github.com/ShreyPaharia/octomux)
185
- - npm: [npmjs.com/package/octomux](https://www.npmjs.com/package/octomux)
186
- - Landing page: [octomux.com](https://octomux.com)
184
+ - [GitHub](https://github.com/ShreyPaharia/octomux) · [npm](https://www.npmjs.com/package/octomux) · [octomux.dev](https://octomux.dev)
187
185
 
188
- Contributions welcome [open an issue](https://github.com/ShreyPaharia/octomux/issues) or submit a PR.
186
+ Issues and PRs welcome.
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * octomux-hook-bridge.js — Cursor hook bridge script
5
+ *
6
+ * Self-contained Node.js script (no npm dependencies). Copied into each
7
+ * worktree's .octomux-hooks/ directory alongside a config.json that carries
8
+ * {baseUrl, token}. Cursor invokes this as a hook command with event JSON
9
+ * on stdin; the bridge forwards it to the octomux server and writes the
10
+ * response JSON to stdout.
11
+ *
12
+ * Fail-open principle: any error (network, parse, missing config) writes '{}'
13
+ * to stdout and exits 0 so Cursor is never blocked by bridge failures.
14
+ *
15
+ * The bridge NEVER exits with code 2. Deny is communicated via stdout JSON
16
+ * {"permission":"deny",...} with exit 0.
17
+ */
18
+
19
+ import fs from 'node:fs';
20
+ import http from 'node:http';
21
+ import { fileURLToPath } from 'node:url';
22
+ import path from 'node:path';
23
+
24
+ // Hardcoded denylist — mirrors Claude's DENIED_TOOLS destructive set.
25
+ // These regexes are checked against event.command for beforeShellExecution.
26
+ const DENYLIST = [
27
+ { re: /^\s*rm\s+-rf(\s|$)/, label: 'rm -rf' },
28
+ { re: /^\s*git\s+push\s+--force(\s|$)/, label: 'git push --force' },
29
+ { re: /^\s*git\s+reset\s+--hard(\s|$)/, label: 'git reset --hard' },
30
+ ];
31
+
32
+ function writeStdout(obj) {
33
+ process.stdout.write(JSON.stringify(obj));
34
+ }
35
+
36
+ /**
37
+ * POST JSON to a local http:// URL. Returns a promise that resolves with the
38
+ * response status code on success, or rejects on network/timeout error.
39
+ */
40
+ function postJson(url, token, body) {
41
+ return new Promise((resolve, reject) => {
42
+ const parsed = new URL(url);
43
+ const qs = '?token=' + encodeURIComponent(token);
44
+
45
+ const options = {
46
+ protocol: parsed.protocol,
47
+ hostname: parsed.hostname,
48
+ port: parsed.port || 80,
49
+ path: parsed.pathname + qs,
50
+ method: 'POST',
51
+ headers: {
52
+ 'Content-Type': 'application/json',
53
+ },
54
+ };
55
+
56
+ const payload = JSON.stringify(body);
57
+
58
+ const req = http.request(options, (res) => {
59
+ // Consume response body to free socket
60
+ res.resume();
61
+ res.on('end', () => resolve(res.statusCode));
62
+ });
63
+
64
+ // 5-second timeout — fail-open on expiry
65
+ req.setTimeout(5000, () => {
66
+ req.destroy(new Error('Hook bridge request timed out'));
67
+ });
68
+
69
+ req.on('error', reject);
70
+ req.write(payload);
71
+ req.end();
72
+ });
73
+ }
74
+
75
+ async function main() {
76
+ // Step 1: read all stdin synchronously. fd 0 = stdin.
77
+ let stdinText = '';
78
+ try {
79
+ stdinText = fs.readFileSync(0, 'utf8');
80
+ } catch {
81
+ // No stdin available — treat as empty
82
+ }
83
+
84
+ // Step 2: parse stdin JSON. Malformed input → fail-open.
85
+ let event;
86
+ try {
87
+ event = JSON.parse(stdinText);
88
+ } catch {
89
+ writeStdout({});
90
+ process.exit(0);
91
+ }
92
+
93
+ // Step 3: resolve and read sibling config.json.
94
+ let baseUrl, token;
95
+ try {
96
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
97
+ const configPath = path.join(scriptDir, 'config.json');
98
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
99
+ baseUrl = config.baseUrl;
100
+ token = config.token;
101
+ if (!baseUrl || !token) throw new Error('config.json missing baseUrl or token');
102
+ } catch (err) {
103
+ console.error('[octomux-hook-bridge] Failed to load config.json:', err);
104
+ writeStdout({});
105
+ process.exit(0);
106
+ }
107
+
108
+ // Step 4: branch on hook_event_name.
109
+ const eventName = event.hook_event_name;
110
+
111
+ if (eventName === 'sessionStart') {
112
+ try {
113
+ await postJson(baseUrl + '/api/hooks/session-start', token, {
114
+ conversation_id: event.conversation_id,
115
+ session_id: event.session_id,
116
+ is_background_agent: event.is_background_agent ?? false,
117
+ });
118
+ } catch (err) {
119
+ // HTTP error — fail-open
120
+ console.error('[octomux-hook-bridge] sessionStart HTTP error:', err);
121
+ }
122
+ writeStdout({});
123
+ } else if (eventName === 'beforeSubmitPrompt') {
124
+ try {
125
+ await postJson(baseUrl + '/api/hooks/user-prompt-submit', token, {
126
+ conversation_id: event.conversation_id,
127
+ prompt: event.prompt,
128
+ });
129
+ } catch (err) {
130
+ console.error('[octomux-hook-bridge] beforeSubmitPrompt HTTP error:', err);
131
+ }
132
+ writeStdout({ continue: true });
133
+ } else if (eventName === 'beforeShellExecution') {
134
+ // Apply denylist locally — no HTTP call.
135
+ const command = event.command ?? '';
136
+ for (const rule of DENYLIST) {
137
+ if (rule.re.test(command)) {
138
+ writeStdout({
139
+ permission: 'deny',
140
+ user_message: 'Blocked by octomux denylist: ' + rule.label,
141
+ });
142
+ process.exit(0);
143
+ }
144
+ }
145
+ writeStdout({ permission: 'allow' });
146
+ } else if (eventName === 'postToolUse' || eventName === 'afterFileEdit') {
147
+ // Normalize both events to the {tool_name, tool_input} shape that
148
+ // /api/hooks/post-tool-use expects (Claude's PostToolUse shape).
149
+ // - postToolUse already carries tool_name + tool_input, pass through.
150
+ // - afterFileEdit carries file_path at top-level; synthesize a tool_use
151
+ // so the server's current_summary deriver picks up the file path.
152
+ let body;
153
+ if (eventName === 'afterFileEdit') {
154
+ body = {
155
+ conversation_id: event.conversation_id,
156
+ tool_name: 'Edit',
157
+ tool_input: { file_path: event.file_path },
158
+ };
159
+ } else {
160
+ body = {
161
+ conversation_id: event.conversation_id,
162
+ tool_name: event.tool_name,
163
+ tool_input: event.tool_input,
164
+ };
165
+ }
166
+ try {
167
+ await postJson(baseUrl + '/api/hooks/post-tool-use', token, body);
168
+ } catch (err) {
169
+ console.error('[octomux-hook-bridge] postToolUse/afterFileEdit HTTP error:', err);
170
+ }
171
+ writeStdout({});
172
+ } else {
173
+ // Unknown event — fail-open
174
+ writeStdout({});
175
+ }
176
+ }
177
+
178
+ main().catch((err) => {
179
+ // Top-level safety net — any unhandled error must not crash Cursor
180
+ console.error('[octomux-hook-bridge] Unhandled error:', err);
181
+ writeStdout({});
182
+ process.exit(0);
183
+ });