loom-agent 1.2.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 (79) hide show
  1. package/.env.example +25 -0
  2. package/CHANGELOG.md +402 -0
  3. package/LICENSE +21 -0
  4. package/LOOM.md +235 -0
  5. package/README.md +433 -0
  6. package/bin/loom-tui.js +43 -0
  7. package/bin/loom.js +44 -0
  8. package/docs/acp.md +151 -0
  9. package/docs/web.md +205 -0
  10. package/package.json +97 -0
  11. package/scripts/acp-smoke.js +146 -0
  12. package/src/acp/acp-server.js +287 -0
  13. package/src/config/provider-cmd.js +37 -0
  14. package/src/config/settings.js +164 -0
  15. package/src/core/agents.js +361 -0
  16. package/src/core/background-tasks.js +103 -0
  17. package/src/core/cli.js +579 -0
  18. package/src/core/custom-commands.js +70 -0
  19. package/src/core/errors.js +29 -0
  20. package/src/core/events.js +24 -0
  21. package/src/core/file-diffs.js +282 -0
  22. package/src/core/format.js +206 -0
  23. package/src/core/graph.js +257 -0
  24. package/src/core/hooks.js +82 -0
  25. package/src/core/lsp.js +385 -0
  26. package/src/core/memory.js +87 -0
  27. package/src/core/model-router.js +87 -0
  28. package/src/core/permissions.js +327 -0
  29. package/src/core/platform.js +33 -0
  30. package/src/core/plugin-cmd.js +380 -0
  31. package/src/core/restore.js +207 -0
  32. package/src/core/session-store.js +167 -0
  33. package/src/core/session.js +910 -0
  34. package/src/core/subagent-log.js +134 -0
  35. package/src/core/tokens.js +31 -0
  36. package/src/core/update.js +6 -0
  37. package/src/core/usage.js +166 -0
  38. package/src/index.js +41 -0
  39. package/src/mcp/mcp-client.js +201 -0
  40. package/src/mcp/mcp-manager.js +193 -0
  41. package/src/providers/anthropic.js +243 -0
  42. package/src/providers/google.js +29 -0
  43. package/src/providers/index.js +175 -0
  44. package/src/providers/local.js +27 -0
  45. package/src/providers/nvidia.js +85 -0
  46. package/src/providers/openai-compat.js +269 -0
  47. package/src/providers/openai.js +35 -0
  48. package/src/providers/openrouter.js +43 -0
  49. package/src/providers/registry.js +196 -0
  50. package/src/providers/tokenrouter.js +19 -0
  51. package/src/skills/skill-matcher.js +133 -0
  52. package/src/skills/skills-manager.js +213 -0
  53. package/src/tools/index.js +543 -0
  54. package/src/tui/App.tsx +1578 -0
  55. package/src/tui/components/BreadcrumbBar.tsx +34 -0
  56. package/src/tui/components/ChatArea.tsx +518 -0
  57. package/src/tui/components/InputBar.tsx +354 -0
  58. package/src/tui/components/MdText.tsx +105 -0
  59. package/src/tui/components/Modals.tsx +851 -0
  60. package/src/tui/components/PermissionPopup.tsx +264 -0
  61. package/src/tui/components/Sidebar.tsx +182 -0
  62. package/src/tui/components/SplashScreen.tsx +51 -0
  63. package/src/tui/components/SubagentPanel.tsx +217 -0
  64. package/src/tui/components/ToastOverlay.tsx +34 -0
  65. package/src/tui/keybinds.ts +318 -0
  66. package/src/tui/mcp-presets.ts +189 -0
  67. package/src/tui/md-render.ts +228 -0
  68. package/src/tui/store.ts +714 -0
  69. package/src/tui/suite-home.ts +20 -0
  70. package/src/tui/theme.ts +313 -0
  71. package/src/tui/themes.generated.ts +968 -0
  72. package/src/tui/tool-display.ts +176 -0
  73. package/src/tui/toolname.ts +60 -0
  74. package/src/tui/tui-config.ts +28 -0
  75. package/src/tui-open.tsx +51 -0
  76. package/src/web/attach.js +242 -0
  77. package/src/web/graph-view.html +262 -0
  78. package/src/web/index.html +824 -0
  79. package/src/web/web-server.js +470 -0
package/.env.example ADDED
@@ -0,0 +1,25 @@
1
+ # Loom Code — environment variables for API keys
2
+ # Copy this file to .env and fill in your keys, or set them as env vars.
3
+
4
+ # Anthropic (Claude)
5
+ ANTHROPIC_API_KEY=
6
+
7
+ # OpenAI (GPT-4o, o1, etc.)
8
+ OPENAI_API_KEY=
9
+
10
+ # NVIDIA NIM / Integrate API
11
+ NVIDIA_API_KEY=
12
+ # Optional: override NVIDIA base URL (defaults to https://integrate.api.nvidia.com/v1)
13
+ # NVIDIA_BASE_URL=https://integrate.api.nvidia.com/v1
14
+
15
+ # Google Gemini
16
+ GOOGLE_API_KEY=
17
+
18
+ # OpenRouter (optional)
19
+ OPENROUTER_API_KEY=
20
+
21
+ # Loom Code debug logging
22
+ # LOOM_DEBUG=1
23
+
24
+ # Skip update check on startup
25
+ # LOOM_NO_UPDATE_CHECK=1
package/CHANGELOG.md ADDED
@@ -0,0 +1,402 @@
1
+ # Changelog
2
+
3
+ All notable changes to **Loom Code** are documented here.
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+ - **Data-driven tool display** — the chat renderer no longer checks tool
11
+ *names* anywhere: every tool's icon, pending text, spinner, label,
12
+ block/diff/todos/check/subagent flags and file-vs-bash diff behavior come
13
+ from one display registry (`src/tui/tool-display.ts`), keyed by the tool
14
+ name with a **GenericTool fallback** for anything unregistered (opencode
15
+ and Cline behave the same way — the UI never hardcodes a tool list).
16
+ `src/tui/toolname.ts` keeps only generic formatters. The agent loop no
17
+ longer branches on `name === "write"` / `"bash"` either — it reads the
18
+ registry's `diffs` flag.
19
+ - **Full-chat session restore** — `/sessions` (and `-s <id>`) restores the
20
+ **whole conversation**, not just the text: thinking text, every tool row
21
+ with its saved output, diffs, todos, error/interrupt flags. Older saves
22
+ that only carry `toolCalls` are reconstructed (tool rows rebuilt from the
23
+ calls, results attached), and saving now merges the TUI display parts into
24
+ the session file so the restore is lossless.
25
+ - **Session picker sorted by recency** — `/sessions` now lists sessions by
26
+ their last write time (updatedAt → createdAt → file mtime) instead of
27
+ file name, which had legacy `share-*` exports pinned to the top forever
28
+ and hid the real conversations; legacy files without a timestamp now show
29
+ their file date.
30
+ - **Loop hardening** — the todos listener registers *before* the turn starts
31
+ (a fast first update can't be missed), and a provider that throws
32
+ synchronously now finishes the turn with an error bubble instead of
33
+ leaving the chat stuck on "Thinking" forever.
34
+ - **Companion pets removed** — the ASCII pet, OpenPets desktop sync, and
35
+ `/companion` are gone (declared useless); the sidebar row now shows the
36
+ **Auto-approve** status instead ("on — no asks" / "off — asks per command",
37
+ Shift+Tab toggles). The `@open-pets/client` dependency was dropped.
38
+ - **Tool activity in the chat** — a faithful port of opencode's chat-area
39
+ layout: **every tool call gets its own row** (`→ Read src/a.ts`,
40
+ `← Edit src/b.ts`, `$ git status`, `✱ Glob "**/*.ts"` … — never one merged
41
+ patch for all commands) and rows are packed **tightly**, opencode's
42
+ "always separate" rule: consecutive single-line rows share no gap, only
43
+ the first row after the reasoning/text part and anything around a block
44
+ get one. While running, most tools show the quiet `~ Preparing edit...`
45
+ pending text (deeper indent, no spinner glyph, no accent — opencode only
46
+ spins read/task/execute rows, which show `⠋ Read src/a.ts`). Once done,
47
+ the row flips to a muted `icon label` line that **stays** in the
48
+ transcript. A finished write/edit/bash that actually changed a file
49
+ **replaces its row with the patch** — opencode's Edit renders only its
50
+ BlockTool once the diff exists: a panel on `bgPanelAlt` with a subtle
51
+ left border, titled `← Edit src/b.ts` / `# Wrote {path}`, with the
52
+ `+2 -1` counts and the colored +/- hunks — right where the edit happened,
53
+ then the message continues normally. The assistant answer has **no
54
+ bubble**: reasoning streams as its own full-width part (`⠋ Thinking`
55
+ spinner with the body live while the turn runs, collapsing to a clickable
56
+ `+ Thought · 4.2s` once done), tool rows, then the plain text part. The
57
+ **`# Todos` block** (same BlockTool panel) renders after the last block,
58
+ opencode TodoWrite-style (sidebar keeps its own copy). A `task` subagent
59
+ shows as an opencode InlineTool row — `│ @explore Task` spinning while
60
+ the child runs, flipping to `✓ @explore Task` once done, with `↳ tool
61
+ log` and the streamed body beneath, ending in `↳ finished · done`. Tool
62
+ rows are throttled (first tool of a burst renders immediately, the rest
63
+ ride the 100 ms stream flush) so fast bursts no longer re-render or
64
+ reflow the whole message (that reflow was what made fast output jitter).
65
+ - **Chatbox vibration fixed** — the caret blink now swaps *color*, never
66
+ the character. The cell always renders a full block (`█`); when the blink
67
+ is off its `fg` matches the background behind it (`bgInput`, or `primary`
68
+ inside a selection) so the block is invisible without changing text
69
+ length. TUI renderers commonly trim trailing whitespace per line, which
70
+ made the earlier "space-when-off" caret shorten the last wrapped line at
71
+ blink rate and vibrate the box height while typing; swapping color
72
+ instead means the wrap is identical in both blink phases and the box
73
+ stays smooth while typing, opencode-style.
74
+ - **One-time session permissions prompt** — every new session asks once:
75
+ "Allow all commands in this session?" or "Ask each time" — no more
76
+ per-command prompts slowing the loop. **Shift+Tab** toggles session-wide
77
+ auto-approval on/off any time (a muted `· auto` indicator in the status
78
+ line shows the state; the popup answers it too).
79
+ - **Browser memory graph** — `loom graph open` (or `/graph open`) renders the
80
+ LOOM.md knowledge graph as a force-directed canvas view in the default
81
+ browser: drag nodes, wheel zoom, pan the background, click a node for its
82
+ details, filter by type, and search. The TUI `/graph` modal also now shows
83
+ the `##`/`###` heading hierarchy as a browsable tree (17 nodes, 4 links in
84
+ LOOM.md).
85
+ - **First-run welcome tips** — a small dismissible (✕) card in the sidebar for
86
+ new users only: "Loom supports 180+ providers — /connect to add a key".
87
+ Dismissal persists in `~/.loom/tui.json`, so it never comes back.
88
+ - **Borderless panel UI** — the breadcrumb bar, sidebar, and chatbox no longer
89
+ draw box-drawing borders. Panels separate via background color, so terminal
90
+ text selection (copy) is never snagged on grid lines.
91
+ - **opencode-style layout** — the chatbox now lives in the chat column with a
92
+ breathing gap between it and the sidebar (no shared bottom edge); the
93
+ sidebar is a full-height, locked column with its own internal scroll; the
94
+ chatbox grows to 14 rows and its wrap-width math matches the real box width
95
+ so the text no longer vibrates at wrap boundaries.
96
+ - **Roomier, calmer chatbox** — the resting chatbox is 5 rows tall (splash
97
+ screen included, centered like opencode) and grows smoothly with the draft;
98
+ the mode name ("Build · all tools") sits at the chatbox bottom instead of a
99
+ single-letter "B" header, and the text always starts one row below the top
100
+ edge (the old empty header row collapsed, pushing text against the top).
101
+ The status line below is one clean opencode-style row — cwd · usage
102
+ ("53.7K (27%) · $8.17") · "ctrl+p commands" — instead of three rows of
103
+ clutter. Wrap-width math is exact (integer-snapped to the measured box
104
+ width), so the height never oscillates and the box never vibrates the
105
+ screen while typing; past the limit the draft scrolls instead of hiding
106
+ lines. Pasted drafts past 10 lines render compressed — first 10 lines plus
107
+ a "pasted ~N lines" badge — instead of blowing the box up; any keystroke
108
+ expands the full text back.
109
+ - **Instant /models** — the registry cache was re-parsed (a multi-MB JSON
110
+ file) once per provider on every model-list open, stalling the picker for
111
+ seconds. `loadRegistry()` is now memoized per process (invalidated only
112
+ when a fresh fetch lands): `/models` and the provider picker open
113
+ instantly again.
114
+ - **Fewer permission prompts** — bash now auto-allows normal commands by
115
+ default (dangerous ones — `sudo`, `rm -rf`, `git push --force`, global
116
+ installs … — still ask, defense in depth stays). Edit/task/skill asks are
117
+ unchanged; `/auto` still flips full auto-approve on/off.
118
+ - **Extended thinking on every step (opencode-style)** — when the active
119
+ model supports reasoning, Loom now asks it to think before EVERY reply,
120
+ including after each tool result, so complex tasks get multiple thinking
121
+ passes instead of a single one. Anthropic extended thinking
122
+ (`thinking: enabled`) is enabled for Sonnet 4/Opus 4 (temperature is
123
+ dropped, which the API requires); OpenAI effort models (o1/o3/o4/gpt-5)
124
+ get `reasoning_effort: high`; DeepSeek R1 / Qwen QwQ / Kimi K2 already
125
+ emit reasoning on their own. Each pass streams live into the "+Thought"
126
+ panel. The system prompt now tells the model to re-evaluate after every
127
+ tool result on complex tasks instead of rushing the answer.
128
+ - **The ask tool (questions, not permissions)** — the model can now ask you a
129
+ question with up to 3 answer options (or let you type your own) when it
130
+ genuinely needs input. The permission popup splits into two clear modes:
131
+ permissions (bash/write/…) show only **Allow · Always allow · Deny** — and
132
+ for commands a fourth row, **Allow all commands in this session** (flips
133
+ auto-approve on for the rest of the session, `/auto` to turn it back off) —
134
+ while the ask tool shows the question, its options, and a "Type your
135
+ answer…" row. Typing on a permission popup is now ignored instead of
136
+ opening a bogus answer editor, and the answer you pick or type is fed back
137
+ to the model as the tool result.
138
+ - **Floating dialogs (no more "new window")** — `/models`, `/mcp`,
139
+ `/connectors`, `/settings`, and the rest now render as a compact centered
140
+ panel with no full-screen backdrop, so the chat and any running agent stay
141
+ visible and untouched while you browse — the agent keeps working behind the
142
+ dialog, exactly like opencode's palette.
143
+ - **Guided key entry for connectors** — presets that need a secret
144
+ (Supabase, Railway, Vercel, GitHub, …) no longer dump a raw
145
+ `-e KEY= railway -- npx …` command line into your face. They ask one
146
+ masked "Paste your token" field per secret (URLs/IDs are shown unmasked so
147
+ you can see them) and build the server command automatically. Click-free,
148
+ paste-friendly, and the token never lands in the visible command.
149
+ - **Todos live in the sidebar** — the todo list no longer renders inside the
150
+ chat bubble; the chat uses the full area for the conversation while the
151
+ Todos tab shows live progress, opencode-style.
152
+ - **Memory graph (`/graph`)** — type `/graph` in the TUI to open a full-screen
153
+ interactive view of your project's memory graph. Nodes represent decisions,
154
+ conventions, bugfixes, and notes extracted from LOOM.md (and any
155
+ `.loom/graph/nodes/*.md` files); edges show `[[wikilink]]` relationships.
156
+ The graph auto-generates from your memory files and saves a `graph.json`
157
+ to `.loom/graph/` for fast reload. Navigate with arrow keys, PgUp/PgDn,
158
+ Home/End; press ESC to return to the chat view. In the CLI, `/graph`
159
+ prints a text summary and writes `graph.json` for the TUI to consume.
160
+ - **opencode-scale provider registry (models.dev)** — Loom now supports the
161
+ same 180+ providers as opencode via the models.dev dataset, fetched once and
162
+ cached at `~/.loom/models-dev.json` (7-day freshness). Registry providers
163
+ become OpenAI-compatible runtimes with their real model lists, prices, and
164
+ context windows; `/models` shows every model grouped by provider once the
165
+ provider's key env var is set (`/connect` hints the exact var name).
166
+ - Built-in providers (Anthropic, OpenAI, NVIDIA, Google, OpenRouter, Token
167
+ Router, Local) always win the merge; registry entries without models are
168
+ skipped; `/providers` lists everything.
169
+ - The provider picker fetches the registry on first open and re-opens with
170
+ the full list when a fetch lands mid-session.
171
+ - `/connect <provider>` in the CLI accepts any registry provider (not just
172
+ the built-in five).
173
+ - **Editor integration via the Agent Client Protocol (ACP)** — `loom acp`
174
+ subprocess mode (JSON-RPC over stdio) so Zed, JetBrains, and Neovim
175
+ (Avante.nvim / CodeCompanion.nvim) can drive Loom as their coding agent.
176
+ - `docs/acp.md`: transport + methods reference, Zed/JetBrains/Neovim configs,
177
+ protocol walkthrough, ACP Registry publishing notes.
178
+ - `scripts/acp-smoke.js`: end-to-end self-test client (`npm run smoke:acp`)
179
+ that drives initialize → connect → sendChatRequest → fetchAgentEvent.
180
+ - **Web interface** — `loom web` runs Loom in the browser via a zero-
181
+ dependency Node HTTP server that serves a single-page UI and drives the same
182
+ core `Session` loop as the TUI/ACP. Same providers, tools, MCP servers, and
183
+ saved sessions; chat streams live over Server-Sent Events.
184
+ - The UI is terminal-native and on-brand: warm-charcoal + terracotta palette
185
+ lifted from the TUI themes, monospace-led typography, a **thread rail**
186
+ connecting turns in the transcript, a **status row** (provider · model ·
187
+ budget · live stream rate) as the header, light/dark toggle, escape-first
188
+ markdown renderer with copy buttons, and responsive down to mobile.
189
+ - Flags: `--port`, `--hostname`, `--mdns` / `--mdns-domain` (implies
190
+ `0.0.0.0`), `--cors`, `--no-open`; config-file `server` block; CLI flags
191
+ take precedence.
192
+ - Password auth via `LOOM_SERVER_PASSWORD` / `LOOM_SERVER_USERNAME` (cookie
193
+ `loom_token`); mDNS advertising via the new `bonjour-service` dependency.
194
+ - `loom attach <url>`: line-mode terminal client that shares the running
195
+ web server's sessions/state (the OpenTUI SolidJS app still runs in-process).
196
+ - `src/web/web-server.js`, `src/web/index.html`, `src/web/attach.js`;
197
+ - `src/web/web-server.test.js` (13 tests: options, health, sessions, auth,
198
+ CORS, mDNS) registered in `test:unit`.
199
+ - `docs/web.md`.
200
+ - **Configurable keybinds** — every TUI key is rebindable from
201
+ `~/.loom/tui.json` (`keybinds`, `leader`, `leader_timeout`), opencode-style:
202
+ `<leader>X` prefix, string/array/`{key}`/`"none"`/`false` binding values,
203
+ and opencode-compatible action aliases (`session_interrupt`,
204
+ `prompt.autocomplete.next`, `dialog.select.*`, …).
205
+ - All legacy defaults preserved; `modal_cancel` mirrors
206
+ `session_interrupt` so ESC keeps clearing the input and closing dialogs.
207
+ - `/keybinds` prints every action with its current key; unknown action
208
+ names are reported as warnings.
209
+ - `src/tui/keybinds.ts` (dependency-free engine, `reload()` reads
210
+ `LOOM_CONFIG_DIR`), table-driven dispatch in `App.tsx`, dialogs wired
211
+ through the engine.
212
+ - `src/tui/keybinds.test.ts` (16 tests) registered in `test:unit`;
213
+ interactive suite covers custom palette key, custom leader,
214
+ `session_interrupt` rebind, `modal_cancel` mirror, disabled leader, and
215
+ config restore (61 → 62 tests).
216
+ - `docs/keybinds.md`.
217
+ - **Claude-compatible `/mcp add` one-liner** — add any stdio MCP server with
218
+ `claude mcp add` syntax: `/mcp add [-e KEY=V] <name> [--] <command> [args...]`
219
+ (e.g. `/mcp add stm32 -- "C:\stm32-mcp\.venv\Scripts\python.exe" -m
220
+ stm32_mcp.server`). The `--` separator is optional; slash commands now
221
+ tokenize quoted paths so paths with spaces survive; `-e`/`--env` flags set
222
+ server env vars, and `$KEY` placeholders in args resolve from them.
223
+ - **One-line MCP/connector adding in the TUI** — the four-field add-server
224
+ form is gone. In the `/mcp` and `/connectors` browsers, `A` now opens a
225
+ single-line input using the same `claude mcp add` syntax (leading `add` /
226
+ `mcp add` forgiven); presets open pre-filled with their `-e KEY=` placeholders
227
+ and the caret parked right after the last `=` (←/→/Home/End to move), so
228
+ filling a token is a single typing burst. `mcpAddLineCmd`
229
+ in `src/core/plugin-cmd.js` is the shared engine for both the slash command
230
+ and the modal. `src/mcp/mcp-manager.test.js` (+4 tests) registered in
231
+ `test:unit`.
232
+ - **OpenCode-style permission system**.
233
+ - `config.permission` tree with per-key rules, granular pattern objects
234
+ (last-match-wins, `*`/`?` wildcards, `~`/`$HOME` expansion),
235
+ per-agent overrides via `agent.<id>.permission`, and sane defaults
236
+ (most tools allow; `edit`/`bash`/`task`/`skill` ask; `read` denies
237
+ `*.env`/`*.env.*` except `*.env.example`).
238
+ - Gates every tool call in the session loop (`resolve` → allow/ask/deny),
239
+ plus `external_directory` checks for paths outside the working directory
240
+ and a `doom_loop` guard that flags three identical tool calls in a row.
241
+ - `loom --auto` / `/permissions auto` to auto-approve `ask` results
242
+ (explicit denies still block); muted `auto` indicator in the status row.
243
+ - `src/core/permissions.test.js` (10 tests covering defaults, precedence,
244
+ wildcards, expansions, external_directory, doom_loop, legacy API).
245
+ - **New-file writes are visible** — a `write` (or any tool) that creates a
246
+ brand-new file now renders its own block titled `# Wrote {path}` with the
247
+ fresh content marked `+` (previously new files showed nothing, opencode's
248
+ Edit-only rule made a new file indistinguishable from "nothing happened").
249
+ - **Interleaved parts, opencode-style** — the assistant message is no longer
250
+ a fixed layout ("thinking on top, tools below, answer last"). Every
251
+ reasoning, tool and text event streams as its OWN part in exact arrival
252
+ order, so a turn reads as a flow: `⠋ Thinking` → `→ Read src/a.ts` →
253
+ a *second* `⠋ Thinking` below it → `~ Preparing edit...` → the
254
+ `← Edit src/b.ts` patch → the reply — the model "thinks on the read, then
255
+ decides, then edits, then answers". Each settled reasoning part collapses
256
+ to its own clickable `+ Thought` line (expandable independently), tool
257
+ rows keep one-per-line persistence, `~ Preparing edit...` runs in place
258
+ and flips to the patch block (`← Edit` / `# Wrote`) when the diff lands,
259
+ and text appears where the model actually said it.
260
+ - **Thinking collapsible mid-turn** — the streaming `⠋ Thinking` header is
261
+ clickable while the turn is still running: clicking collapses the live
262
+ body (the header stays), clicking again reopens it; after the turn each
263
+ part still settles to its clickable `+ Thought · Ns` line (opencode
264
+ toggles reasoning at any time, even mid-stream).
265
+ - **Tool-use cap removed** — the 50-iteration `MAX_TOOL_ITERATIONS` ceiling
266
+ (and its "(reached tool limit…)" failure message) is gone; a turn runs
267
+ until the model stops calling tools. Esc-interrupt remains the stop
268
+ switch for runaway loops, and the existing doom-loop guard (three
269
+ identical calls in a row) still asks/denies as configured.
270
+ - **Tool output blocks** — a finished `bash` call with output swaps its row
271
+ for the `$ command` block (opencode's Shell BlockTool), collapsed to the
272
+ first 10 lines with a click-to-expand hint; ANY other tool (MCP, custom)
273
+ renders through the generic fallback as a `# {tool} {args}` block with its
274
+ output (3-line preview). Quiet generic tools without output keep their
275
+ single `⚙ label` row. Completed blocks persist in the transcript.
276
+ - **`/details` toggle** — completed tool parts hide when tool details are
277
+ off (opencode's `shouldHide`): running rows, errors, and pending rows
278
+ always stay. Default is on. The chat re-renders from a filtered message
279
+ stream, so the toggle applies instantly without stale rows.
280
+
281
+ ### Fixed
282
+ - **Chat froze / crashed on later messages** — an orphan raw-text node
283
+ (same-line whitespace between two JSX elements inside a box) made the
284
+ reconciler throw "Orphan text error" on every message append after the
285
+ splash left, killing the renderer and the test suite mid-run.
286
+ - **`/details` never hid tool rows** — the hide rule read the toggle inside
287
+ the chat subtree, which never re-renders on that signal; the filter now
288
+ lives in a memo over the message stream at the app root, the one path
289
+ that provably re-renders the chat.
290
+ - **Splash logo corruption** — the "LOOM CODE" box-drawing logo had been
291
+ mojibaked by an encoding round-trip (scattered letters on the splash
292
+ screen); restored byte-exact from the last clean tree.
293
+ - **`/restore` was slow and dangerous** — every prompt snapshotted the whole
294
+ project tree into `~/.loom/restore.json` (tens of MB), so `/restore` froze
295
+ for seconds parsing it; worse, files too big to snapshot (> 1 MB) were
296
+ recorded as `null` and then **deleted** on restore (silent data loss).
297
+ Snapshots now cap at 200 KB per file / 2 MB per point, uncaptured files are
298
+ recorded as "leave alone" and never touched by a restore, and the store
299
+ self-compacts on load (oldest points dropped until the file fits an 8 MB
300
+ budget). The existing oversized restore file is compacted automatically on
301
+ the first load.
302
+ - **`/sessions` never jumped** — it only printed a text list of session ids.
303
+ It now opens a searchable picker; Enter loads the chosen session into the
304
+ live chat (same code path as `loom -s <id>` resume on start).
305
+
306
+ ### Removed
307
+ - **15 unused slash commands** — `/share`, `/export`, `/undo`, `/redo`,
308
+ `/reset`, `/compact`, `/doctor`, `/diff`, `/debug`, `/editor`, `/fork`,
309
+ `/format`, `/lsp`, `/init`, `/keybinds` are gone from the slash popup and
310
+ the help screen. Kept: build/plan/chat, connect, key, baseurl, model,
311
+ models, providers, status, usage, budget, new, clear, restore, settings,
312
+ sessions, thinking, details, theme, memory, graph, permissions, skills,
313
+ mcp, connectors, help, agents, exit.
314
+
315
+ ## [1.2.0] — 2026-08-12
316
+
317
+ ### Added
318
+ - **agent system** (`/agents`).
319
+ - Three **primary agents** matched to the active mode: `build` (all tools),
320
+ `plan` (read-only + delegation), `chat` (no tools).
321
+ - Three **subagents**: `explore` (read-only codebase search),
322
+ `scout` (external/web research), `general` (autonomous, all tools except
323
+ delegation). Subagents can never call the `task` tool, so delegation always
324
+ terminates (no recursion).
325
+ - **`task` tool** — the primary agent calls it automatically when a subtask
326
+ warrants a focused subagent. Progress streams into a dedicated chat panel
327
+ showing the child's status, tool log, and streamed text.
328
+ - **`@agent` mentions** — prefix a message with `@explore`, `@scout`, etc. to
329
+ delegate the whole turn manually. The mention is stripped from the rendered
330
+ user bubble; type `@` for an autocomplete of available subagents.
331
+ - **Custom agents** — define your own subagents or override/disable built-ins
332
+ via `~/.loom/config.json` → `agents`. Tool patterns use last-match-wins
333
+ semantics (`["*"]`, `["read","glob"]`, `["*","!task"]`, `["mcp__*"]`).
334
+ - `/agents` slash command listing the active registry (id, mode, tools, model).
335
+ - Sidebar Todos tab — markdown checklists (`- [x]`, `[ ]`, `[+]`, `[-]`,
336
+ `1. [~]`) the model writes in its reply now drive the sidebar todo list and
337
+ are clickable to toggle done; reads/writes back to the session todo store.
338
+
339
+ ### Changed
340
+ - Sidebar relayouted for deterministic rendering under the OpenTUI yoga layout:
341
+ info rows, companion, tabs, and content area are now pinned with explicit
342
+ heights and `flexShrink={0}` so the pet's speech-bubble animation no longer
343
+ overlaps or clips the todo/file rows.
344
+ - Companion pet is now a fixed-height block (height 7) so the sidebar content
345
+ area stays stable whether or not the pet is speaking.
346
+ - Interactive test suite hardened with a `waitForFrame` polling helper, fixing
347
+ flaky frame-capture assertions (`child findings`, `finished`, `[free]`,
348
+ `+Thought` toggle).
349
+
350
+ ### Fixed
351
+ - `setSidebarTab` was missing from `Sidebar.tsx` imports — clicking a tab threw
352
+ `ReferenceError`, which cascaded and killed the interactive suite at test 27.
353
+ - OpenTUI `TextNode` crash on nested `<text>` inside `<text>` — todo rows now
354
+ emit a single string with a row color instead of nested styled elements.
355
+ - Sidebar text-garble (`Model:er:`, overlapping todo rows) caused by yoga
356
+ shrinking text nodes to zero width — explicit `width={34}` on todo/file rows
357
+ and explicit `height={1}` on info rows.
358
+
359
+ ### Tests
360
+ - Interactive suite: **58/58 passing** (splash, slash popup, paste, busy-submit
361
+ hold, caret editing, markdown rendering, permission popup, `/budget`,
362
+ `/skills`, `/mcp`, `/connectors`, sidebar todos/files, `/agents`,
363
+ `@explore` delegation, bash diff detection, restore points, model picker,
364
+ MCP seeding, themes, speed hardening, multiline, caret editing, `/clear`).
365
+ - Unit suite: **97 pass / 2 skip / 0 fail** across 8 files, including the new
366
+ `src/core/agents.test.js` (registry shape, tool-pattern filters, config
367
+ merge/disable, `runSubagent` error paths, usage accumulation, agent-turn
368
+ gating).
369
+ - `lint:core` (`tsc -p tsconfig.core.json`) clean — strict JSDoc typing across
370
+ `src/core`, `src/providers`, `src/mcp`, `src/skills`, `src/tools`,
371
+ `src/config`.
372
+
373
+ ### Removed
374
+ - GitLab CI configuration (`.gitlab-ci.yml`) — the project is no longer using
375
+ GitLab CI going forward.
376
+
377
+ ## [1.1.0]
378
+
379
+ - Drag-to-copy selections, paste support, blinking cursor.
380
+ - Companion pet (OpenPets) integration.
381
+ - `/themes`, `/companion`, `/budget` (free/cheap/best/auto), `/skills`,
382
+ `/mcp`, `/connectors` slash commands.
383
+ - One-shot print mode: `loom -p "query"`.
384
+
385
+ ## [1.0.0]
386
+
387
+ - Initial public release.
388
+ - Full OpenTUI interface with slash commands and autocomplete.
389
+ - Build / Plan / Chat modes.
390
+ - Multi-provider support: Anthropic, OpenAI, NVIDIA, Google Gemini, OpenRouter,
391
+ Local (Ollama).
392
+ - Built-in tools: `read`, `write`, `edit`, `bash`, `grep`, `glob`, `webfetch`,
393
+ `todowrite`.
394
+ - Agentic tool loop (multiple tool calls per turn, up to 50 iterations).
395
+ - MCP (Model Context Protocol) server integration.
396
+ - Session memory, `/undo`, `/redo`, `/compact`, `/reset`, `/fork`, `/share`.
397
+ - LOOM.md project memory file.
398
+ - Usage & billing tracking per session + lifetime.
399
+
400
+ [1.2.0]: https://npmjs.com/package/loom-code
401
+ [1.1.0]: https://npmjs.com/package/loom-code
402
+ [1.0.0]: https://npmjs.com/package/loom-code
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Toshal Kumbhar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.