intercomm-aimfp 0.4.0

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 (59) hide show
  1. package/README.md +353 -0
  2. package/dist/claude-tui.d.ts +7 -0
  3. package/dist/claude-tui.d.ts.map +1 -0
  4. package/dist/claude-tui.js +55 -0
  5. package/dist/claude-tui.js.map +1 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +144 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/config.d.ts +14 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +44 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/db.d.ts +9 -0
  15. package/dist/db.d.ts.map +1 -0
  16. package/dist/db.js +117 -0
  17. package/dist/db.js.map +1 -0
  18. package/dist/fs-wrapper.d.ts +3 -0
  19. package/dist/fs-wrapper.d.ts.map +1 -0
  20. package/dist/fs-wrapper.js +9 -0
  21. package/dist/fs-wrapper.js.map +1 -0
  22. package/dist/git-wrapper.d.ts +11 -0
  23. package/dist/git-wrapper.d.ts.map +1 -0
  24. package/dist/git-wrapper.js +44 -0
  25. package/dist/git-wrapper.js.map +1 -0
  26. package/dist/mcp-entry.d.ts +3 -0
  27. package/dist/mcp-entry.d.ts.map +1 -0
  28. package/dist/mcp-entry.js +8 -0
  29. package/dist/mcp-entry.js.map +1 -0
  30. package/dist/mcp-server.d.ts +2 -0
  31. package/dist/mcp-server.d.ts.map +1 -0
  32. package/dist/mcp-server.js +557 -0
  33. package/dist/mcp-server.js.map +1 -0
  34. package/dist/orchestrate.d.ts +52 -0
  35. package/dist/orchestrate.d.ts.map +1 -0
  36. package/dist/orchestrate.js +226 -0
  37. package/dist/orchestrate.js.map +1 -0
  38. package/dist/protocol.d.ts +3 -0
  39. package/dist/protocol.d.ts.map +1 -0
  40. package/dist/protocol.js +33 -0
  41. package/dist/protocol.js.map +1 -0
  42. package/dist/store.d.ts +26 -0
  43. package/dist/store.d.ts.map +1 -0
  44. package/dist/store.js +186 -0
  45. package/dist/store.js.map +1 -0
  46. package/dist/task-contract.d.ts +4 -0
  47. package/dist/task-contract.d.ts.map +1 -0
  48. package/dist/task-contract.js +111 -0
  49. package/dist/task-contract.js.map +1 -0
  50. package/dist/tmux-wrapper.d.ts +17 -0
  51. package/dist/tmux-wrapper.d.ts.map +1 -0
  52. package/dist/tmux-wrapper.js +66 -0
  53. package/dist/tmux-wrapper.js.map +1 -0
  54. package/dist/types.d.ts +92 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +56 -0
  57. package/dist/types.js.map +1 -0
  58. package/package.json +35 -0
  59. package/system-prompt.md +245 -0
package/README.md ADDED
@@ -0,0 +1,353 @@
1
+ # InterComm AIMFP
2
+
3
+ Local-only coordination system for multiple Claude Code instances working on the same project. One instance is master, the rest are workers. The master delegates tasks and controls workers via tmux. All state lives in a single SQLite database — no servers, no HTTP, no sockets.
4
+
5
+ **Requires tmux** and the **AIMFP MCP server**. InterComm AIMFP is a hard AIMFP addon — the worker flow runs `aimfp_run` and `git_create_branch`, and the master integrates each branch via `export_state_changeset` / `apply_state_changeset`. Without the AIMFP MCP server connected (listed in the project's `.mcp.json`), instances can still coordinate and isolate files via worktrees, but there is **no AIMFP tracking and no semantic-changeset merge**. (InterComm's own code stays AIMFP-agnostic — it never reads AIMFP's DB.)
6
+
7
+ ## How It Works
8
+
9
+ ```
10
+ ┌─────────────────────────────────────────────────────┐
11
+ │ User ↔ Master (Claude Code) │
12
+ │ │ │
13
+ │ ├── tmux send-keys ──→ Worker-1 (tmux session) │
14
+ │ ├── tmux send-keys ──→ Worker-2 (tmux session) │
15
+ │ └── tmux send-keys ──→ Worker-N (tmux session) │
16
+ │ │
17
+ │ All instances share: .intercomm-aimfp/intercomm.db │
18
+ └─────────────────────────────────────────────────────┘
19
+ ```
20
+
21
+ 1. The user talks to the **master** instance only.
22
+ 2. The user either tells the master "I have N tmux sessions available" or the master asks "please spin up N tmux sessions for this task."
23
+ 3. The master assigns work via `intercomm_assign` — a **thin-pointer task contract** that points the worker at an AIMFP work entity — and **wakes** the worker (via `intercomm_wake` / tmux send-keys).
24
+ 4. Each worker registers, reads its contract, runs `aimfp_run` in its own git worktree, continues the assigned AIMFP entity on its own branch, then reports `done` (with `branch` + `commit`) via InterComm.
25
+ 5. Workers do **not** poll. They sit idle until the master pushes a prompt via tmux.
26
+
27
+ InterComm stays **AIMFP-agnostic**: it isolates files (worktrees), carries the pointer contract, and tracks status — it never reads AIMFP's DB and never runs `git merge`. The master uses AIMFP's `export_state_changeset` / `apply_state_changeset` to integrate each worker's DB tracking (see [Integration](#integration)).
28
+
29
+ ## Install
30
+
31
+ 1. Copy the `intercommAIMFP/` folder somewhere permanent on your machine.
32
+
33
+ 2. Install dependencies and build:
34
+ ```bash
35
+ cd /path/to/intercommAIMFP
36
+ npm install
37
+ npm run build
38
+ ```
39
+
40
+ 3. Add the MCP server to your project's `.mcp.json` (create it in your project root if it doesn't exist):
41
+ ```json
42
+ {
43
+ "mcpServers": {
44
+ "intercomm": {
45
+ "command": "node",
46
+ "args": ["/absolute/path/to/intercommAIMFP/dist/mcp-entry.js"]
47
+ }
48
+ }
49
+ }
50
+ ```
51
+ Replace `/absolute/path/to/intercommAIMFP` with the actual path on your machine.
52
+
53
+ That's it — there is **no protocol to paste**. The master/worker coordination
54
+ protocol is delivered automatically via the MCP server's `instructions` field the
55
+ moment any instance connects (master or worker), the same way AIMFP injects its
56
+ rules. It travels with the server into every project, so your `CLAUDE.md` stays
57
+ yours (e.g. AIMFP-only) and is never touched. Any instance can re-read the full
58
+ protocol on demand with the `intercomm_get_protocol` tool — useful after a long
59
+ session compacts context. (`system-prompt.md` in this repo is the single in-repo
60
+ source for that injected text; edit it there and rebuild.)
61
+
62
+ ## Install as a Claude Code plugin
63
+
64
+ InterComm AIMFP is also packaged as a **Claude Code plugin** (the same model as
65
+ AIMFP). Installing the plugin is the supported one-step distribution path — it
66
+ wires up the MCP server, the slash commands, the setup skill, and the hooks in
67
+ any project, and auto-updates the server. The manual `.mcp.json` install above is
68
+ the **development** path (running your local `dist/`); the plugin is the
69
+ **distribution** path. Use one or the other for a given project, not both, or you
70
+ will get a duplicate `intercomm` server.
71
+
72
+ Once the package is published to npm and this repo is pushed:
73
+
74
+ ```bash
75
+ # add this repo as a plugin marketplace, then install
76
+ claude plugin marketplace add aryanduntley/intercommAIMFP
77
+ claude plugin install intercomm-aimfp
78
+ ```
79
+
80
+ The plugin's `intercomm-plugin/.mcp.json` runs the server via
81
+ `npx -y -p intercomm-aimfp@latest intercomm-mcp`, so npm compiles the native
82
+ `better-sqlite3` binding for each machine on first run and `@latest` keeps the
83
+ server current — no bundled binaries. It ships:
84
+
85
+ - **MCP server** — all 19 coordination tools, with the protocol auto-injected via the server's `instructions` field.
86
+ - **Commands** — `/register`, `/spawn`, `/status`, `/teardown`.
87
+ - **Skill** — `intercomm-mode` (a tiny setup bootstrap; the real protocol lives in the injected instructions).
88
+ - **Hooks** — a `SessionStart` note on how to begin coordinating.
89
+
90
+ > **Requires the AIMFP MCP server.** InterComm AIMFP is a hard AIMFP addon. The
91
+ > plugin does not bundle AIMFP — install it separately (its own plugin or
92
+ > `python3 -m aimfp` in your `.mcp.json`). The master's startup preflight warns if
93
+ > the AIMFP tools are absent.
94
+
95
+ **Local development / testing (before publishing):**
96
+
97
+ ```bash
98
+ # load the plugin straight from this working tree
99
+ claude --plugin-dir /path/to/intercommAIMFP
100
+ ```
101
+
102
+ ### Releasing (versioning + publish)
103
+
104
+ Bump every embedded version in one step, then commit and push — CI publishes:
105
+
106
+ ```bash
107
+ node dev/bump-version.mjs <X.Y.Z> # syncs package.json + .claude-plugin/plugin.json
108
+ # + the McpServer version in src/mcp-server.ts,
109
+ # resyncs the lockfile, then runs `npm run build`
110
+ git commit -am "release X.Y.Z" && git push
111
+ ```
112
+
113
+ `.github/workflows/publish.yml` publishes `intercomm-aimfp@X.Y.Z` to npm on push to
114
+ `main` whenever the version is new (idempotent). It needs the repo secret
115
+ `NPM_TOKEN`; see the one-time setup below.
116
+
117
+ ### One-time npm + CI setup
118
+
119
+ Manual, one-time steps to connect GitHub → npm. After these, `bump → commit →
120
+ push` is the whole release.
121
+
122
+ 1. **npm account + name.** Have an npm account and `npm login`. The package name
123
+ `intercomm-aimfp` must be free or owned by you (currently unpublished).
124
+ 2. **First publish by hand.** CI publishes *updates*, but the package must exist
125
+ first. Once, from the repo root:
126
+ ```bash
127
+ npm install && npm run build && npm publish --access public
128
+ ```
129
+ (`--access public` is required the first time for an unscoped public package.)
130
+ 3. **Create an npm token.** npm → Account → Access Tokens → Generate → **Automation**
131
+ type (works in CI without 2FA). Copy it.
132
+ 4. **Add it to GitHub.** Repo → Settings → Secrets and variables → Actions → New
133
+ repository secret → name it **`NPM_TOKEN`**, paste the token.
134
+
135
+ That is the *entire* GitHub↔npm connection. There is **no separate "worker" to
136
+ create** — the workflow file `.github/workflows/publish.yml` *is* the worker;
137
+ GitHub Actions runs it automatically once it is on `main`. (Actions is enabled by
138
+ default. The native `better-sqlite3` build is skipped in CI via `--ignore-scripts`;
139
+ the published artifact is `dist/` + `system-prompt.md`, and the consumer's
140
+ `npx`/`npm install` compiles the native binding.)
141
+
142
+ ## Usage
143
+
144
+ The master spawns and controls workers itself through InterComm's MCP tools — no shell scripts and no manual tmux setup required. (The `scripts/*.sh` helpers remain as a dev-only fallback; see [Dev scripts](#dev-scripts).)
145
+
146
+ ### 1. Register the master
147
+
148
+ In your main Claude Code instance, register as master (via the `intercomm_register(role: "master")` tool, or just tell it "you are the master"). This must happen **before** spawning workers, so workers can message `master` on startup.
149
+
150
+ ### 2. Spawn workers automatically
151
+
152
+ From the master, run:
153
+
154
+ From the master, call the **`intercomm_spawn`** tool:
155
+
156
+ ```
157
+ intercomm_spawn(count: 3)
158
+ ```
159
+
160
+ This creates 3 detached tmux sessions (`worker-1`, `worker-2`, `worker-3`), launches Claude Code in each (with `INTERCOMM_DB_ROOT` pinned to the shared bus, so they load `.mcp.json` + `CLAUDE.md`), auto-clears the first-run trust / MCP-approval dialogs, and wakes each to register and read its task. It returns immediately with the session list — workers self-register asynchronously, so call `intercomm_status` to confirm the `session ↔ worker-N` mapping.
161
+
162
+ Useful parameters:
163
+
164
+ | Param | Effect |
165
+ |---|---|
166
+ | `worktrees: true` | Launch each worker in its own isolated git worktree (branch-per-agent) |
167
+ | `worktree_base` | Git ref each worktree checks out detached (default: `main`) |
168
+ | `bootstrap` | Setup command run inside each new worktree (e.g. `npm install`) |
169
+ | `perm_mode` | `claude --permission-mode` (default: `acceptEdits`; `bypassPermissions` for fully hands-off) |
170
+ | `prefix` | tmux session-name prefix (default: `worker`) |
171
+ | `ready_timeout` | Per-session seconds to clear boot dialogs (default: 20) |
172
+ | `wake: false` | Create + launch only; wake them yourself later via `intercomm_wake` |
173
+
174
+ ### 3. Approve flagged permission prompts
175
+
176
+ In the default `acceptEdits` mode, workers auto-accept file edits but **pause on Bash and other tools**. A blocked worker is frozen and cannot report over InterComm, so the master polls for them with **`intercomm_scan`**:
177
+
178
+ ```
179
+ intercomm_scan()
180
+ ```
181
+
182
+ It reports each worker pane's state (`blocked`, `trust`, `bypass`, `ready`, `running`, `idle`). Clear any pane that needs attention with **`intercomm_approve`**, which selects the right option for whichever dialog it's on:
183
+
184
+ ```
185
+ intercomm_approve(worker: "worker-1")
186
+ ```
187
+
188
+ To skip approvals entirely, spawn with `perm_mode: "bypassPermissions"`.
189
+
190
+ ### 4. Let it run, then tear down
191
+
192
+ The master delegates via **`intercomm_assign`** (which builds the thin-pointer task contract, records it, and wakes the worker), monitors with `intercomm_scan` / `intercomm_read`, and collects results. When done, **`intercomm_teardown`** kills the sessions, removes the worktrees, and reaps the registry in one call:
193
+
194
+ ```
195
+ intercomm_assign(
196
+ worker: "worker-1",
197
+ role_instructions: "AIMFP user=alice; stay within src/auth/**; report branch+commit when done",
198
+ target_type: "task", target_id: 42,
199
+ )
200
+ intercomm_teardown() # pass worktrees: true if you spawned with worktrees
201
+ ```
202
+
203
+ ## Task Contract (thin pointer)
204
+
205
+ The master does **not** ship prose instructions to a worker. It ships a small JSON **pointer** to an AIMFP work entity; the worker resolves it itself by running `aimfp_run` in its clone and continuing that entity the normal AIMFP way (its own `git_create_branch` on `aimfp-{user}-{number}`, its own tracking + validation). InterComm carries the contract as opaque message content — it never resolves the pointer and never reads `project.db`.
206
+
207
+ ```json
208
+ {
209
+ "kind": "task_contract",
210
+ "v": 2,
211
+ "role": "worker",
212
+ "role_instructions": "Continue the assigned AIMFP entity. Stay within your assigned files.",
213
+ "aimfp_target": { "type": "task", "id": 42, "slug": "task-implement-auth-9f3a1c20" },
214
+ "reportBack": ["branch", "commit"]
215
+ }
216
+ ```
217
+
218
+ | Field | Meaning |
219
+ |---|---|
220
+ | `role` / `role_instructions` | Worker role label + free-form guidance (assigned files, a **distinct AIMFP user identity** per worker so `aimfp-{user}-{number}` branches don't collide). |
221
+ | `aimfp_target` | The AIMFP entity to continue: `type` (task / milestone / subtask / sidequest / item) plus an integer `id` and/or a stable `slug` (at least one). |
222
+ | `reportBack` | Fields the worker returns on `done` — at minimum `branch` + `commit`, so the master can export a changeset from the branch. |
223
+
224
+ `intercomm_assign` builds this for you; `intercomm_read` shows the worker a validated summary (or an error if the contract is malformed). A worker that can't parse the contract sends a `question` and waits — it never acts on a bad contract.
225
+
226
+ ## Integration
227
+
228
+ When a worker reports `done`, the master integrates its branch — **source and AIMFP DB state are merged by different mechanisms**:
229
+
230
+ 1. **Text-merge the worker's source** into `main` (normal code review / conflict resolution). The binary `project.db` is **never** git-merged.
231
+ 2. **Export the worker's DB tracking** as a semantic changeset: AIMFP `export_state_changeset(base_main_commit, branch)` — an integer-free diff keyed on stable semantic keys.
232
+ 3. **Apply it onto main**: AIMFP `apply_state_changeset(changeset)` — a 3-way merge that auto-applies non-overlapping changes, mints canonical IDs, rewrites references, and reports conflicts for the master to resolve.
233
+ 4. **Commit** merged source + updated `project.db` to main; move to the next branch.
234
+
235
+ InterComm only **tracks** the lifecycle (`intercomm_worktree_list` is the master's merge-queue view); the DB merge intelligence lives entirely in AIMFP.
236
+
237
+ > **Parallel git-worktree runs are supported and validated end-to-end** — 4 workers in isolated worktrees through the full `export_state_changeset` → `apply_state_changeset` merge chain, including concurrent shared-type conflict prediction + resolution. Each worker's AIMFP tracking is committed on its own branch (requires the worktree-aware AIMFP build). One residual AIMFP-side caveat: a worker should treat `<its worktree>/src` as the source dir even if `get_source_directory()` still returns the shared-checkout path — the worker pre-flight guard checks this.
238
+
239
+ ### Dev scripts
240
+
241
+ `scripts/spawn-workers.sh`, `scan-workers.sh`, and `kill-workers.sh` predate the tools and remain for local development / debugging only — they are **not** a runtime dependency. The MCP tools above are the supported path when InterComm is dropped into any project as an MCP server. (Claude Code's TUI needs a double `Enter` to submit a prompt; the tools and scripts both handle that for you.)
242
+
243
+ ## MCP Tools (19 total)
244
+
245
+ **Identity & messaging**
246
+
247
+ | Tool | Description |
248
+ |---|---|
249
+ | `intercomm_register` | Register as master or worker. Initializes DB. Workers auto-assign lowest available `worker-N` name. |
250
+ | `intercomm_send` | Send a direct message to a specific peer. |
251
+ | `intercomm_broadcast` | Broadcast a message to all peers. |
252
+ | `intercomm_read` | Read all new messages since last check. Updates read cursor. For `task` messages, also surfaces the parsed, validated thin-pointer contract. |
253
+ | `intercomm_escalate` | **Worker→master, no polling.** Persist a question/decision to the master AND wake it in its tmux pane (the server does the wake; the worker never touches tmux). Set `needs_user` when the master must ask the human. Returns `{persisted, woke, reason?}`. |
254
+ | `intercomm_assign` | Master-only. Assign work as a thin-pointer task contract (`{role, role_instructions, aimfp_target, reportBack}`): builds it, records the `task` message, and wakes the worker. |
255
+ | `intercomm_status` | Show all instances and their state. |
256
+ | `intercomm_signoff` | Cleanly deactivate this instance before shutting down. |
257
+ | `intercomm_clear` | Delete old messages (master-only). |
258
+ | `intercomm_get_protocol` | Re-read the full master/worker coordination protocol on demand (the same text auto-injected via the server's MCP `instructions` on connect). No registration required. |
259
+
260
+ **Orchestration (master-only) — the tool-driven lifecycle, no shell scripts**
261
+
262
+ | Tool | Description |
263
+ |---|---|
264
+ | `intercomm_spawn` | Spawn N workers in detached tmux sessions; launch claude (optionally one git worktree each), auto-clear boot dialogs, wake to self-register. Non-blocking. |
265
+ | `intercomm_wake` | Push a prompt into a worker's pane (resolves a worker id or raw tmux target). |
266
+ | `intercomm_scan` | Report each worker pane's state (blocked / trust / bypass / ready / running / idle). |
267
+ | `intercomm_approve` | Clear a worker's blocking dialog (trust / MCP-approval / bypass / permission prompt). |
268
+ | `intercomm_teardown` | Kill worker sessions, remove their worktrees, and reap the instance rows in one call. |
269
+
270
+ **Worktree registry (master-only) — branch-per-agent isolation**
271
+
272
+ | Tool | Description |
273
+ |---|---|
274
+ | `intercomm_worktree_add` | Create an isolated git worktree (detached) for a worker and register it. |
275
+ | `intercomm_worktree_list` | List registered worktrees and their lifecycle status (the merge-queue view). |
276
+ | `intercomm_worktree_set_status` | Update a worktree's status; record the branch a worker reported back. |
277
+ | `intercomm_worktree_remove` | Remove a worker's git worktree and mark it removed. |
278
+
279
+ ## Message Types
280
+
281
+ | Type | Purpose |
282
+ |---|---|
283
+ | `task` | Master assigns work to a worker |
284
+ | `status` | Instance reports progress |
285
+ | `question` | Instance asks for input |
286
+ | `answer` | Response to a question |
287
+ | `announce` | Broadcast information to all |
288
+ | `done` | Instance signals task completion |
289
+
290
+ ## Storage
291
+
292
+ All state is stored in `.intercomm-aimfp/intercomm.db` (SQLite, WAL mode) created in the project root. Four tables: `instances` (incl. `tmux_target` for wake/scan/approve), `messages`, `read_cursors` (monotonic `last_read_seq` cursor), and `worktrees` (the branch-per-agent registry).
293
+
294
+ ## Stale Detection
295
+
296
+ Every tool call updates the instance's `last_active` timestamp. An instance is considered stale after **30 seconds** of inactivity. If the master goes stale, a worker can claim master via `intercomm_register(role: "master")`.
297
+
298
+ ## Troubleshooting
299
+
300
+ ### MCP server fails to start after a Node.js upgrade
301
+
302
+ `better-sqlite3` is a native (C++) addon compiled against a specific Node.js ABI. If you upgrade Node.js (via `nvm`, a system update, etc.), the prebuilt binary no longer loads and the MCP server exits immediately on startup — so every tool call fails. The give-away error (visible by running the entry point directly) looks like:
303
+
304
+ ```
305
+ The module '.../better-sqlite3/build/Release/better_sqlite3.node'
306
+ was compiled against a different Node.js version using
307
+ NODE_MODULE_VERSION 131. This version of Node.js requires
308
+ NODE_MODULE_VERSION 137.
309
+ ```
310
+
311
+ **Fix:** rebuild the native module against your current Node.js:
312
+
313
+ ```bash
314
+ cd /path/to/intercommAIMFP
315
+ npm rebuild better-sqlite3 # or: npm install
316
+ ```
317
+
318
+ To diagnose MCP startup failures in general, run the entry point manually and watch stderr:
319
+
320
+ ```bash
321
+ node dist/mcp-entry.js # prints "InterComm AIMFP MCP server error: ..." on failure
322
+ ```
323
+
324
+ ## Known Limitations
325
+
326
+ ### Worker Permission Prompts
327
+
328
+ Claude Code requires approval for certain tool calls (e.g., running bash commands). When a worker hits a permission prompt it **blocks** until answered, and while blocked it cannot report over InterComm — so the master must detect blocked workers by polling their panes.
329
+
330
+ This is handled, not eliminated:
331
+ - Workers are spawned in `acceptEdits` mode by default (file edits auto-approved; Bash/other still prompt).
332
+ - `intercomm_scan` polls every worker pane and reports which are blocked.
333
+ - The master clears the dialog with `intercomm_approve` (it reads the pane via `capture-pane` and selects the right option via `send-keys`).
334
+
335
+ **Reduce or remove the prompts further:**
336
+ - Spawn with `--bypass` (`bypassPermissions`) for fully hands-off workers (use with caution — workers run anything unprompted).
337
+ - Pre-approve common operations in `.claude/settings.local.json` (`permissions.allow`), e.g. `Bash(npm run:*)`. The InterComm MCP tools are already allowlisted in this repo.
338
+
339
+ ### tmux Prompt Submission
340
+
341
+ Claude Code's input requires an extra `Enter` keystroke to submit prompts sent via `tmux send-keys`. Always use `Enter Enter` (double Enter) when waking workers.
342
+
343
+ ## CLI (Debug)
344
+
345
+ The CLI is for debugging only — normal usage is through MCP tools.
346
+
347
+ ```
348
+ intercomm status Show all instances
349
+ intercomm send --from <id> <to> <message> [--type t] Send a direct message
350
+ intercomm broadcast --from <id> <message> [--type t] Broadcast to all
351
+ intercomm read --id <id> [--all] Read new messages
352
+ intercomm clear [--keep <n>] Clear old messages (default: keep 100)
353
+ ```
@@ -0,0 +1,7 @@
1
+ export type PaneState = "trust" | "mcp_approval" | "bypass" | "blocked" | "ready" | "running" | "idle";
2
+ export type KeyGroup = readonly string[];
3
+ export declare const detectPaneState: (capture: string) => PaneState;
4
+ export declare const clearKeystrokes: (state: PaneState) => readonly KeyGroup[];
5
+ export declare const isBooted: (state: PaneState) => boolean;
6
+ export declare const isBlocking: (state: PaneState) => boolean;
7
+ //# sourceMappingURL=claude-tui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-tui.d.ts","sourceRoot":"","sources":["../src/claude-tui.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,cAAc,GACd,QAAQ,GACR,SAAS,GACT,OAAO,GACP,SAAS,GACT,MAAM,CAAC;AAGX,MAAM,MAAM,QAAQ,GAAG,SAAS,MAAM,EAAE,CAAC;AAYzC,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,KAAG,SAQjD,CAAC;AAQF,eAAO,MAAM,eAAe,GAAI,OAAO,SAAS,KAAG,SAAS,QAAQ,EAYnE,CAAC;AAIF,eAAO,MAAM,QAAQ,GAAI,OAAO,SAAS,KAAG,OACF,CAAC;AAG3C,eAAO,MAAM,UAAU,GAAI,OAAO,SAAS,KAAG,OACX,CAAC"}
@@ -0,0 +1,55 @@
1
+ // Pure logic for reading and clearing Claude Code's terminal (TUI) dialogs.
2
+ //
3
+ // Ported from scripts/spawn-workers.sh (wait_ready) and scripts/scan-workers.sh.
4
+ // NO IO here — these functions take a captured pane string and return a state, or
5
+ // the keystrokes that would clear it. tmux-wrapper.ts carries the keystrokes; the
6
+ // spawn/scan/approve tools compose the two. Keeping detection pure makes the
7
+ // dialog matrix unit-testable without a live tmux or Claude process.
8
+ const TRUST_RE = /trust the files|do you trust/i;
9
+ const MCP_RE = /new MCP servers found|Select any you wish to enable/i;
10
+ const BYPASS_RE = /Yes, I accept/i;
11
+ const BLOCKED_RE = /do you want to proceed/i;
12
+ const READY_RE = /for shortcuts|shift\+tab to cycle|accept edits on|bypass permissions on|plan mode on|auto mode on/i;
13
+ const RUNNING_RE = /esc to interrupt|tokens|Brewed|Thinking|Working|Running/i;
14
+ // Classify a captured pane. Blocking dialogs first (so a stuck pane is never
15
+ // reported "running"), then ready before running, then idle as the fallback.
16
+ export const detectPaneState = (capture) => {
17
+ if (BLOCKED_RE.test(capture))
18
+ return "blocked";
19
+ if (TRUST_RE.test(capture))
20
+ return "trust";
21
+ if (MCP_RE.test(capture))
22
+ return "mcp_approval";
23
+ if (BYPASS_RE.test(capture))
24
+ return "bypass";
25
+ if (READY_RE.test(capture))
26
+ return "ready";
27
+ if (RUNNING_RE.test(capture))
28
+ return "running";
29
+ return "idle";
30
+ };
31
+ // The keystroke groups that clear a blocking dialog, in order. Each group is one
32
+ // send-keys call; callers pause between groups (the bypass warning needs the "2"
33
+ // selection to register before Enter confirms). Non-blocking states return [].
34
+ // trust / blocked -> option 1 (Yes / accept) + Enter
35
+ // mcp_approval -> bare Enter (servers are pre-checked)
36
+ // bypass -> "2" (Yes, I accept), then Enter [default is "1. No, exit"]
37
+ export const clearKeystrokes = (state) => {
38
+ switch (state) {
39
+ case "trust":
40
+ case "blocked":
41
+ return [["1", "Enter"]];
42
+ case "mcp_approval":
43
+ return [["Enter"]];
44
+ case "bypass":
45
+ return [["2"], ["Enter"]];
46
+ default:
47
+ return [];
48
+ }
49
+ };
50
+ // True once a pane has booted past first-run dialogs and is usable (ready or
51
+ // actively running). spawn's bounded clear-loop uses this to know it can stop.
52
+ export const isBooted = (state) => state === "ready" || state === "running";
53
+ // True when the pane is stuck on a dialog that a keystroke can clear.
54
+ export const isBlocking = (state) => clearKeystrokes(state).length > 0;
55
+ //# sourceMappingURL=claude-tui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-tui.js","sourceRoot":"","sources":["../src/claude-tui.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,EAAE;AACF,iFAAiF;AACjF,kFAAkF;AAClF,kFAAkF;AAClF,6EAA6E;AAC7E,qEAAqE;AAwBrE,MAAM,QAAQ,GAAG,+BAA+B,CAAC;AACjD,MAAM,MAAM,GAAG,sDAAsD,CAAC;AACtE,MAAM,SAAS,GAAG,gBAAgB,CAAC;AACnC,MAAM,UAAU,GAAG,yBAAyB,CAAC;AAC7C,MAAM,QAAQ,GACZ,oGAAoG,CAAC;AACvG,MAAM,UAAU,GAAG,0DAA0D,CAAC;AAE9E,6EAA6E;AAC7E,6EAA6E;AAC7E,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAe,EAAa,EAAE;IAC5D,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/C,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAC3C,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,cAAc,CAAC;IAChD,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC7C,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAC3C,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/C,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,iFAAiF;AACjF,iFAAiF;AACjF,+EAA+E;AAC/E,uDAAuD;AACvD,4DAA4D;AAC5D,mFAAmF;AACnF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAgB,EAAuB,EAAE;IACvE,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,OAAO,CAAC;QACb,KAAK,SAAS;YACZ,OAAO,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;QAC1B,KAAK,cAAc;YACjB,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACrB,KAAK,QAAQ;YACX,OAAO,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5B;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,6EAA6E;AAC7E,+EAA+E;AAC/E,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAgB,EAAW,EAAE,CACpD,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,SAAS,CAAC;AAE3C,sEAAsE;AACtE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAgB,EAAW,EAAE,CACtD,eAAe,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ // CLI entry point — debug-only interface for InterComm AIMFP
3
+ import { initDb, closeDb, resolveRoot } from "./db.js";
4
+ import * as store from "./store.js";
5
+ // --- Pure argument parsing ---
6
+ const parseArgs = (argv) => {
7
+ const args = argv.slice(2);
8
+ const command = args[0] ?? "";
9
+ const positional = [];
10
+ const flags = {};
11
+ for (let i = 1; i < args.length; i++) {
12
+ const arg = args[i];
13
+ if (arg.startsWith("--")) {
14
+ const key = arg.slice(2);
15
+ const next = args[i + 1];
16
+ if (next && !next.startsWith("--")) {
17
+ flags[key] = next;
18
+ i++;
19
+ }
20
+ else {
21
+ flags[key] = true;
22
+ }
23
+ }
24
+ else {
25
+ positional.push(arg);
26
+ }
27
+ }
28
+ return { command, positional, flags };
29
+ };
30
+ const getRoot = () => resolveRoot(process.cwd());
31
+ // --- Command handlers ---
32
+ const cmdInit = () => {
33
+ initDb(getRoot());
34
+ console.log("InterComm AIMFP initialized. DB ready.");
35
+ };
36
+ const cmdStatus = () => {
37
+ initDb(getRoot());
38
+ const instances = store.getAllInstances();
39
+ if (instances.length === 0) {
40
+ console.log("No instances registered.");
41
+ return;
42
+ }
43
+ console.log("Instances:");
44
+ for (const inst of instances) {
45
+ console.log(store.formatInstanceForDisplay(inst));
46
+ }
47
+ };
48
+ const cmdRegister = (args) => {
49
+ initDb(getRoot());
50
+ const role = args.positional[0] ?? "worker";
51
+ if (role !== "master" && role !== "worker") {
52
+ console.error("Usage: intercomm register [master|worker]");
53
+ process.exit(1);
54
+ }
55
+ const sessionId = `cli-${Date.now()}`;
56
+ const instance = store.registerAs(role, sessionId);
57
+ console.log(`Registered as "${instance.id}" (${instance.role})`);
58
+ };
59
+ const cmdSend = (args) => {
60
+ initDb(getRoot());
61
+ const from = args.flags["from"];
62
+ const to = args.positional[0];
63
+ const content = args.positional.slice(1).join(" ");
64
+ const type = args.flags["type"] ?? "status";
65
+ if (!from || !to || !content) {
66
+ console.error("Usage: intercomm send --from <id> <to> <message> [--type <type>]");
67
+ process.exit(1);
68
+ }
69
+ store.insertMessage(from, to, type, content);
70
+ store.touchInstance(from);
71
+ console.log(`Sent (${type}) to ${to}`);
72
+ };
73
+ const cmdBroadcast = (args) => {
74
+ initDb(getRoot());
75
+ const from = args.flags["from"];
76
+ const content = args.positional.join(" ");
77
+ const type = args.flags["type"] ?? "announce";
78
+ if (!from || !content) {
79
+ console.error("Usage: intercomm broadcast --from <id> <message> [--type <type>]");
80
+ process.exit(1);
81
+ }
82
+ store.insertMessage(from, "all", type, content);
83
+ store.touchInstance(from);
84
+ console.log(`Broadcast (${type}) to all`);
85
+ };
86
+ const cmdRead = (args) => {
87
+ initDb(getRoot());
88
+ const id = args.flags["id"];
89
+ const readAll = args.flags["all"] === true;
90
+ if (!id) {
91
+ console.error("Usage: intercomm read --id <instance-id> [--all]");
92
+ process.exit(1);
93
+ }
94
+ const messages = store.readNewMessages(id, readAll);
95
+ if (messages.length === 0) {
96
+ console.log("No new messages.");
97
+ return;
98
+ }
99
+ console.log(`--- ${messages.length} message(s) ---`);
100
+ for (const msg of messages) {
101
+ console.log(store.formatMessageForDisplay(msg));
102
+ }
103
+ };
104
+ const cmdClear = (args) => {
105
+ initDb(getRoot());
106
+ const keep = parseInt(args.flags["keep"], 10) || 100;
107
+ const deleted = store.clearOldMessages(keep);
108
+ console.log(`Cleared ${deleted} old messages (kept last ${keep}).`);
109
+ };
110
+ const USAGE = `InterComm AIMFP — Debug CLI
111
+
112
+ Commands:
113
+ init Initialize DB
114
+ status Show all instances
115
+ register [master|worker] Register as master or worker (default: worker)
116
+ send --from <id> <to> <message> [--type <type>] Send a direct message
117
+ broadcast --from <id> <message> [--type <type>] Broadcast to all
118
+ read --id <id> [--all] Read new messages
119
+ clear [--keep <n>] Clear old messages (default: keep 100)
120
+
121
+ Message types: task, status, question, answer, announce, done
122
+ `;
123
+ // --- Main dispatch ---
124
+ const commands = {
125
+ init: cmdInit,
126
+ status: cmdStatus,
127
+ register: cmdRegister,
128
+ send: cmdSend,
129
+ broadcast: cmdBroadcast,
130
+ read: cmdRead,
131
+ clear: cmdClear,
132
+ };
133
+ const main = () => {
134
+ const args = parseArgs(process.argv);
135
+ const handler = commands[args.command];
136
+ if (!handler) {
137
+ console.log(USAGE);
138
+ process.exit(args.command ? 1 : 0);
139
+ }
140
+ handler(args);
141
+ closeDb();
142
+ };
143
+ main();
144
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export declare const STALE_THRESHOLD_MS = 30000;
2
+ export declare const DEFAULT_KEEP = 100;
3
+ export declare const ENV_DB_ROOT = "INTERCOMM_DB_ROOT";
4
+ export declare const basePath: (root: string) => string;
5
+ export declare const dbFile: (root: string) => string;
6
+ export declare const resolveDbRoot: (opts: {
7
+ cwd: string;
8
+ envRoot?: string | undefined;
9
+ gitCommonDir?: string | null | undefined;
10
+ }) => string;
11
+ export declare const worktreesDir: (root: string) => string;
12
+ export declare const worktreePath: (root: string, workerId: string) => string;
13
+ export declare const rootMismatchWarning: (resolvedRoot: string, cwd: string) => string | null;
14
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,kBAAkB,QAAS,CAAC;AACzC,eAAO,MAAM,YAAY,MAAM,CAAC;AAEhC,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAE/C,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,KAAG,MAAmC,CAAC;AAE5E,eAAO,MAAM,MAAM,GAAI,MAAM,MAAM,KAAG,MACF,CAAC;AASrC,eAAO,MAAM,aAAa,GAAI,MAAM;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CAC1C,KAAG,MAKH,CAAC;AAIF,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,MACH,CAAC;AAE1C,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,EAAE,UAAU,MAAM,KAAG,MAC1B,CAAC;AASrC,eAAO,MAAM,mBAAmB,GAC9B,cAAc,MAAM,EACpB,KAAK,MAAM,KACV,MAAM,GAAG,IAOX,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,44 @@
1
+ // Pure functions for paths and constants
2
+ import { dirname, join, resolve } from "node:path";
3
+ const BASE_DIR_NAME = ".intercomm-aimfp";
4
+ const DB_FILE_NAME = "intercomm.db";
5
+ const WORKTREES_DIR_NAME = ".intercomm-worktrees";
6
+ export const STALE_THRESHOLD_MS = 30_000;
7
+ export const DEFAULT_KEEP = 100;
8
+ export const ENV_DB_ROOT = "INTERCOMM_DB_ROOT";
9
+ export const basePath = (root) => join(root, BASE_DIR_NAME);
10
+ export const dbFile = (root) => join(basePath(root), DB_FILE_NAME);
11
+ // Resolve the directory that holds the SHARED intercomm.db. With one worktree
12
+ // per worker, the launch cwd differs per worker, so the bus must be pinned to a
13
+ // single path. Precedence (all inputs supplied by the caller's IO layer):
14
+ // 1. an explicit INTERCOMM_DB_ROOT override (exported by spawn-workers.sh),
15
+ // 2. the parent of the repo's common git dir (same for every linked worktree),
16
+ // 3. the launch cwd (non-git / single-tree fallback).
17
+ // Pure: it only transforms the strings it is given.
18
+ export const resolveDbRoot = (opts) => {
19
+ const env = opts.envRoot?.trim();
20
+ if (env)
21
+ return env;
22
+ if (opts.gitCommonDir)
23
+ return dirname(opts.gitCommonDir);
24
+ return opts.cwd;
25
+ };
26
+ // Default on-disk location for a worker's worktree: a sibling of the repo root
27
+ // (outside the main checkout, so git does not recursively scan it).
28
+ export const worktreesDir = (root) => resolve(root, "..", WORKTREES_DIR_NAME);
29
+ export const worktreePath = (root, workerId) => join(worktreesDir(root), workerId);
30
+ // Warn when the resolved bus root differs from the launch cwd — the signature of a
31
+ // project nested inside ANOTHER git repo: with no INTERCOMM_DB_ROOT pin, resolveDbRoot
32
+ // falls back to the parent of the git-common-dir, which for a nested checkout is the
33
+ // OUTER repo's root, not this project's. That mis-root makes intercomm_spawn(worktrees)
34
+ // create worktrees of the wrong repo (the Run-1 live-test bug). Pure: returns null when
35
+ // root == cwd. The caller suppresses it when INTERCOMM_DB_ROOT was explicitly set
36
+ // (root != cwd is then intentional, e.g. a pinned shared bus).
37
+ export const rootMismatchWarning = (resolvedRoot, cwd) => {
38
+ if (resolve(resolvedRoot) === resolve(cwd))
39
+ return null;
40
+ return (`WARNING: InterComm bus root (${resolvedRoot}) differs from the launch directory (${cwd}). ` +
41
+ `If this project is nested inside another git repository, spawned worktrees may be created from the WRONG repo. ` +
42
+ `Pin INTERCOMM_DB_ROOT to the intended project root, or launch from a standalone (non-nested) checkout before spawning.`);
43
+ };
44
+ //# sourceMappingURL=config.js.map