@loompad/cli 0.1.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 (177) hide show
  1. package/ARCHITECTURE.md +211 -0
  2. package/CHANGELOG.md +470 -0
  3. package/LICENSE +21 -0
  4. package/README.md +630 -0
  5. package/dist/adapters/base.d.ts +71 -0
  6. package/dist/adapters/base.js +187 -0
  7. package/dist/adapters/base.js.map +1 -0
  8. package/dist/adapters/bridges/antigravity.d.ts +53 -0
  9. package/dist/adapters/bridges/antigravity.js +106 -0
  10. package/dist/adapters/bridges/antigravity.js.map +1 -0
  11. package/dist/adapters/bridges/cdp.d.ts +82 -0
  12. package/dist/adapters/bridges/cdp.js +222 -0
  13. package/dist/adapters/bridges/cdp.js.map +1 -0
  14. package/dist/adapters/bridges/gui-chat.d.ts +113 -0
  15. package/dist/adapters/bridges/gui-chat.js +498 -0
  16. package/dist/adapters/bridges/gui-chat.js.map +1 -0
  17. package/dist/adapters/bridges/kiro.d.ts +19 -0
  18. package/dist/adapters/bridges/kiro.js +22 -0
  19. package/dist/adapters/bridges/kiro.js.map +1 -0
  20. package/dist/adapters/bridges/profiles.d.ts +82 -0
  21. package/dist/adapters/bridges/profiles.js +125 -0
  22. package/dist/adapters/bridges/profiles.js.map +1 -0
  23. package/dist/adapters/claude-code.d.ts +26 -0
  24. package/dist/adapters/claude-code.js +209 -0
  25. package/dist/adapters/claude-code.js.map +1 -0
  26. package/dist/adapters/codex.d.ts +51 -0
  27. package/dist/adapters/codex.js +291 -0
  28. package/dist/adapters/codex.js.map +1 -0
  29. package/dist/adapters/echo.d.ts +20 -0
  30. package/dist/adapters/echo.js +79 -0
  31. package/dist/adapters/echo.js.map +1 -0
  32. package/dist/adapters/grok.d.ts +61 -0
  33. package/dist/adapters/grok.js +217 -0
  34. package/dist/adapters/grok.js.map +1 -0
  35. package/dist/adapters/index.d.ts +9 -0
  36. package/dist/adapters/index.js +33 -0
  37. package/dist/adapters/index.js.map +1 -0
  38. package/dist/adapters/opencode.d.ts +64 -0
  39. package/dist/adapters/opencode.js +426 -0
  40. package/dist/adapters/opencode.js.map +1 -0
  41. package/dist/cli/common.d.ts +11 -0
  42. package/dist/cli/common.js +40 -0
  43. package/dist/cli/common.js.map +1 -0
  44. package/dist/cli/doctor.d.ts +12 -0
  45. package/dist/cli/doctor.js +181 -0
  46. package/dist/cli/doctor.js.map +1 -0
  47. package/dist/cli/index.d.ts +8 -0
  48. package/dist/cli/index.js +641 -0
  49. package/dist/cli/index.js.map +1 -0
  50. package/dist/cli/tui-model.d.ts +73 -0
  51. package/dist/cli/tui-model.js +312 -0
  52. package/dist/cli/tui-model.js.map +1 -0
  53. package/dist/cli/tui.d.ts +15 -0
  54. package/dist/cli/tui.js +451 -0
  55. package/dist/cli/tui.js.map +1 -0
  56. package/dist/cli/ui.d.ts +10 -0
  57. package/dist/cli/ui.js +135 -0
  58. package/dist/cli/ui.js.map +1 -0
  59. package/dist/core/ades.d.ts +79 -0
  60. package/dist/core/ades.js +130 -0
  61. package/dist/core/ades.js.map +1 -0
  62. package/dist/core/baton.d.ts +37 -0
  63. package/dist/core/baton.js +92 -0
  64. package/dist/core/baton.js.map +1 -0
  65. package/dist/core/brain-extract.d.ts +111 -0
  66. package/dist/core/brain-extract.js +248 -0
  67. package/dist/core/brain-extract.js.map +1 -0
  68. package/dist/core/brain-index.d.ts +128 -0
  69. package/dist/core/brain-index.js +338 -0
  70. package/dist/core/brain-index.js.map +1 -0
  71. package/dist/core/brain.d.ts +214 -0
  72. package/dist/core/brain.js +425 -0
  73. package/dist/core/brain.js.map +1 -0
  74. package/dist/core/claude-cli.d.ts +12 -0
  75. package/dist/core/claude-cli.js +47 -0
  76. package/dist/core/claude-cli.js.map +1 -0
  77. package/dist/core/distill.d.ts +19 -0
  78. package/dist/core/distill.js +88 -0
  79. package/dist/core/distill.js.map +1 -0
  80. package/dist/core/eventlog.d.ts +43 -0
  81. package/dist/core/eventlog.js +211 -0
  82. package/dist/core/eventlog.js.map +1 -0
  83. package/dist/core/git.d.ts +203 -0
  84. package/dist/core/git.js +460 -0
  85. package/dist/core/git.js.map +1 -0
  86. package/dist/core/logbook.d.ts +59 -0
  87. package/dist/core/logbook.js +118 -0
  88. package/dist/core/logbook.js.map +1 -0
  89. package/dist/core/memory.d.ts +34 -0
  90. package/dist/core/memory.js +124 -0
  91. package/dist/core/memory.js.map +1 -0
  92. package/dist/core/notify.d.ts +11 -0
  93. package/dist/core/notify.js +30 -0
  94. package/dist/core/notify.js.map +1 -0
  95. package/dist/core/projection.d.ts +20 -0
  96. package/dist/core/projection.js +96 -0
  97. package/dist/core/projection.js.map +1 -0
  98. package/dist/core/registry.d.ts +76 -0
  99. package/dist/core/registry.js +147 -0
  100. package/dist/core/registry.js.map +1 -0
  101. package/dist/core/router.d.ts +42 -0
  102. package/dist/core/router.js +99 -0
  103. package/dist/core/router.js.map +1 -0
  104. package/dist/core/routes.d.ts +93 -0
  105. package/dist/core/routes.js +399 -0
  106. package/dist/core/routes.js.map +1 -0
  107. package/dist/core/search.d.ts +63 -0
  108. package/dist/core/search.js +0 -0
  109. package/dist/core/search.js.map +1 -0
  110. package/dist/core/setup.d.ts +70 -0
  111. package/dist/core/setup.js +250 -0
  112. package/dist/core/setup.js.map +1 -0
  113. package/dist/core/suggestions.d.ts +15 -0
  114. package/dist/core/suggestions.js +38 -0
  115. package/dist/core/suggestions.js.map +1 -0
  116. package/dist/core/worktree.d.ts +32 -0
  117. package/dist/core/worktree.js +83 -0
  118. package/dist/core/worktree.js.map +1 -0
  119. package/dist/daemon/app-page.d.ts +32 -0
  120. package/dist/daemon/app-page.js +6363 -0
  121. package/dist/daemon/app-page.js.map +1 -0
  122. package/dist/daemon/auth.d.ts +51 -0
  123. package/dist/daemon/auth.js +135 -0
  124. package/dist/daemon/auth.js.map +1 -0
  125. package/dist/daemon/board.d.ts +117 -0
  126. package/dist/daemon/board.js +202 -0
  127. package/dist/daemon/board.js.map +1 -0
  128. package/dist/daemon/brand-icons.d.ts +29 -0
  129. package/dist/daemon/brand-icons.js +44 -0
  130. package/dist/daemon/brand-icons.js.map +1 -0
  131. package/dist/daemon/client.d.ts +127 -0
  132. package/dist/daemon/client.js +237 -0
  133. package/dist/daemon/client.js.map +1 -0
  134. package/dist/daemon/geist-font.d.ts +10 -0
  135. package/dist/daemon/geist-font.js +783 -0
  136. package/dist/daemon/geist-font.js.map +1 -0
  137. package/dist/daemon/guards.d.ts +18 -0
  138. package/dist/daemon/guards.js +40 -0
  139. package/dist/daemon/guards.js.map +1 -0
  140. package/dist/daemon/linear.d.ts +59 -0
  141. package/dist/daemon/linear.js +117 -0
  142. package/dist/daemon/linear.js.map +1 -0
  143. package/dist/daemon/push.d.ts +22 -0
  144. package/dist/daemon/push.js +58 -0
  145. package/dist/daemon/push.js.map +1 -0
  146. package/dist/daemon/runtime.d.ts +299 -0
  147. package/dist/daemon/runtime.js +1016 -0
  148. package/dist/daemon/runtime.js.map +1 -0
  149. package/dist/daemon/server.d.ts +125 -0
  150. package/dist/daemon/server.js +1728 -0
  151. package/dist/daemon/server.js.map +1 -0
  152. package/dist/daemon/tasks.d.ts +153 -0
  153. package/dist/daemon/tasks.js +264 -0
  154. package/dist/daemon/tasks.js.map +1 -0
  155. package/dist/daemon/terminals.d.ts +82 -0
  156. package/dist/daemon/terminals.js +372 -0
  157. package/dist/daemon/terminals.js.map +1 -0
  158. package/dist/index.d.ts +20 -0
  159. package/dist/index.js +20 -0
  160. package/dist/index.js.map +1 -0
  161. package/dist/sdk.d.ts +17 -0
  162. package/dist/sdk.js +17 -0
  163. package/dist/sdk.js.map +1 -0
  164. package/dist/types.d.ts +236 -0
  165. package/dist/types.js +18 -0
  166. package/dist/types.js.map +1 -0
  167. package/docs/SETUP.md +265 -0
  168. package/docs/adapters.md +132 -0
  169. package/docs/design-system.md +245 -0
  170. package/docs/img/board-linear.png +0 -0
  171. package/docs/img/board-projects.png +0 -0
  172. package/docs/img/brain.png +0 -0
  173. package/docs/img/settings.png +0 -0
  174. package/docs/img/workspace.png +0 -0
  175. package/docs/integration-notes.md +76 -0
  176. package/docs/proposals/brain.md +262 -0
  177. package/package.json +84 -0
@@ -0,0 +1,211 @@
1
+ # Loom — Architecture & v1 Design
2
+
3
+ > **The name stuck: Loom.** A loom weaves many threads into one fabric — the agents are
4
+ > threads, shared memory is the weave, the tailnet is a literal mesh. It ships on npm as
5
+ > [`threadloom`](https://www.npmjs.com/package/threadloom) (`loom` was taken).
6
+
7
+ > **Status — read this as the design record, not the current map.** It's the reasoning the
8
+ > project was built from, and the *decisions* below all held. Some of the plan didn't
9
+ > survive contact: the surfaces grew from two to four (a web app and an Electron shell
10
+ > arrived alongside the CLI and phone), the phone app is Android-first over Expo rather
11
+ > than iOS-first over APNs, and the "pre-build spikes" have all been run — see
12
+ > [docs/integration-notes.md](docs/integration-notes.md) for what the agents actually do.
13
+ > Corrections are marked **[shipped]** inline. For what exists today, start at the
14
+ > [README](README.md).
15
+
16
+ ## What it is
17
+
18
+ Loom is a **local-first control plane for coding agents**. One daemon runs on your
19
+ machine. It boots agent backends as background servers, exposes them behind a single
20
+ chat surface, and gives them a **shared brain** so context flows between them. You reach
21
+ it from your laptop CLI or your phone over a private Tailscale mesh, QR-paired.
22
+
23
+ Two headline workflows:
24
+ - **Handoff** — finish with Claude, slide the same context into another agent.
25
+ - **Routing** — Claude plans, another agent executes, Claude reviews.
26
+
27
+ The hard problem Loom solves: these agents have *incompatible* memory, control
28
+ interfaces, and session models, and Loom makes them behave like one organism.
29
+
30
+ ## Glossary
31
+
32
+ | Term | Meaning |
33
+ |---|---|
34
+ | **Daemon** | Long-lived local process. Manages all projects, adapters, the log, networking. |
35
+ | **Project** | A working directory + `.loom/` config + its own event log, baton, and agent fleet. |
36
+ | **Event log** | Per-project append-only source of truth (messages, tool calls, edits, decisions). |
37
+ | **Adapter** | Full-duplex integration with a controllable agent (OpenCode, Claude Code). |
38
+ | **Bridge** | Read-mostly integration with a GUI agent (Antigravity). Never holds the baton. |
39
+ | **Baton** | The write lock. Exactly one agent per project holds it and may edit the tree. |
40
+ | **Projection** | Rendering the log into a target agent's native memory format on handoff. |
41
+ | **Role** | An agent instance's declared function: planner / executor / reviewer / general. |
42
+ | **Surface** | Where you interact. **[shipped]** four of them: TUI/CLI, web app, Electron desktop shell, phone app — every one a paired client of the same daemon. |
43
+
44
+ ## Decisions (locked)
45
+
46
+ | Branch | Decision | Why |
47
+ |---|---|---|
48
+ | Topology | Local-first, per-user daemon; tailnet + QR pairing token | Smallest trust surface; matches BYO-agents ethos |
49
+ | v1 wedge | Unified thread + **manual** routing | Prove "one place for all agents" before auto-orchestration |
50
+ | Integration | Full-duplex **Adapters** + read-only **Bridges** | Keeps the core contract pure without abandoning GUI agents |
51
+ | Memory | Append-only event log = source of truth; **namespaced** projections | One schema to reconcile through; never clobber user config |
52
+ | Surface | One shared thread; agents take turns; baton = write lock | Directly models the handoff story |
53
+ | Capture | **Eager** live streaming into the log | Enables phone mirroring + mid-flight handoff |
54
+ | Files | One shared working tree; active agent holds write lock; interruptible | Simplest correct model for sequential routing |
55
+ | Roles | Declared roles + **suggested** (confirmed) handoffs | Scaffolds plan→execute→review; rails toward auto-routing |
56
+ | Concurrency | Many concurrent projects, **per-project** baton | Async agents across projects is half the value |
57
+ | Async | **Core**: fire-and-notify | The "check from my phone" magic; justifies native push |
58
+ | Product | OSS core + public adapter/bridge SDK | Community writes adapters (like MCP servers proliferated) |
59
+ | Runtime | TypeScript / Node (daemon, CLI, web); React Native (phone app) | One language end-to-end, biggest contributor pool |
60
+
61
+ **[shipped]** The phone app is **Android-first on Expo**, not iOS-first: push goes through
62
+ Expo's service, so Loom manages no APNs/FCM credentials — the same "no accounts, no keys"
63
+ bet the rest of the project makes. The v1 wedge row also under-sold itself: manual routing
64
+ shipped *and* so did LLM auto-routing (`src/core/router.ts`).
65
+
66
+ ## Architecture
67
+
68
+ **[shipped]** — four surfaces, not two; every one a paired client of the same daemon:
69
+
70
+ ```
71
+ phone app ── tailnet ──┐
72
+ web app ──────────────┤ browser, or the Electron shell around it
73
+ laptop CLI/TUI ────────┤
74
+ ▼
75
+ loom daemon
76
+ │
77
+ ┌───────────────┼────────────────┐
78
+ project A project B project C
79
+ ┌────────┐ ┌────────┐ ┌────────┐
80
+ │ log(SoT)│ │ log │ │ log │ ← eager stream capture
81
+ └───┬────┘ └────────┘ └────────┘
82
+ projections (on handoff)
83
+ ┌─────┴──────┬─────────────┐
84
+ [adapter] [adapter] [bridge]
85
+ OpenCode Claude Code Antigravity
86
+ serve/HTTP headless/SDK debug port (read-mostly)
87
+ └── all edit ONE working tree; baton = write lock ──┘
88
+ ```
89
+
90
+ ## Data model — the event log
91
+
92
+ Per-project, append-only, ordered. Suggested store: **SQLite** (one file per project under
93
+ `.loom/`), events as rows + JSON payloads. Event kinds (initial set):
94
+
95
+ - `message` — human or agent turn (role, author agent id, text, ts)
96
+ - `tool_call` / `tool_result` — what an agent did
97
+ - `file_edit` — path, diff/hash (stream messages live; snapshot large diffs)
98
+ - `decision` — a distilled fact worth projecting (drives memory)
99
+ - `handoff` — baton moved from X to Y, with the briefing that was injected
100
+ - `role_change`, `agent_join`, `agent_leave`
101
+
102
+ The log is the **only** source of truth. Everything an agent "knows" that Loom manages is
103
+ a *projection* of this log — never the other way around.
104
+
105
+ ## Adapter contract (full-duplex — mandatory for Adapters)
106
+
107
+ An Adapter MUST implement all of:
108
+
109
+ 1. `send(message)` — deliver a user/handoff turn to the agent
110
+ 2. `stream()` — live event stream (messages, tool calls, edits) → written to the log
111
+ 3. `injectMemory(projection)` — write Loom's namespaced memory block into the agent's
112
+ native store **without touching user-authored files**
113
+ 4. `acquireLock()` / `releaseLock()` — baton / write-lock coordination
114
+ 5. `diff()` — current working-tree changes attributable to this agent
115
+ 6. `interrupt()` — stop/pause the active agent
116
+
117
+ v1 Adapters: **OpenCode** (`serve` HTTP + event API), **Claude Code** (headless / SDK,
118
+ streaming JSON, memory files).
119
+
120
+ ## Bridge contract (read-mostly — for GUI agents)
121
+
122
+ A Bridge implements only a subset and is **explicitly second-class**:
123
+
124
+ - `stream()` (best-effort capture) and `injectMemory()` / briefing receipt.
125
+ - **Never** acquires the baton; **never** edits the shared tree under Loom's lock.
126
+ - Antigravity (VS Code/Windsurf-class fork) attaches here via its debug port until/unless
127
+ it exposes a real API — then it can graduate to an Adapter.
128
+
129
+ ## Handoff & projection
130
+
131
+ 1. You (or a suggested-handoff prompt) pass the baton from X to Y.
132
+ 2. Loom distills the log into Y's **namespaced** memory format (a Loom-managed block —
133
+ e.g. an imported memory file), so it **persists** for Y's later autonomous use.
134
+ 3. Loom also prepends a short "you're picking up ___, current state is ___" briefing for
135
+ Y's immediate turn (cheap, focused).
136
+ 4. Your own `CLAUDE.md` / `AGENTS.md` are never edited.
137
+
138
+ ## Networking & security
139
+
140
+ - Daemon binds **only** to the Tailscale interface. Tailscale provides device auth + E2E
141
+ encryption — that is the trust boundary.
142
+ - **QR pairing**: encodes a short-lived, one-time pairing **token** bound to the node's
143
+ identity — not raw secrets, never in a URL query string. Enrolls the phone as a known
144
+ client.
145
+ - No hosted backend in v1. (Optional relay is a *later, opt-in* product decision, not core.)
146
+
147
+ ## Concurrency & async
148
+
149
+ - Daemon runs **many projects at once**; each has an independent baton and log.
150
+ - Agents are **long-running/background**. When one **needs input** or **finishes**, Loom
151
+ notifies you: a local CLI/desktop notification, and **push to the phone** — **[shipped]**
152
+ through Expo's push service (`src/daemon/push.ts`), not APNs.
153
+ - The primary surface is a **board of projects**, each opening into a shared thread.
154
+
155
+ ## Surfaces
156
+
157
+ **[shipped]** All four exist; none is "later".
158
+
159
+ - **TUI / CLI.** Full experience: create projects, add agents, chat, pass the baton, watch
160
+ streams, get notified.
161
+ - **Web app.** Served by the daemon itself at `/app` (`src/daemon/app-page.ts`) — no build
162
+ step, no CDN. On a wide screen it's the full workspace (thread, tasks, explorer, diffs,
163
+ a real pty terminal); on a phone-sized screen it's the board.
164
+ - **Desktop shell.** A thin first-party Electron window around that same `/app`
165
+ ([`desktop/`](desktop/README.md)) — not an IDE, no editor.
166
+ - **Phone app (React Native / Expo).** Thin client over the tailnet to the daemon's
167
+ WebSocket API. Push for "needs input" / "done". Android-first; iOS needs only a build.
168
+
169
+ ## Open risks / pre-build spikes
170
+
171
+ **[shipped]** These were assumptions to verify before building — all have since been run
172
+ against real, pinned versions; the findings live in
173
+ [docs/integration-notes.md](docs/integration-notes.md). Kept here as the record of what
174
+ was uncertain at the start:
175
+
176
+ 1. **OpenCode `serve`** — confirm it exposes a live event/SSE stream and per-session
177
+ control sufficient for eager capture + interrupt.
178
+ 2. **Claude Code headless** — confirm streaming-JSON event shape, memory-file locations,
179
+ and clean interrupt/cancel.
180
+ 3. **Antigravity debug port** — determine what the port actually exposes (CDP? extension
181
+ host?) and whether even read-only capture is stable. Highest-uncertainty item.
182
+ 4. **Baton correctness** — the write-lock must be airtight; a leaked lock strands a project.
183
+ 5. **Suggested-handoff detection** — start with a simple heuristic (agent signals "plan
184
+ complete"); a model-based classifier is a later refinement.
185
+
186
+ ## v1 build sequence (proposed)
187
+
188
+ **[shipped]** Every step below is built, in roughly this order — with step 10 landing as
189
+ an Expo/Android app rather than iOS/APNs, and a web app plus desktop shell arriving that
190
+ this list never anticipated. See the [CHANGELOG](CHANGELOG.md) for what actually happened.
191
+
192
+ 1. Daemon skeleton + project model + SQLite event log.
193
+ 2. First Adapter (Claude Code headless) — send, stream→log, injectMemory, lock, interrupt.
194
+ 3. CLI: create project, chat one agent, watch stream.
195
+ 4. Second Adapter (OpenCode serve). Now two agents exist.
196
+ 5. Baton + handoff + namespaced projection → the core "shift from A to B" demo.
197
+ 6. Roles + suggested handoffs.
198
+ 7. Multi-project concurrency + background/notify (CLI notifications).
199
+ 8. Tailnet binding + QR pairing token.
200
+ 9. Antigravity **Bridge** (read-only) — the first GUI participant.
201
+ 10. iOS app (v1.5) hitting the same daemon API + APNs push.
202
+ 11. Publish the adapter/bridge SDK + docs (OSS).
203
+
204
+ ## Naming
205
+
206
+ Working name **Loom**. Decide once we see which of {routing, memory, surface} feels like
207
+ the soul in practice — Baton if handoff dominates, Choir if it's the shared-context feel,
208
+ Switchboard if it stays a router. Loom is the safe, brandable default.
209
+
210
+ **[shipped]** Loom it is — the shared-context feel won, which is why the tagline settled on
211
+ *the shared-memory layer*. The npm package is `threadloom`; the command stayed `loom`.