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.
- package/.env.example +25 -0
- package/CHANGELOG.md +402 -0
- package/LICENSE +21 -0
- package/LOOM.md +235 -0
- package/README.md +433 -0
- package/bin/loom-tui.js +43 -0
- package/bin/loom.js +44 -0
- package/docs/acp.md +151 -0
- package/docs/web.md +205 -0
- package/package.json +97 -0
- package/scripts/acp-smoke.js +146 -0
- package/src/acp/acp-server.js +287 -0
- package/src/config/provider-cmd.js +37 -0
- package/src/config/settings.js +164 -0
- package/src/core/agents.js +361 -0
- package/src/core/background-tasks.js +103 -0
- package/src/core/cli.js +579 -0
- package/src/core/custom-commands.js +70 -0
- package/src/core/errors.js +29 -0
- package/src/core/events.js +24 -0
- package/src/core/file-diffs.js +282 -0
- package/src/core/format.js +206 -0
- package/src/core/graph.js +257 -0
- package/src/core/hooks.js +82 -0
- package/src/core/lsp.js +385 -0
- package/src/core/memory.js +87 -0
- package/src/core/model-router.js +87 -0
- package/src/core/permissions.js +327 -0
- package/src/core/platform.js +33 -0
- package/src/core/plugin-cmd.js +380 -0
- package/src/core/restore.js +207 -0
- package/src/core/session-store.js +167 -0
- package/src/core/session.js +910 -0
- package/src/core/subagent-log.js +134 -0
- package/src/core/tokens.js +31 -0
- package/src/core/update.js +6 -0
- package/src/core/usage.js +166 -0
- package/src/index.js +41 -0
- package/src/mcp/mcp-client.js +201 -0
- package/src/mcp/mcp-manager.js +193 -0
- package/src/providers/anthropic.js +243 -0
- package/src/providers/google.js +29 -0
- package/src/providers/index.js +175 -0
- package/src/providers/local.js +27 -0
- package/src/providers/nvidia.js +85 -0
- package/src/providers/openai-compat.js +269 -0
- package/src/providers/openai.js +35 -0
- package/src/providers/openrouter.js +43 -0
- package/src/providers/registry.js +196 -0
- package/src/providers/tokenrouter.js +19 -0
- package/src/skills/skill-matcher.js +133 -0
- package/src/skills/skills-manager.js +213 -0
- package/src/tools/index.js +543 -0
- package/src/tui/App.tsx +1578 -0
- package/src/tui/components/BreadcrumbBar.tsx +34 -0
- package/src/tui/components/ChatArea.tsx +518 -0
- package/src/tui/components/InputBar.tsx +354 -0
- package/src/tui/components/MdText.tsx +105 -0
- package/src/tui/components/Modals.tsx +851 -0
- package/src/tui/components/PermissionPopup.tsx +264 -0
- package/src/tui/components/Sidebar.tsx +182 -0
- package/src/tui/components/SplashScreen.tsx +51 -0
- package/src/tui/components/SubagentPanel.tsx +217 -0
- package/src/tui/components/ToastOverlay.tsx +34 -0
- package/src/tui/keybinds.ts +318 -0
- package/src/tui/mcp-presets.ts +189 -0
- package/src/tui/md-render.ts +228 -0
- package/src/tui/store.ts +714 -0
- package/src/tui/suite-home.ts +20 -0
- package/src/tui/theme.ts +313 -0
- package/src/tui/themes.generated.ts +968 -0
- package/src/tui/tool-display.ts +176 -0
- package/src/tui/toolname.ts +60 -0
- package/src/tui/tui-config.ts +28 -0
- package/src/tui-open.tsx +51 -0
- package/src/web/attach.js +242 -0
- package/src/web/graph-view.html +262 -0
- package/src/web/index.html +824 -0
- package/src/web/web-server.js +470 -0
package/LOOM.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# LOOM.md
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
Loom Code is an AI-powered coding agent for the terminal — multi-provider support (Anthropic, OpenAI, NVIDIA, Google, OpenRouter, Local/Ollama) and a redesigned OpenTUI (SolidJS) terminal interface.
|
|
5
|
+
|
|
6
|
+
## Build / Run Commands
|
|
7
|
+
- `npm install` — install node dependencies (basic REPL)
|
|
8
|
+
- `bun install` — install all deps including OpenTUI native modules
|
|
9
|
+
- `node src/index.js` — run basic line-mode REPL
|
|
10
|
+
- `bun run src/tui-open.tsx` — launch the new OpenTUI TUI for this project
|
|
11
|
+
- `bun src/tui-open.tsx -p "query"` — one-shot/headless mode
|
|
12
|
+
- `npm run tui` — alias for `bun run src/tui-open.tsx`
|
|
13
|
+
- `npm start` — alias for `node src/index.js`
|
|
14
|
+
- `bun build src/tui-open.tsx --outdir=dist --target=bun` — bundle the TUI
|
|
15
|
+
|
|
16
|
+
## Test Commands
|
|
17
|
+
- Manual TUI render test: `bun run src/tui-open.tsx` (visual check)
|
|
18
|
+
- Interactive render test: `bun run src/tui/test-interactive.tsx` — verifies splash, slash popup, filtering, Enter execute, modals (asserts included)
|
|
19
|
+
- Headless smoke (no TTY): pipe into `bun src/tui-open.tsx -p "hi"` — requires a valid provider API key
|
|
20
|
+
- No unit test framework configured yet; add `bun test` tests under `src/**/*.test.tsx` using `testRender` from `@opentui/solid`
|
|
21
|
+
|
|
22
|
+
## Code Style
|
|
23
|
+
- CommonJS (`.js`) for legacy core (session, providers, tools, mcp, config, cli)
|
|
24
|
+
- ES modules + TypeScript (`.ts`, `.tsx`) for the OpenTUI layer — `src/tui/**`, `src/tui-open.tsx`
|
|
25
|
+
- SolidJS signals for state; `@opentui/solid` hooks (`useKeyboard`, `useRenderer`, `useTerminalDimensions`)
|
|
26
|
+
- OpenTUI JSX intrinsics use kebab/underscore names per reconciliation rules:
|
|
27
|
+
- `<text>`, `<box>`, `<scrollbox>` — layout/display
|
|
28
|
+
- `<input>`, `<textarea>`, `<select>`, `<tab_select>` — inputs (underscore for `tab_select`)
|
|
29
|
+
- Text styling uses **nested** modifier tags: `<strong>`, `<em>`, `<u>`, `<span fg="...">` — NOT props
|
|
30
|
+
- Colors via palette helpers in `src/tui/theme.ts` (hex strings)
|
|
31
|
+
|
|
32
|
+
## Architecture — TUI (new, OpenTUI/SolidJS)
|
|
33
|
+
```
|
|
34
|
+
src/tui-open.tsx Entry point (bun) — render(<App/>)
|
|
35
|
+
src/tui/
|
|
36
|
+
App.tsx Root component — layout, keyboard routing, session submit loop, slash commands (command errors surface as toasts, not chat messages)
|
|
37
|
+
store.ts SolidJS signals: messages, input, thinking, modal, suggestions, provider state, todos, pets
|
|
38
|
+
theme.ts Color palette (loom dark/light), LOOM banner ASCII, VERSION
|
|
39
|
+
components/
|
|
40
|
+
SplashScreen.tsx Full-screen banner + input prompt (shown when no messages)
|
|
41
|
+
ChatArea.tsx Message list (user/assistant/tool/system bubbles, thinking indicator) — assistant bubbles render on a slightly darker patch; while the model edits files the view splits: chat left, diff panel right
|
|
42
|
+
Sidebar.tsx Right-hand panel: provider, model, tabs (Info/Todos/Files), companion pet
|
|
43
|
+
InputBar.tsx Bottom input with slash autocomplete (mouse click/wheel) + status line
|
|
44
|
+
Modals.tsx Provider picker, model picker, key input, base-URL editor, confirm dialogs (mouse click/wheel)
|
|
45
|
+
companion/
|
|
46
|
+
openpets.ts Optional OpenPets desktop app bridge (IPC via @open-pets/client)
|
|
47
|
+
(Companion.tsx lives in components/ — animated pet with blink frames, click reactions, speech bubbles)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The old `src/tui.js` (ink) has been removed. `src/core/cli.js` now spawns `bun src/tui-open.tsx` when TTY + bun available, else falls back to the basic readline REPL.
|
|
51
|
+
|
|
52
|
+
## Architecture — Core (unchanged)
|
|
53
|
+
```
|
|
54
|
+
src/providers/ anthropic.js, openai.js, nvidia.js, google.js, openrouter.js, local.js
|
|
55
|
+
All "OpenAI-compat" providers delegate to providers/openai-compat.js
|
|
56
|
+
Base URLs resolved via config/settings.getBaseUrl(provider)
|
|
57
|
+
Env overrides: ANTHROPIC_BASE_URL, OPENAI_BASE_URL, NVIDIA_BASE_URL, GOOGLE_BASE_URL, OPENROUTER_BASE_URL
|
|
58
|
+
|
|
59
|
+
src/core/session.js Session class — agentic tool loop (up to 50 iterations)
|
|
60
|
+
src/core/model-router.js Budget router — free/cheap/best/auto level → per-turn model pick
|
|
61
|
+
src/core/usage.js Usage & billing ledger + monthly spend governor (budgetStatus / setMonthlyBudget)
|
|
62
|
+
src/core/events.js Sync event bus (turn lifecycle, model:switch, tool calls)
|
|
63
|
+
src/core/session-store.js Save/load conversations in ~/.loom/sessions
|
|
64
|
+
src/core/permissions.js bash/edit/write permission gating
|
|
65
|
+
src/core/plugin-cmd.js /skills, /mcp, /diff, /debug, /editor, /export, /sessions, /fork handlers
|
|
66
|
+
src/config/settings.js ~/.loom/config.json persistence, API key + base URL helpers
|
|
67
|
+
src/tools/index.js read/write/edit/bash/grep/glob/webfetch/todowrite + MCP passthrough
|
|
68
|
+
src/mcp/ MCP client + manager (stdio servers)
|
|
69
|
+
src/skills/ Skill install/remove/list from ~/.loom/skills
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Companion Pet System
|
|
73
|
+
- **5 built-in pets**: Cat, Robot, Fenrir (wolf), Luma (firefly), OpenPets (desktop sync)
|
|
74
|
+
- **Animation**: 700ms blink interval, idle/thinking/working/happy/sleep poses, random phrases
|
|
75
|
+
- **Interaction**: Click pet → celebrating mood + hearts counter + speech bubble
|
|
76
|
+
- **Event-driven moods**: `notifyPet({ mood: "working"|"success"|"error"|"celebrating" ... })` called from chat lifecycle
|
|
77
|
+
- **OpenPets desktop sync** (optional): `@open-pets/client` IPC bridge at `src/tui/companion/openpets.ts`
|
|
78
|
+
- Reactions: idle, thinking, working, editing, running, testing, waiting, waving, success, error, celebrating
|
|
79
|
+
- Auto-discovers desktop app via `%APPDATA%\OpenPets\runtime\ipc.json` (Win) or `~/.config/OpenPets/runtime/ipc.json` (Linux)
|
|
80
|
+
- Graceful no-op when desktop app not running
|
|
81
|
+
- `/companion` opens picker modal; selection persisted to `~/.loom/tui.json`
|
|
82
|
+
- Settings `[e]` toggles OpenPets sync on/off
|
|
83
|
+
|
|
84
|
+
## Slash Commands (39 total)
|
|
85
|
+
| Command | Args | Description |
|
|
86
|
+
|---------|------|-------------|
|
|
87
|
+
| `/help` | — | Show this help dialog |
|
|
88
|
+
| `/build` | — | Build mode — full agent tools (all tools, executes changes) |
|
|
89
|
+
| `/plan` | — | Plan mode — read-only analysis; model gets only read/glob/grep/webfetch/todowrite, mutations blocked by an execute guard; ends with "Plan complete — Tab to Build, send 'go'" |
|
|
90
|
+
| `/chat` | — | Chat mode — conversation only, zero tools |
|
|
91
|
+
| `/connect` | `[provider]` | Add/connect a provider (opens picker if no arg) |
|
|
92
|
+
| `/key` | — | Edit API key for current provider |
|
|
93
|
+
| `/baseurl` | `[provider] [url]` | Set provider base URL |
|
|
94
|
+
| `/model` | `[model-id]` | Pick active model (or open picker) |
|
|
95
|
+
| `/models` | — | List available models (grouped by provider) |
|
|
96
|
+
| `/providers` | — | List supported providers |
|
|
97
|
+
| `/status` | — | Show connection status (provider, model, key) |
|
|
98
|
+
| `/usage` | — | Show token usage and billing breakdown (session + lifetime + monthly budget) |
|
|
99
|
+
| `/budget` | `[level \| $]` | Budget routing — `free`/`cheap`/`best`/`auto`, or a dollar cap (`/budget 50`). Over-cap blocks paid turns (`/budget free` is the escape hatch) |
|
|
100
|
+
| `/new` | — | Start a new session |
|
|
101
|
+
| `/clear` | — | Clear the chat |
|
|
102
|
+
| `/compact` | — | Compact conversation (keep last 10) |
|
|
103
|
+
| `/restore` | — | Restore a snapshot point |
|
|
104
|
+
| `/undo` | — | Undo last exchange |
|
|
105
|
+
| `/redo` | — | Redo last undone exchange |
|
|
106
|
+
| `/reset` | — | Reset the session |
|
|
107
|
+
| `/settings` | — | Toggle details/thinking/sidebar/OpenPets |
|
|
108
|
+
| `/sessions` | — | Browse saved sessions |
|
|
109
|
+
| `/share` | — | Export the current session to JSON |
|
|
110
|
+
| `/export` | — | Export to markdown |
|
|
111
|
+
| `/thinking` | — | Toggle thinking visibility |
|
|
112
|
+
| `/details` | — | Toggle tool detail visibility |
|
|
113
|
+
| `/theme` | — | Theme picker (live switch + persist) |
|
|
114
|
+
| `/permissions` | — | Saved permission rules (view / reset) |
|
|
115
|
+
| `/editor` | — | Open external editor (LOOM.md) |
|
|
116
|
+
| `/diff` | — | Show git diff |
|
|
117
|
+
| `/init` | — | Create LOOM.md |
|
|
118
|
+
| `/memory` | — | Show memory file locations |
|
|
119
|
+
| `/doctor` | — | Run diagnostics |
|
|
120
|
+
| `/skills` | `install <dir\|git> \| remove <name>` | Manage skills (`/skills install <path-or-url> [name]`, `/skills remove <name>`, `/skills help`) |
|
|
121
|
+
| `/mcp` | `add <name> <cmd> \| remove \| toggle` | Manage MCP servers (`/mcp add <name> <command> [args...]`, `/mcp remove <name>`, `/mcp toggle <name>`, `/mcp help`) |
|
|
122
|
+
| `/debug` | — | Show debug info |
|
|
123
|
+
| `/fork` | — | Fork conversation |
|
|
124
|
+
| `/companion` | — | Change your companion pet |
|
|
125
|
+
| `/exit` | — | Quit Loom Code |
|
|
126
|
+
|
|
127
|
+
### Leader Key (Ctrl+X)
|
|
128
|
+
| Key | Slash Command |
|
|
129
|
+
|-----|---------------|
|
|
130
|
+
| `c` | `/compact` |
|
|
131
|
+
| `e` | `/editor` |
|
|
132
|
+
| `q` | `/exit` |
|
|
133
|
+
| `x` | `/export` |
|
|
134
|
+
| `h` | `/help` |
|
|
135
|
+
| `m` | `/models` |
|
|
136
|
+
| `n` | `/new` |
|
|
137
|
+
| `r` | `/redo` |
|
|
138
|
+
| `l` | `/sessions` |
|
|
139
|
+
| `u` | `/undo` |
|
|
140
|
+
| `s` | `/settings` |
|
|
141
|
+
| `t` | `/thinking` |
|
|
142
|
+
| `d` | `/details` |
|
|
143
|
+
| `b` | `/build` |
|
|
144
|
+
| `p` | `/plan` |
|
|
145
|
+
|
|
146
|
+
### Autocomplete
|
|
147
|
+
- **Slash (`/`)** — live-filtered commands with descriptions, 10-row scrollable window over the full list, UP/DOWN/TAB/ENTER/ESC
|
|
148
|
+
- **At (`@`)** — fuzzy file search (project files, top 10)
|
|
149
|
+
- **Bang (`!`)** — shell presets (`!ls -la`, `!git status`, `!git diff`, `!pwd`)
|
|
150
|
+
- **Mouse** — click a suggestion row to select + execute it; scroll wheel scrolls through the full list and moves the selection (also in modals: provider/model/companion pickers + palette — model picker shows "showing N-M of X" range)
|
|
151
|
+
|
|
152
|
+
### Keyboard Shortcuts
|
|
153
|
+
| Keys | Action |
|
|
154
|
+
|------|--------|
|
|
155
|
+
| `Enter` | Submit prompt / pick autocomplete |
|
|
156
|
+
| `Esc` | Interrupt thinking / close modal / clear input |
|
|
157
|
+
| `Ctrl+C` | Exit TUI (clean terminal restore) |
|
|
158
|
+
| `Ctrl+B` | Toggle sidebar |
|
|
159
|
+
| `Ctrl+P` | Command palette (type to filter) |
|
|
160
|
+
| `Ctrl+X` | Leader prefix (3s timeout) |
|
|
161
|
+
| `Ctrl+I` | Cycle sidebar tab (Info/Todos/Files) |
|
|
162
|
+
| `Tab` | Cycle autocomplete / cycle input mode (Build/Plan/Chat) |
|
|
163
|
+
| `Up/Down` | Navigate autocomplete / suggestion list |
|
|
164
|
+
| `Mouse wheel` | Scroll chat history (scrollbox) / move suggestion + modal selection |
|
|
165
|
+
| `Mouse click` | Run a suggestion or modal option (autocomplete, pickers, palette) |
|
|
166
|
+
|
|
167
|
+
## Configuration Format
|
|
168
|
+
`~/.loom/config.json`:
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"provider": "nvidia",
|
|
172
|
+
"model": { "nvidia": "deepseek-ai/deepseek-v4-flash", "anthropic": "claude-sonnet-4-20250514" },
|
|
173
|
+
"apiKeys": { "nvidia": "..." },
|
|
174
|
+
"baseUrls": { "nvidia": "https://integrate.api.nvidia.com/v1" },
|
|
175
|
+
"maxTokens": 8192,
|
|
176
|
+
"temperature": 0.7
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## TUI State Persistence
|
|
181
|
+
`~/.loom/tui.json`:
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"sidebarVisible": true,
|
|
185
|
+
"showToolDetails": false,
|
|
186
|
+
"showThinking": true,
|
|
187
|
+
"companion": "cat",
|
|
188
|
+
"openPetsSync": false,
|
|
189
|
+
"petEnabled": true
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Native Modules Note
|
|
194
|
+
`@opentui/core` ships per-platform native packages (`@opentui/core-win32-x64`, `-darwin-arm64`, etc.).
|
|
195
|
+
Install the one matching the host OS — npm/bun install will pull them automatically when flagged in
|
|
196
|
+
`optionalDependencies`. `bun build` may warn about missing *other* platforms; that is expected.
|
|
197
|
+
|
|
198
|
+
## Notes for the Coding Agent
|
|
199
|
+
- Always use `bun run src/tui-open.tsx` to drive the UI; never edit the old `tui.js` (gone)
|
|
200
|
+
- When adding a new slash command:
|
|
201
|
+
1. Register in `src/tui/store.ts` (`SLASH_LIST`)
|
|
202
|
+
2. Add a `case "cmdname":` block in `src/tui/App.tsx` (`processSlash`)
|
|
203
|
+
3. If it opens a picker, add a modal handler in `src/tui/components/Modals.tsx`
|
|
204
|
+
- To add a provider: drop a file in `src/providers/`, register in `providers/index.js` (`PROVIDERS`, `PROVIDER_ORDER`, `PROVIDER_LABELS`), add its base URL default to `config/settings.js`
|
|
205
|
+
- Run deterministic checks with `bun -e "..."` or `bun build` before pushing UI changes
|
|
206
|
+
- **Critical**: All signal reads MUST be inside JSX expressions (`{signal()}`) or `<Show when={signal()}>` — top-level `const s = signal()` captures once and never updates
|
|
207
|
+
|
|
208
|
+
## SolidJS Reactivity Pattern (MUST FOLLOW)
|
|
209
|
+
```tsx
|
|
210
|
+
// WRONG — captures once, never updates
|
|
211
|
+
const s = suggestions();
|
|
212
|
+
|
|
213
|
+
// CORRECT — read inside JSX
|
|
214
|
+
<Show when={suggestions().length > 0 && autoKind() === "slash"}>
|
|
215
|
+
{suggestions().slice(0, 4).map(...)}
|
|
216
|
+
</Show>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
This pattern applies to ALL components: InputBar, Companion, Sidebar, BreadcrumbBar, ChatArea, Modals.
|
|
220
|
+
|
|
221
|
+
### Passing props to children
|
|
222
|
+
Pass **accessor functions**, not resolved values, for reactive props. Component bodies
|
|
223
|
+
run once (untracked), so `const msgs = props.messages` in a child body captures a stale value.
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
// WRONG — child body reads props.messages once, never updates
|
|
227
|
+
<ChatArea messages={messages()} />
|
|
228
|
+
|
|
229
|
+
// CORRECT — child calls the accessor inside JSX
|
|
230
|
+
<ChatArea messages={messages} />
|
|
231
|
+
// in ChatArea: const visible = () => (props.messages?.() || []).slice(-40); ... {visible().map(...)}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Boolean/static props may stay as values (`show={sidebarVisible()}` is fine when read inside
|
|
235
|
+
`<Show when={...}>` or another JSX expression).
|
package/README.md
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
# Loom Code
|
|
2
|
+
|
|
3
|
+
[](https://npmjs.com/loom-code)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](#)
|
|
6
|
+
|
|
7
|
+
An AI-powered coding agent for the terminal with multi-provider support and a full terminal UI.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Full OpenTUI interface with slash commands, autocomplete, and ESC-to-interrupt
|
|
12
|
+
- Build / Plan / Chat modes
|
|
13
|
+
- OpenCode-style agents — primary agents (`build`/`plan`/`chat`) plus delegating subagents (`explore`, `scout`, `general`) invoked automatically via the `task` tool or manually via `@agent` mentions
|
|
14
|
+
- LOOM.md project memory file
|
|
15
|
+
- Multi-provider: Anthropic, OpenAI, NVIDIA, Google Gemini, OpenRouter, Local (Ollama)
|
|
16
|
+
- Built-in tools: read, write, edit, bash, grep, glob, webfetch, todowrite
|
|
17
|
+
- Agentic tool loop (multiple tool calls per turn)
|
|
18
|
+
- MCP (Model Context Protocol) server integration
|
|
19
|
+
- Drag-to-copy selections, paste support
|
|
20
|
+
- Session memory, /undo, /redo, /compact, /reset, /fork
|
|
21
|
+
- One-shot print mode: `loom -p "query"`
|
|
22
|
+
- Editor integration via the Agent Client Protocol (ACP): `loom acp` (JSON-RPC over stdio) so Zed, JetBrains, and Neovim plugins can drive Loom as their coding agent
|
|
23
|
+
- Browser interface via `loom web` — a zero-dependency Node HTTP server (chat in the browser, password auth, CORS) plus `loom attach` to share the same server from the terminal; optional mDNS advertising as `loom.local` via [bonjour-service](https://www.npmjs.com/package/bonjour-service)
|
|
24
|
+
- Usage & billing tracking per session + lifetime
|
|
25
|
+
|
|
26
|
+
## Prerequisites
|
|
27
|
+
|
|
28
|
+
- **[bun](https://bun.sh) >= 1.0** (required for the full TUI). Without bun, only the line-mode REPL runs.
|
|
29
|
+
- **Node.js >= 18** (for non-TUI mode and package scripts)
|
|
30
|
+
- **An API key** from at least one provider:
|
|
31
|
+
|
|
32
|
+
| Provider | Sign-up | Key type |
|
|
33
|
+
|----------|---------|----------|
|
|
34
|
+
| Anthropic | [console.anthropic.com](https://console.anthropic.com) | Claude API key |
|
|
35
|
+
| OpenAI | [platform.openai.com](https://platform.openai.com) | API key |
|
|
36
|
+
| NVIDIA | [build.nvidia.com](https://build.nvidia.com) | NIM API key (free tier available) |
|
|
37
|
+
| Google | [aistudio.google.com](https://aistudio.google.com) | Gemini API key (free tier available) |
|
|
38
|
+
| OpenRouter | [openrouter.ai](https://openrouter.ai) | OpenRouter API key |
|
|
39
|
+
| Local | Install [Ollama](https://ollama.com) | No key needed (runs locally) |
|
|
40
|
+
|
|
41
|
+
Install from npm (global — installs the `loom` command):
|
|
42
|
+
```bash
|
|
43
|
+
npm install -g loom-code
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Or run from source:
|
|
47
|
+
```bash
|
|
48
|
+
npm install -g .
|
|
49
|
+
# or
|
|
50
|
+
npm link
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Setup
|
|
54
|
+
|
|
55
|
+
1. **Install bun** (for the full TUI):
|
|
56
|
+
```bash
|
|
57
|
+
# macOS / Linux
|
|
58
|
+
curl -fsSL https://bun.sh/install | bash
|
|
59
|
+
|
|
60
|
+
# Windows
|
|
61
|
+
powershell -c "irm bun.sh/install.ps1 | iex"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
2. **Configure API keys** — pick one method:
|
|
65
|
+
```bash
|
|
66
|
+
# a) Environment variables (.env)
|
|
67
|
+
cp .env.example .env
|
|
68
|
+
# Edit .env with your key:
|
|
69
|
+
# ANTHROPIC_API_KEY=sk-ant-...
|
|
70
|
+
# NVIDIA_API_KEY=nvapi-...
|
|
71
|
+
# OPENAI_API_KEY=sk-...
|
|
72
|
+
|
|
73
|
+
# b) Interactive setup in the TUI
|
|
74
|
+
loom
|
|
75
|
+
> /connect nvidia
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
3. **Initialize project memory** (optional):
|
|
79
|
+
```bash
|
|
80
|
+
loom
|
|
81
|
+
> /init
|
|
82
|
+
```
|
|
83
|
+
Creates `LOOM.md` with a project-specific template.
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
loom # interactive TUI session
|
|
89
|
+
loom "explain this project" # start with initial prompt
|
|
90
|
+
loom -p "list files in src/" # one-shot, print result, exit
|
|
91
|
+
cat logs.txt | loom -p "explain" # analyze piped content
|
|
92
|
+
loom --version
|
|
93
|
+
loom --help
|
|
94
|
+
loom --basic # skip TUI, use line-mode REPL
|
|
95
|
+
loom acp # ACP subprocess mode for editor integrations
|
|
96
|
+
loom web # browser interface (HTTP server, opens browser)
|
|
97
|
+
loom attach http://localhost:4096 # attach the terminal to a running loom web
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Web
|
|
101
|
+
|
|
102
|
+
Loom runs in your browser with `loom web` — a zero-dependency HTTP server that
|
|
103
|
+
serves a single-page UI and drives the same core session loop as the TUI. Same
|
|
104
|
+
providers/models, tools, MCP servers, and saved sessions; chat streams live in
|
|
105
|
+
the browser, and a shared terminal client can attach to the same server.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
loom web # 127.0.0.1, random port, opens the browser
|
|
109
|
+
loom web --port 4096 # fixed port
|
|
110
|
+
loom web --hostname 0.0.0.0 # reachable on the LAN
|
|
111
|
+
loom web --mdns # advertise as loom.local (implies 0.0.0.0)
|
|
112
|
+
loom web --mdns-domain proj.local # custom mDNS domain
|
|
113
|
+
loom web --cors https://example.com
|
|
114
|
+
LOOM_SERVER_PASSWORD=secret loom web # password-protect (user: LOOM_SERVER_USERNAME, default "loom")
|
|
115
|
+
|
|
116
|
+
# Attach the terminal (shares the server's sessions/state):
|
|
117
|
+
loom attach http://localhost:4096
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Config-file equivalent (`~/.loom/config.json`): `{ "server": { "port": 4096,
|
|
121
|
+
"hostname": "0.0.0.0", "mdns": true, "cors": ["https://example.com"] } }` — CLI
|
|
122
|
+
flags take precedence. Full flags, the JSON API, and `loom attach` options are
|
|
123
|
+
documented in **[docs/web.md](docs/web.md)**.
|
|
124
|
+
|
|
125
|
+
## Editor integration (ACP)
|
|
126
|
+
|
|
127
|
+
Loom implements the [Agent Client Protocol (ACP)](https://agentclientprotocol.com)
|
|
128
|
+
as `loom acp` — a JSON-RPC-over-stdio subprocess server that any ACP-compatible
|
|
129
|
+
editor can launch, the same mechanism opencode uses with Zed. Zed, JetBrains,
|
|
130
|
+
and Neovim (Avante.nvim / CodeCompanion.nvim) all support ACP agents; you only
|
|
131
|
+
configure the agent command `loom acp`, then chat from the editor while Loom
|
|
132
|
+
runs real tools in your repo.
|
|
133
|
+
|
|
134
|
+
Zed (add to `~/.config/zed/settings.json`, then Command Palette → `agent: new thread`):
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"agent_servers": {
|
|
139
|
+
"Loom Code": { "type": "custom", "command": "loom", "args": ["acp"], "env": {} }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Full transport spec, JetBrains/Neovim configs, the protocol walkthrough, and a
|
|
145
|
+
self-test client (`node scripts/acp-smoke.js`) live in **[docs/acp.md](docs/acp.md)**.
|
|
146
|
+
|
|
147
|
+
## Slash Commands (35 total)
|
|
148
|
+
|
|
149
|
+
| Command | Args | Description |
|
|
150
|
+
|---------|------|-------------|
|
|
151
|
+
| `/help` | — | Show all commands |
|
|
152
|
+
| `/build` | — | Build mode — all tools |
|
|
153
|
+
| `/plan` | — | Plan mode — read-only analysis |
|
|
154
|
+
| `/chat` | — | Chat mode — no tools |
|
|
155
|
+
| `/agents` | — | List primary agents and subagents |
|
|
156
|
+
| `/connect` | `[provider]` | Add/connect a provider |
|
|
157
|
+
| `/key` | — | Edit API key for current provider |
|
|
158
|
+
| `/baseurl` | `[provider] [url]` | Set provider base URL |
|
|
159
|
+
| `/model` | `[model-id]` | Pick active model |
|
|
160
|
+
| `/models` | — | List available models |
|
|
161
|
+
| `/providers` | — | List supported providers |
|
|
162
|
+
| `/status` | — | Show connection status |
|
|
163
|
+
| `/usage` | — | Show token usage and billing |
|
|
164
|
+
| `/new` | — | Start a new session |
|
|
165
|
+
| `/clear` | — | Clear the chat |
|
|
166
|
+
| `/compact` | — | Compact conversation |
|
|
167
|
+
| `/undo` | — | Undo last exchange |
|
|
168
|
+
| `/redo` | — | Redo last undone exchange |
|
|
169
|
+
| `/reset` | — | Reset the session |
|
|
170
|
+
| `/settings` | — | Toggle details/sidebar/etc. |
|
|
171
|
+
| `/sessions` | — | Browse saved sessions |
|
|
172
|
+
| `/share` | — | Export current session to JSON |
|
|
173
|
+
| `/export` | — | Export to markdown |
|
|
174
|
+
| `/thinking` | — | Toggle thinking visibility |
|
|
175
|
+
| `/details` | — | Toggle tool detail visibility |
|
|
176
|
+
| `/init` | — | Create LOOM.md |
|
|
177
|
+
| `/memory` | — | Show memory files |
|
|
178
|
+
| `/doctor` | — | Run diagnostics |
|
|
179
|
+
| `/skills` | `install\|remove` | Manage skills |
|
|
180
|
+
| `/mcp` | `add\|remove\|toggle` | Manage MCP servers |
|
|
181
|
+
| `/debug` | — | Show debug info |
|
|
182
|
+
| `/fork` | — | Fork conversation |
|
|
183
|
+
| `/exit` | — | Quit Loom Code |
|
|
184
|
+
|
|
185
|
+
## Keybindings
|
|
186
|
+
|
|
187
|
+
Every key is configurable from `~/.loom/tui.json` (`keybinds`, `leader`,
|
|
188
|
+
`leader_timeout`) — see [docs/keybinds.md](docs/keybinds.md) for the full
|
|
189
|
+
action list, syntax, and opencode-compatible aliases. The defaults:
|
|
190
|
+
|
|
191
|
+
| Key | Action |
|
|
192
|
+
|-----|--------|
|
|
193
|
+
| **ESC** | Interrupt current operation (aborts API requests) / close dialogs / clear the draft |
|
|
194
|
+
| **Ctrl+C** | Exit (copies a text selection first) |
|
|
195
|
+
| **Ctrl+B** | Toggle sidebar (file browser) |
|
|
196
|
+
| **Ctrl+P** | Open command palette |
|
|
197
|
+
| **Ctrl+X** | Leader key — the next key runs a leader binding |
|
|
198
|
+
| **Ctrl+X** then **b / p** | Build mode / Plan mode |
|
|
199
|
+
| **Ctrl+X** then **n / l / x / c** | New session / sessions list / export / compact |
|
|
200
|
+
| **Ctrl+X** then **m / a / h / e** | Model picker / agents / help / editor |
|
|
201
|
+
| **Ctrl+X** then **q** | Quit |
|
|
202
|
+
| **Tab** | Next suggestion, or cycle mode (Build → Plan → Chat) |
|
|
203
|
+
| **Ctrl+A** | Select the whole draft (readline-style) |
|
|
204
|
+
| **Shift+Enter** | Insert a newline in the draft |
|
|
205
|
+
|
|
206
|
+
## Agents
|
|
207
|
+
|
|
208
|
+
Loom Code agent architecture: the user talks to a
|
|
209
|
+
**primary agent** (picked by the active mode), and that primary can delegate
|
|
210
|
+
focused work to **subagents** — either **automatically**, by calling the `task`
|
|
211
|
+
tool when a subtask warrants it, or **manually**, when you prefix a message with
|
|
212
|
+
`@agent`.
|
|
213
|
+
|
|
214
|
+
### Primaries (matched to your mode)
|
|
215
|
+
|
|
216
|
+
| Agent | Mode | Tools | Role |
|
|
217
|
+
|-------|------|-------|------|
|
|
218
|
+
| `build` | Build | all tools (`*`) | Full development work — editing, shell, anything. |
|
|
219
|
+
| `plan` | Plan | read-only + `task` | Analyze and produce an ordered plan. Never edits files or runs shell commands; delegates heavy investigation to subagents. |
|
|
220
|
+
| `chat` | Chat | none | Conversation only. |
|
|
221
|
+
|
|
222
|
+
### Subagents (delegated to via the `task` tool or `@agent` mentions)
|
|
223
|
+
|
|
224
|
+
| Agent | Tools | Role |
|
|
225
|
+
|-------|-------|------|
|
|
226
|
+
| `explore` | read-only, minus `task` | Fast read-only codebase exploration: search symbols, read files, list files. Never modifies anything and never delegates (no recursion). |
|
|
227
|
+
| `scout` | `read`, `glob`, `grep`, `webfetch` | External research: fetch docs, check APIs and dependencies. Read-only. |
|
|
228
|
+
| `general` | all tools, minus `task` | General-purpose autonomous subagent for self-contained implementation tasks, bug fixes, and multi-step work. |
|
|
229
|
+
|
|
230
|
+
Every subagent is **read-only or sandboxed** and **cannot delegate further**
|
|
231
|
+
(subagents never get the `task` tool), so delegation always terminates.
|
|
232
|
+
|
|
233
|
+
### Two ways to invoke a subagent
|
|
234
|
+
|
|
235
|
+
1. **Automatic — the main agent calls `task` itself.** When a turn would
|
|
236
|
+
benefit from a focused subagent (e.g. a fast read-only sweep before editing),
|
|
237
|
+
the primary calls the `task` tool with an agent id and a prompt. Progress
|
|
238
|
+
streams into a dedicated panel in the chat:
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
┌ @explore finished · done ─────────────┐
|
|
242
|
+
│ │ grep · read │
|
|
243
|
+
│ child findings… │
|
|
244
|
+
└────────────────────────────────────────┘
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
2. **Manual — `@agent` mentions.** Prefix your message with `@agent` to force
|
|
248
|
+
the whole turn onto that subagent:
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
@explore find the bug
|
|
252
|
+
@scout what's the latest Stripe API for refunds?
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The `@agent` prefix is stripped from the user bubble shown in chat, so only
|
|
256
|
+
your query renders. Type `@` to open an autocomplete of available subagents.
|
|
257
|
+
|
|
258
|
+
### Listing, configuring, and extending agents
|
|
259
|
+
|
|
260
|
+
- **`/agents`** — prints the active registry (id, mode, tool set, model).
|
|
261
|
+
- **Custom subagents** — add to `~/.loom/config.json`:
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"agents": {
|
|
266
|
+
"reviewer": {
|
|
267
|
+
"mode": "subagent",
|
|
268
|
+
"description": "Reads diffs and flags risky changes before commit.",
|
|
269
|
+
"tools": ["read", "glob", "grep", "diff"],
|
|
270
|
+
"prompt": "You are a cautious code reviewer. Read the diff and list risk points.",
|
|
271
|
+
"model": "anthropic/claude-sonnet-4-20250514"
|
|
272
|
+
},
|
|
273
|
+
"explore": { "disable": true }
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Custom `mode: "subagent"` entries need a `description`; built-ins can be
|
|
279
|
+
disabled with `"disable": true`. Tool patterns use last-match-wins semantics:
|
|
280
|
+
`["*"]` (all), `["read","glob"]` (only those), `["*","!task"]` (all except
|
|
281
|
+
delegation), `["mcp__*"]` (wildcards).
|
|
282
|
+
|
|
283
|
+
## Troubleshooting
|
|
284
|
+
|
|
285
|
+
### "bun not found — full TUI requires bun"
|
|
286
|
+
Install bun from [bun.sh](https://bun.sh). If bun is already installed but not found, add `~/.bun/bin` to your `PATH`.
|
|
287
|
+
|
|
288
|
+
### "API key is invalid or expired"
|
|
289
|
+
Run `/connect <provider>` in the TUI and paste a new key. For environment variables, check your `.env` file.
|
|
290
|
+
|
|
291
|
+
### "403 Forbidden: the API key is not authorized"
|
|
292
|
+
Your key doesn't have access to the selected model. Some providers (especially NVIDIA NIM and OpenRouter) require accepting model terms on their website first.
|
|
293
|
+
|
|
294
|
+
### "402 quota exceeded"
|
|
295
|
+
Your API billing tier or rate limit has been reached. Upgrade your plan or switch to a different provider.
|
|
296
|
+
|
|
297
|
+
### TUI is slow or flickering
|
|
298
|
+
Try launching with `--basic` to use the line-mode REPL instead, which is lighter.
|
|
299
|
+
|
|
300
|
+
## Project Structure
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
LoomCode/
|
|
304
|
+
├── bin/
|
|
305
|
+
│ ├── loom.js # CLI entry point
|
|
306
|
+
│ ├── loomcode.js # Alternative binary name
|
|
307
|
+
│ └── loom-tui.js # TUI launcher
|
|
308
|
+
├── src/
|
|
309
|
+
│ ├── index.js # Bootstraps CLI
|
|
310
|
+
│ ├── core/
|
|
311
|
+
│ │ ├── cli.js # Interactive REPL + slash commands
|
|
312
|
+
│ │ ├── session.js # Conversation + agent tool loop
|
|
313
|
+
│ │ ├── agents.js # agent registry + subagent runner
|
|
314
|
+
│ │ ├── permissions.js # Command permission checks
|
|
315
|
+
│ │ ├── platform.js # OS/platform detection
|
|
316
|
+
│ │ ├── session-store.js # Persisted sessions
|
|
317
|
+
│ │ ├── restore.js # Snapshot/restore project file tree
|
|
318
|
+
│ │ ├── usage.js # Token/cost tracking
|
|
319
|
+
│ │ └── plugin-cmd.js # Subcommand backend
|
|
320
|
+
│ ├── providers/
|
|
321
|
+
│ │ ├── index.js # ProviderRouter dispatch
|
|
322
|
+
│ │ ├── openai-compat.js # OpenAI-compatible provider base
|
|
323
|
+
│ │ ├── anthropic.js # Anthropic Claude connector
|
|
324
|
+
│ │ ├── openai.js # OpenAI GPT connector
|
|
325
|
+
│ │ ├── nvidia.js # NVIDIA NIM connector
|
|
326
|
+
│ │ ├── google.js # Google Gemini connector
|
|
327
|
+
│ │ ├── openrouter.js # OpenRouter connector
|
|
328
|
+
│ │ ├── local.js # Local (Ollama) connector
|
|
329
|
+
│ │ └── custom.js # Custom provider host
|
|
330
|
+
│ ├── tools/
|
|
331
|
+
│ │ └── index.js # read/write/edit/bash/grep/glob/webfetch/todowrite/task
|
|
332
|
+
│ ├── config/
|
|
333
|
+
│ │ ├── settings.js # ~/.loom/config.json persistence
|
|
334
|
+
│ │ └── provider-cmd.js # /connect command logic
|
|
335
|
+
│ └── tui/
|
|
336
|
+
│ ├── App.tsx # OpenTUI root component
|
|
337
|
+
│ ├── store.ts # SolidJS reactive store
|
|
338
|
+
│ ├── theme.ts # Theme / palette
|
|
339
|
+
│ ├── components/
|
|
340
|
+
│ │ ├── InputBar.tsx # Chat input + autocomplete
|
|
341
|
+
│ │ ├── ChatArea.tsx # Message list
|
|
342
|
+
│ │ ├── BreadcrumbBar.tsx # Mode + provider bar
|
|
343
|
+
│ │ ├── Modals.tsx # Settings pickers
|
|
344
|
+
│ │ └── Sidebar.tsx # File sidebar
|
|
345
|
+
├── package.json
|
|
346
|
+
├── LOOM.md # Developer reference
|
|
347
|
+
└── .gitignore
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## Configuration
|
|
351
|
+
|
|
352
|
+
Config is stored at `~/.loom/config.json` (permissions: 0600). Includes:
|
|
353
|
+
- `provider` — default LLM provider
|
|
354
|
+
- `model` — per-provider model IDs (editable in source or via `/model`)
|
|
355
|
+
- `apiKeys` — API keys from `/connect` or manual edit
|
|
356
|
+
- `baseUrls` — custom provider endpoints
|
|
357
|
+
- `maxTokens`, `temperature` — model settings
|
|
358
|
+
- `permission` — OpenCode-style permission tree (see below)
|
|
359
|
+
- `permissionRules` — rules saved from the permission popup ("Always allow"/"Never")
|
|
360
|
+
|
|
361
|
+
API keys can also be set via environment variables: `.env` or `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `NVIDIA_API_KEY`, `GOOGLE_API_KEY`, `OPENROUTER_API_KEY` etc.
|
|
362
|
+
|
|
363
|
+
## Permissions
|
|
364
|
+
|
|
365
|
+
Every tool call resolves through an OpenCode-style permission tree, so you can
|
|
366
|
+
allow or block specific tools, commands, and file patterns without retyping a
|
|
367
|
+
prompt each time. The last matching rule wins; `*` matches any run of
|
|
368
|
+
characters and `?` a single one, and `~`/`$HOME` are expanded in paths.
|
|
369
|
+
|
|
370
|
+
```jsonc
|
|
371
|
+
{
|
|
372
|
+
"permission": {
|
|
373
|
+
"bash": {
|
|
374
|
+
"*": "allow", // allow shell commands...
|
|
375
|
+
"git push --force": "deny" // ...except destructive ones
|
|
376
|
+
},
|
|
377
|
+
"edit": { "*": "ask", "src/**": "allow" },
|
|
378
|
+
"read": {
|
|
379
|
+
"*": "allow",
|
|
380
|
+
"*.env": "deny", // secrets stay off-limits by default
|
|
381
|
+
"*.env.example": "allow"
|
|
382
|
+
},
|
|
383
|
+
"external_directory": "ask" // reads/writes outside the project dir
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Available permission keys: `read`, `edit` (covers edit/write), `glob`, `grep`,
|
|
389
|
+
`bash`, `task`, `skill`, `lsp`, `question`, `webfetch`, `websearch`,
|
|
390
|
+
`external_directory` (paths outside the working directory), and `doom_loop`
|
|
391
|
+
(three identical tool calls in a row). Most tools default to `allow`;
|
|
392
|
+
`edit`/`bash`/`task`/`skill`/`external_directory`/`doom_loop` default to `ask`,
|
|
393
|
+
and `read` denies `*.env`/`*.env.*` files (except `*.env.example`).
|
|
394
|
+
|
|
395
|
+
When the model asks for permission, the TUI popup offers Allow, Always allow,
|
|
396
|
+
Deny, or a typed answer. "Always allow"/"Never" persist a rule to
|
|
397
|
+
`permissionRules`. Run `loom --auto` (or `/permissions auto` in the TUI, or
|
|
398
|
+
Ctrl+P → the palette) to auto-approve `ask` results — explicit `deny` rules
|
|
399
|
+
still block. A muted `auto` indicator appears in the status row while enabled.
|
|
400
|
+
|
|
401
|
+
Per-agent overrides live in the agent's own config
|
|
402
|
+
(`agent.<id>.permission`, same shape, applied on top of the global tree):
|
|
403
|
+
|
|
404
|
+
```jsonc
|
|
405
|
+
{
|
|
406
|
+
"agent": {
|
|
407
|
+
"explore": { "mode": "subagent", "permission": { "edit": "deny" } }
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Adding a New Provider
|
|
413
|
+
|
|
414
|
+
Register in `src/providers/<name>.js` exporting `{ chat, stream, models }`:
|
|
415
|
+
|
|
416
|
+
```js
|
|
417
|
+
async function chat(messages, options) {
|
|
418
|
+
// call API, return { content: '...', toolCalls: [{...}], usage: {{...}} }
|
|
419
|
+
}
|
|
420
|
+
async function stream(messages, options, onDelta) {
|
|
421
|
+
// stream response, return { content, toolCalls, usage }
|
|
422
|
+
}
|
|
423
|
+
const models = [
|
|
424
|
+
{ id: 'my-model', name: 'My Model', provider: 'myname', context: 128000, priceIn: 0.50, priceOut: 2.00 }
|
|
425
|
+
];
|
|
426
|
+
module.exports = { chat, stream, models };
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Then add it in `src/providers/index.js` under `PROVIDERS`.
|
|
430
|
+
|
|
431
|
+
## License
|
|
432
|
+
|
|
433
|
+
MIT
|