@ziamana/bruine 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 (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +315 -0
  3. package/THIRD_PARTY_NOTICES.md +24 -0
  4. package/cordis.patch.yml +166 -0
  5. package/dist/bin.js +5358 -0
  6. package/dist/compat.js +40 -0
  7. package/dist/plugins/approval.js +147 -0
  8. package/dist/plugins/headless.js +236 -0
  9. package/dist/plugins/herdr.js +470 -0
  10. package/dist/plugins/mcp.js +275 -0
  11. package/dist/plugins/modes.js +850 -0
  12. package/dist/plugins/render.js +3723 -0
  13. package/dist/plugins/repl.js +9589 -0
  14. package/dist/plugins/silence.js +234 -0
  15. package/dist/plugins/startup.js +42 -0
  16. package/dist/plugins/web-search.js +138 -0
  17. package/package.json +93 -0
  18. package/skills/code-review/SKILL.md +30 -0
  19. package/skills/git-workflow/SKILL.md +31 -0
  20. package/skills/impeccable/LICENSE +191 -0
  21. package/skills/impeccable/NOTICE.md +11 -0
  22. package/skills/impeccable/SKILL.md +87 -0
  23. package/skills/impeccable/reference/adapt.md +318 -0
  24. package/skills/impeccable/reference/adapt.native.md +58 -0
  25. package/skills/impeccable/reference/android.md +46 -0
  26. package/skills/impeccable/reference/animate.md +89 -0
  27. package/skills/impeccable/reference/audit.md +137 -0
  28. package/skills/impeccable/reference/audit.native.md +139 -0
  29. package/skills/impeccable/reference/bolder.md +33 -0
  30. package/skills/impeccable/reference/clarify.md +94 -0
  31. package/skills/impeccable/reference/colorize.md +86 -0
  32. package/skills/impeccable/reference/component-review.md +63 -0
  33. package/skills/impeccable/reference/craft-floor.md +44 -0
  34. package/skills/impeccable/reference/craft.md +5 -0
  35. package/skills/impeccable/reference/critique.md +806 -0
  36. package/skills/impeccable/reference/degraded/asset-producer.md +42 -0
  37. package/skills/impeccable/reference/degraded/documenter.md +24 -0
  38. package/skills/impeccable/reference/degraded/finish-reviewer.md +38 -0
  39. package/skills/impeccable/reference/degraded/manual-edit-applier.md +92 -0
  40. package/skills/impeccable/reference/delight.md +70 -0
  41. package/skills/impeccable/reference/distill.md +111 -0
  42. package/skills/impeccable/reference/doctor.md +54 -0
  43. package/skills/impeccable/reference/document.md +416 -0
  44. package/skills/impeccable/reference/extract.md +69 -0
  45. package/skills/impeccable/reference/generate.md +101 -0
  46. package/skills/impeccable/reference/harden.md +345 -0
  47. package/skills/impeccable/reference/hooks.md +113 -0
  48. package/skills/impeccable/reference/init.md +131 -0
  49. package/skills/impeccable/reference/ios.md +51 -0
  50. package/skills/impeccable/reference/layout.md +84 -0
  51. package/skills/impeccable/reference/live-setup.md +104 -0
  52. package/skills/impeccable/reference/live.md +325 -0
  53. package/skills/impeccable/reference/mode-operate.md +21 -0
  54. package/skills/impeccable/reference/mode-persuade.md +19 -0
  55. package/skills/impeccable/reference/mode-read.md +21 -0
  56. package/skills/impeccable/reference/new-work.md +154 -0
  57. package/skills/impeccable/reference/onboard.md +234 -0
  58. package/skills/impeccable/reference/operate.md +61 -0
  59. package/skills/impeccable/reference/optimize.md +258 -0
  60. package/skills/impeccable/reference/overdrive.md +127 -0
  61. package/skills/impeccable/reference/polish.md +105 -0
  62. package/skills/impeccable/reference/quieter.md +99 -0
  63. package/skills/impeccable/reference/region-map.md +26 -0
  64. package/skills/impeccable/reference/routing.md +24 -0
  65. package/skills/impeccable/reference/shape.md +59 -0
  66. package/skills/impeccable/reference/typeset.md +80 -0
  67. package/skills/impeccable/reference/visualize.md +46 -0
  68. package/skills/impeccable/scripts/VERSION +1 -0
  69. package/skills/impeccable/scripts/command-metadata.json +98 -0
  70. package/skills/impeccable/scripts/data/font-index-failures.json +121 -0
  71. package/skills/impeccable/scripts/data/font-index.json +1 -0
  72. package/skills/impeccable/scripts/impeccable +206 -0
  73. package/skills/impeccable/scripts/impeccable.cmd +214 -0
  74. package/skills/impeccable/scripts/live-browser-dom.js +167 -0
  75. package/skills/impeccable/scripts/live-browser-ignores.js +242 -0
  76. package/skills/impeccable/scripts/live-browser-session.js +148 -0
  77. package/skills/impeccable/scripts/live-browser.js +13510 -0
  78. package/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
  79. package/skills/make-interfaces-feel-better/LICENSE +21 -0
  80. package/skills/make-interfaces-feel-better/SKILL.md +187 -0
  81. package/skills/make-interfaces-feel-better/agents/openai.yaml +3 -0
  82. package/skills/make-interfaces-feel-better/animations.md +403 -0
  83. package/skills/make-interfaces-feel-better/icons.md +63 -0
  84. package/skills/make-interfaces-feel-better/performance.md +88 -0
  85. package/skills/make-interfaces-feel-better/surfaces.md +256 -0
  86. package/skills/make-interfaces-feel-better/typography.md +157 -0
  87. package/skills/playwright-cli/LICENSE +201 -0
  88. package/skills/playwright-cli/SKILL.md +489 -0
  89. package/skills/playwright-cli/references/element-attributes.md +23 -0
  90. package/skills/playwright-cli/references/playwright-tests.md +39 -0
  91. package/skills/playwright-cli/references/pr-attachments.md +60 -0
  92. package/skills/playwright-cli/references/request-mocking.md +87 -0
  93. package/skills/playwright-cli/references/running-code.md +245 -0
  94. package/skills/playwright-cli/references/session-management.md +227 -0
  95. package/skills/playwright-cli/references/storage-state.md +275 -0
  96. package/skills/playwright-cli/references/test-generation.md +433 -0
  97. package/skills/playwright-cli/references/tracing.md +139 -0
  98. package/skills/playwright-cli/references/video-recording.md +216 -0
  99. package/skills/remotion/SKILL.md +42 -0
  100. package/skills/systematic-debugging/SKILL.md +26 -0
  101. package/skills/thermo-nuclear-code-quality-review/LICENSE +21 -0
  102. package/skills/thermo-nuclear-code-quality-review/SKILL.md +192 -0
  103. package/skills/write-tests/SKILL.md +35 -0
  104. package/skills/youtube-transcript/LICENSE +21 -0
  105. package/skills/youtube-transcript/SKILL.md +41 -0
  106. package/skills/youtube-transcript/package.json +8 -0
  107. package/skills/youtube-transcript/transcript.js +44 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ziamana
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.
package/README.md ADDED
@@ -0,0 +1,315 @@
1
+ <div align="center">
2
+
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="docs/media/wordmark-dark.svg">
5
+ <img src="docs/media/wordmark-light.svg" alt="bruine" width="380">
6
+ </picture>
7
+
8
+ ### A coding agent for your terminal that runs your own models.
9
+
10
+ *bruine* (French, /bʁɥin/): a fine, steady rain.
11
+
12
+ [![License: MIT](https://img.shields.io/badge/license-MIT-b4a7ff)](LICENSE)
13
+ [![Node 22+](https://img.shields.io/badge/node-22%2B-7dcfff)](https://nodejs.org)
14
+ ![Windows · macOS · Linux](https://img.shields.io/badge/Windows%20%C2%B7%20macOS%20%C2%B7%20Linux-supported-8fe3a3)
15
+ [![Changelog](https://img.shields.io/badge/version-0.1.0-ff9ed2)](CHANGELOG.md)
16
+
17
+ <a href="docs/media/bruine-film.mp4"><img src="docs/media/hero.webp" alt="The bruine film: the word bruine becomes the logo in the rain, then the effort climbs from low to max and the rain turns into a storm" width="100%"></a>
18
+
19
+ <sub>▶ <a href="docs/media/bruine-film.mp4"><b>Watch the 42-second film</b></a> (with sound) · made in code with Remotion, the demo inside it is the real bruine, recorded cell by cell</sub>
20
+
21
+ </div>
22
+
23
+ <br>
24
+
25
+ Point bruine at a llama.cpp server on your machine, at any cloud provider (DeepSeek, Anthropic,
26
+ OpenAI, Google, OpenRouter, Groq, Mistral, xAI and about twenty more), or at anything that speaks
27
+ the OpenAI-compatible `/v1` API. It reads and edits your code, runs your commands, and asks before
28
+ anything risky. There is no account, and nothing is sent anywhere you did not point it at.
29
+
30
+ ```
31
+ npm install -g @ziamana/bruine
32
+ bruine
33
+ ```
34
+
35
+ The first launch opens a setup wizard: it finds a local model server if you run one, asks for API
36
+ keys if you want a cloud route, lets you pick skills, and writes `~/.bruine/`.
37
+
38
+ ## See it
39
+
40
+ <img src="docs/demo/bruine.svg" alt="bruine in a terminal: a prompt, the model's reasoning streaming then folding away, a file read, an edit shown as a diff, two permission prompts, the tests passing, and a second prompt that was queued while it worked going out after" width="100%">
41
+
42
+ <sub>A real session in a real terminal on a real project, with a scripted model so it is the same every
43
+ time: [`test/e2e/demo-recording.test.ts`](test/e2e/demo-recording.test.ts) records it. The stills below
44
+ come the same way, from [`test/e2e/screenshots.test.ts`](test/e2e/screenshots.test.ts).</sub>
45
+
46
+ <table>
47
+ <tr>
48
+ <td width="50%" valign="top">
49
+ <img src="docs/media/approval.png" alt="An approval: a framed amber band saying Allow write src/summary.ts, with y Allow once, a Always allow every write this session, n Reject, and the prompt box below reading Waiting for you 3s">
50
+ <p><b>It asks first, in one keystroke.</b> The request is the one framed thing on screen: <code>y</code> once,
51
+ <code>a</code> always (and it says exactly what "always" covers), <code>n</code> or <code>esc</code> no. The
52
+ turn's clock stops and the rain holds still while it waits for you.</p>
53
+ </td>
54
+ <td width="50%" valign="top">
55
+ <img src="docs/media/retry.png" alt="A retry: the transcript says The model has not answered for 1s. Retry 1/5 in 0.5s, and the prompt box reads Working 6s, retry 1/5">
56
+ <p><b>A quiet model is retried, out loud.</b> How long bruine waits depends on what the model was doing,
57
+ how hard it was asked to think and how many tries it has had; when it gives up, the transcript says
58
+ why, which attempt, and when.</p>
59
+ </td>
60
+ </tr>
61
+ <tr>
62
+ <td width="50%" valign="top">
63
+ <img src="docs/media/image.png" alt="read_image design/mockup.png: the image itself drawn in the terminal with coloured half blocks, a night-blue gradient with a falling drop and rings, captioned PNG 192x108">
64
+ <p><b>It shows you what it looks at.</b> An image the model reads is drawn right in the transcript, in
65
+ any 24-bit or 256-colour terminal: no graphics protocol, no window, just colour.</p>
66
+ </td>
67
+ <td width="50%" valign="top">
68
+ <img src="docs/media/light.png" alt="bruine on a light terminal theme: an edit shown as a diff with pale red and green bands, dark text, and the status bar with cache 95.3% and ctx 6.4% of 100k">
69
+ <p><b>Light or dark, it reads.</b> bruine asks the terminal for its background and fits every colour to
70
+ it; on 256-colour terminals the surfaces stay gray instead of turning navy.</p>
71
+ </td>
72
+ </tr>
73
+ </table>
74
+
75
+ ## Why bruine
76
+
77
+ - **Your models, first class.** A local model on one GPU is not a degraded cloud: bruine keeps the
78
+ prompt cache warm (the system prompt and the tool list never change inside a session), keeps side
79
+ requests off a single-slot server, and shows what the hardware really does.
80
+ - **One permission gate you can read.** Ask, Auto or Full access, decided by a rule table in plain
81
+ TypeScript with tests, not a black box: a plain read runs, a path outside the project asks, a
82
+ dangerous command always asks, and Plan mode refuses changes. MCP tools go through the same gate.
83
+ - **Numbers you can trust.** Context used, tokens per second, prefill and cache hits are measured from
84
+ bruine's own clock. The harness once reported 79 tok/s where the truth was 60; bruine does not copy it.
85
+ - **Calm.** Reasoning streams word by word and folds into one line; tool calls read as sentences; edits
86
+ are diffs; you can keep typing while it works. And it rains in your terminal, as hard as the model is
87
+ asked to think.
88
+
89
+ ## The gist in a minute
90
+
91
+ | You want to | In bruine |
92
+ |---|---|
93
+ | Plan before it builds | `Shift+Tab` toggles Plan and Build |
94
+ | Choose how much it asks | `/permissions`, or `/ask`, `/auto`, `/full` |
95
+ | Think harder or faster | `ctrl+e` cycles the reasoning effort, `/effort` picks one |
96
+ | Switch model mid-session | `/model`, `f2` walks the ones you used recently |
97
+ | Queue the next task while it works | just type and press Enter: it waits above the box |
98
+ | Run a command yourself | `!npm test` (the output is not sent to the model) |
99
+ | Show it a screenshot | `ctrl+v` (`alt+v` in Windows Terminal) |
100
+ | Use your MCP servers | `mcpServers` in `bruine.json` or `.mcp.json`, then `/mcp` |
101
+ | Reuse your skills | Claude Code, OpenCode, pi and `~/.agents/skills` skills are picked up |
102
+ | Script it | `bruine -p "task" --output-format json` |
103
+
104
+ A first setup also asks, as a plain yes or no, whether to use Space Bunny Free: a model OpenCode serves
105
+ at no charge for a limited time through its Zen gateway, with no account and no key. The answer starts on
106
+ No. Saying yes sends your prompts and files to OpenCode's provider (which states zero retention and no
107
+ training), and the offer can end without notice, so keep another model in reach.
108
+
109
+ ## What it does
110
+
111
+ | Feature | What you see |
112
+ |---|---|
113
+ | Live reasoning | The model's thinking streams in place, word by word, then collapses to `Thought for 4.2s` |
114
+ | Diffs | Every edit and write is shown as a diff, one green or red band per line, before you approve it |
115
+ | Images, both ways | `ctrl+v` sends a screenshot to a vision model, and an image the model reads is drawn in the transcript |
116
+ | Retries you can see | An adaptive silence budget per request, and "Retry 2/5" in the transcript instead of a clock that keeps counting |
117
+ | Updates | A notice when a new version is out, and `/update` installs it after a yes |
118
+ | Tool calls | Every call streams its arguments, then a grouped summary with a duration and a coloured rail |
119
+ | A real permission gate | Ask / Auto / Full access, decided by a rule table you can read, not a black box |
120
+ | Plan mode | `Shift+Tab` to plan before it builds; the plan is a message, the tools never change |
121
+ | `/model` and `/provider` | Switch model mid-session from the server's own live catalogue |
122
+ | Skills | Reuses the skills you already wrote for Claude Code, OpenCode, pi or `~/.agents/skills` |
123
+ | Context and speed | A footer that shows context used, tok/s, prefill and cache hit rate |
124
+ | Works on | Windows Terminal, PowerShell, macOS Terminal, iTerm2, Konsole, GNOME Terminal |
125
+ | herdr | Works with herdr: shows up as `bruine` in `herdr agent list`. |
126
+
127
+ ## The weather
128
+
129
+ Use `/effect` for a live preview picker, or choose directly: `/effect bruine` (quiet drizzle), `/effect pluie` (steady rain), `/effect foudre` (heavy rain with distant lavender lightning), `/effect auto` (drizzle at rest; while working, rain as hard as the reasoning effort: each of low, medium, high, xhigh and max has its own weather, and max brings distant lightning), or `/effect off`.
130
+ `/effect on` is an alias for `/effect auto`. Enter saves the choice in `bruine.json`; Escape cancels the preview. Weather is rendered locally and uses no model tokens.
131
+ It follows the visible screen, including empty space below the composer and while reading back. It stays clear of the composer, controls and painted cards, pauses while selecting text, and follows `BRUINE_NO_ANIMATION`, `BRUINE_NO_RAIN`, ASCII and no-color settings.
132
+
133
+ <p align="center"><a href="docs/media/bruine-film.mp4"><img src="docs/media/effort.webp" alt="The prompt box as ctrl+e climbs from low to max: its border glows violet, then runs every colour, and the rain turns into a storm" width="100%"></a></p>
134
+
135
+ ## MCP servers
136
+
137
+ bruine runs the tools of any MCP server, in the format Claude Code and Cursor use, so a server row
138
+ from a README or from your Claude Code config works as it is. Put your own servers under
139
+ `mcpServers` in `~/.bruine/bruine.json`; a project can share its own in `.mcp.json` at its root.
140
+
141
+ ```json
142
+ {
143
+ "mcpServers": {
144
+ "github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" } },
145
+ "docs": { "type": "http", "url": "https://example.com/mcp", "readOnly": true }
146
+ }
147
+ }
148
+ ```
149
+
150
+ - The tools show up as `mcp__<server>__<tool>` and go through the same permission gate as every
151
+ other tool: they ask first in Ask mode, are judged in Auto, and Plan mode refuses them. A server
152
+ marked `"readOnly": true` runs without asking (also in Plan mode); `"alwaysAllow": ["tool"]`
153
+ lets chosen tools through. `${VAR}` and `${VAR:-default}` are read from the environment.
154
+ - A project's `.mcp.json` starts programs on your machine, so its servers only run once you have
155
+ seen what they run and said yes. The answer is kept per project, and a changed command asks again.
156
+ - Servers connect before the first turn and the tool list stays the same for the whole session, so
157
+ the prompt cache survives. `/mcp` lists the servers, their state and tools; `/mcp enable <name>`
158
+ and `/mcp disable <name>` apply to the next session.
159
+ - Tools are bridged; MCP resources and prompts are not (yet). Streamable HTTP is supported, the old
160
+ SSE transport is not.
161
+
162
+ ## How it compares
163
+
164
+ An honest table, checked against each project's documentation in October 2026; tell us if something
165
+ moved. Every one of these is a good tool, and some do things bruine does not.
166
+
167
+ | | bruine | Claude Code | OpenCode | Aider | Codex CLI |
168
+ |---|---|---|---|---|---|
169
+ | Source | MIT | Proprietary | MIT | Apache-2.0 | Apache-2.0 |
170
+ | Models | Any: local llama.cpp, any `/v1` server, ~30 cloud providers | Claude (Anthropic API, Bedrock, Vertex) | 75+ providers, local included | Most LLMs, local included | OpenAI; local open-weight models with `--oss` |
171
+ | Built for local models | Yes: cache kept warm, single-slot aware, measured tok/s | No | Supported | Supported | gpt-oss through Ollama or LM Studio |
172
+ | MCP | Tools, stdio and HTTP | Yes | Yes | Not built in | Yes |
173
+ | Permission model | One rule table, Ask / Auto / Full, Plan mode | Modes and rules, Plan mode | Per-agent permissions, Plan agent | Confirms commands; Git is the safety net | Approval modes and an OS sandbox |
174
+ | Undo a change | No (use Git) | Yes, checkpoints and `/rewind` | Yes, `/undo` `/redo` | Yes, every edit is a commit, `/undo` | No (`/undo` was removed; use Git) |
175
+ | IDE integration | No, terminal only | Yes | Yes (LSP, desktop app) | Editor plugins by the community | Yes |
176
+
177
+ **Where bruine is behind, today:** no checkpoints or rewind (Git is your undo), no IDE or ACP
178
+ integration, MCP is tools only (no resources, prompts or OAuth login), no LSP, and it is young (0.1)
179
+ on top of a harness that is itself a developer preview. If you mostly use Claude models in an IDE,
180
+ Claude Code is the better fit; if you want Git-commit-per-edit, Aider is.
181
+
182
+ ## FAQ
183
+
184
+ **Is it free?** Yes, MIT, and everything that runs locally stays MIT. You pay your model provider, or
185
+ nothing at all with a local model.
186
+
187
+ **Does it send my code anywhere?** Only to the model route you configured. Telemetry is off unless you
188
+ turn it on in the setup. Network discovery of model servers is opt-in and only touches private ranges
189
+ (10/8, 172.16/12, 192.168/16, and Tailscale 100.64/10).
190
+
191
+ **Which local model should I use?** One trained for tool calls, served by llama.cpp (or any OpenAI
192
+ compatible server) with a context of 32k or more. Small models can chat but tend to lose the thread in
193
+ long agent loops. The setup detects what the server's chat template supports (thinking, effort levels).
194
+
195
+ **Does it work on Windows?** Yes: Windows Terminal, PowerShell and the classic console; commands run in
196
+ PowerShell there. [docs/PLATFORMS.md](docs/PLATFORMS.md) lists what was checked and what still has limits.
197
+
198
+ **How do I make it ask less?** `/auto` lets a fast model judge the routine actions (risky ones still
199
+ ask), the approval's `a` (Always) remembers one exact command for the session and says which, and
200
+ `/full` asks nothing at all (it says so loudly). For an MCP server you trust, `"readOnly": true` or `"alwaysAllow": [...]`.
201
+
202
+ **Can I reuse my Claude Code setup?** Your skills, yes, and MCP servers in the same format, including a
203
+ project's `.mcp.json`. Claude Code hooks are not run.
204
+
205
+ **What is dsh?** [DeepSeek Harness](https://github.com/deepseek-ai), the agent runtime underneath.
206
+ bruine is a profile and a bundle of plugins on top of it, not a fork, so harness updates arrive without
207
+ a merge. The version is pinned and upgraded on purpose.
208
+
209
+ **Why does it rain?** Because it is called bruine. The weather is local, uses no tokens, stays out of
210
+ the input box and the cards, follows the effort with `/effect auto`, and goes away with `/effect off`
211
+ or `BRUINE_NO_ANIMATION=1`.
212
+
213
+ ## Commands
214
+
215
+ ```
216
+ bruine Start bruine. Extra args are passed through to dsh.
217
+ bruine --continue Resume the latest conversation in this project.
218
+ bruine -p "task" Run a task without the terminal UI and print its answer.
219
+ bruine -p - Read a task from stdin.
220
+ bruine setup (Re)run the setup wizard, pre-filled with your current values.
221
+ bruine skills List the skills bruine has enabled.
222
+ bruine update Update bruine through its installer.
223
+ bruine --version Print the version.
224
+ bruine --help Print the help.
225
+ ```
226
+
227
+ Use `--output-format json` or `--output-format stream-json` with `-p` for scripts. A headless
228
+ task denies any tool action that would need a permission prompt; use `--permission-mode full`
229
+ only when that task should run with full access.
230
+
231
+ Inside a session, `/` opens the command palette: `/new`, `/resume`, `/verify`, `/compact`, `/plan`, `/permissions`,
232
+ `/model`, `/provider`, `/effort`, `/skills`, `/plugins`, `/mcp`, `/update`, `/reload`, `/help`, `/exit`. `f2` walks the routes you
233
+ used recently. You can keep typing while bruine works: a prompt sent during a turn waits above the
234
+ box and goes out, as its own turn, when the current one ends (`↑` on an empty box takes the last one
235
+ back to edit; `escape` stops the turn and puts the queued prompts back in the box, unsent). `ctrl+t` shows the task list, `ctrl+o` expands tool output, `ctrl+e` cycles the
236
+ reasoning effort, `ctrl+v` pastes an image (`alt+v` in Windows Terminal, which keeps `ctrl+v` for its own paste).
237
+
238
+ ## Updates
239
+
240
+ Once a day, bruine asks npm for the latest version (one plain GET, nothing about you). When there
241
+ is a newer one, the session says so above the box: `/update` installs it after a yes, through the
242
+ installer bruine was installed with (npm, pnpm or bun), and `bruine update` does the same from a
243
+ shell. A git checkout is never touched. `"updateCheck": false` in `bruine.json` turns the check off.
244
+ What changed is in [CHANGELOG.md](CHANGELOG.md).
245
+
246
+ ## When the model goes quiet
247
+
248
+ A model can be silent for a long time and still be working: thinking at a high effort, or writing a
249
+ large file whose content the server sends at once. It can also simply have stopped. bruine gives
250
+ each silence a budget that depends on what the model was doing (more time for a model that has not
251
+ started, has just finished thinking, or is writing a big tool call; less in the middle of a
252
+ sentence), scaled by the effort and doubled on every retry. Past it, the request is retried, and the
253
+ transcript says so: "The model has not answered for 3m. Retry 2/5 in 1.2s." The route's
254
+ `streamIdleTimeoutMs` in `settings.yaml` stays the hard ceiling.
255
+
256
+ ## Environment
257
+
258
+ | Variable | Effect |
259
+ |---|---|
260
+ | `BRUINE_HOME` | Where bruine keeps its config (default `~/.bruine`) |
261
+ | `BRUINE_ASCII=1` | Plain-ASCII glyphs instead of symbols |
262
+ | `BRUINE_NO_ANIMATION=1` | No rain, header or spinner animation |
263
+ | `BRUINE_NO_RIPPLE=1` | Only the ring a finished turn leaves on the prompt rule; the rest of the motion stays |
264
+ | `BRUINE_NO_RAIN=1` | No rain in the setup (margins and behind the panel); the rest of the motion stays |
265
+ | `BRUINE_NO_GLOW=1` | The prompt frame stays one colour whatever the thinking effort (otherwise `high` shimmers violet slowly, `xhigh` fast, and `max` runs every colour) |
266
+ | `BRUINE_INTRO` | The logo's entrance at launch: `random` (the default), `off`, or one effect: `rain`, `decrypt`, `beams`, `wipe`, `slide`, `blackhole`, `spotlights`, `waves`, `fog`, `mist`, `afterrain`, `storm`. Also `"intro"` in `bruine.json`. Any key stops it. |
267
+ | `BRUINE_TOOL_SUMMARIES=0` | Disable readable tool descriptions (also `"toolSummaries": false` in `bruine.json`). Summaries reuse existing arguments and make no model calls. |
268
+ | `BRUINE_BG=0` | Never paint a background, whatever the terminal reports |
269
+ | `BRUINE_NO_UPDATE_CHECK=1` | Never contact the npm registry to check for a version |
270
+ | `BRUINE_SILENCE=off` | Keep the adapters' fixed stream timeout instead of bruine's adaptive silence budget |
271
+
272
+ ## Three promises
273
+
274
+ **The numbers are measured, not quoted.** The tok/s in the footer is computed by bruine from its own
275
+ timestamps. The same measurement found the harness reporting 79 tok/s where the truth was 60, so
276
+ bruine does not copy it.
277
+
278
+ **The permission gate is the only one.** The harness sandbox is deliberately left fully open and
279
+ bruine's own rule table is what asks. One layer, readable, testable: a plain command is allowed, a
280
+ path outside the workspace is not, and a dangerous one is never guessed about.
281
+
282
+ **Your prompt cache survives.** The system prompt and the tool list stay byte-identical for the whole
283
+ session. A mode change is an appended message, never a prompt or tool swap. A test spawns the real
284
+ harness and asserts the request prefix never changes.
285
+
286
+ Telemetry is off unless you turn it on in the wizard. Network discovery is opt-in and only ever
287
+ touches private ranges (10/8, 172.16/12, 192.168/16, and Tailscale 100.64/10).
288
+
289
+ ## Requirements
290
+
291
+ Node 22 or newer. Windows, macOS, or Linux.
292
+
293
+ ## License
294
+
295
+ MIT. Everything that runs locally stays MIT.
296
+
297
+ ## More
298
+
299
+ - [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) - the design, the product constraints, and the
300
+ dsh APIs bruine depends on
301
+ - [`docs/tickets/`](./docs/tickets) - the work, one ticket at a time
302
+ - [`docs/PLATFORMS.md`](./docs/PLATFORMS.md) - what was checked and fixed for Windows and macOS
303
+ - [`bench/`](./bench) - the benchmark: a system-prompt change only ships when a number says it helps
304
+
305
+ ## Existing Kumo installations
306
+
307
+ The `bruine` and `kumo` commands use the same launcher. Existing installs keep working without moving or deleting their home.
308
+
309
+ The home is selected in this order: `BRUINE_HOME`, `KUMO_HOME`, an existing `~/.bruine`, an existing `~/.kumo`, then `~/.bruine` for a fresh install.
310
+ Every `BRUINE_*` setting takes precedence over its matching `KUMO_*` fallback, including an explicitly empty value.
311
+ Bruine reads `bruine.json` first and falls back to `kumo.json` only when the new file is absent. Saves write `bruine.json` and leave `kumo.json` untouched.
312
+ The installed-skills manifest follows the same rule: `.bruine-installed.json`, falling back to `.kumo-installed.json`.
313
+ Bruine creates `profiles/bruine` when needed and leaves `profiles/kumo` in place. Updates install the `bruine` npm package.
314
+
315
+ The GitHub repository is `ziamana/bruine`; the old `ziamana/kumo-code` address redirects to it.
@@ -0,0 +1,24 @@
1
+ # Third-party notices
2
+
3
+ Bruine ships a few skills written by other people, copied unchanged from their public
4
+ repositories with their license files. Each one keeps its own `LICENSE` (and `NOTICE.md`
5
+ where its author provides one) inside `skills/<name>/`. They are offered in the setup, and
6
+ a user can leave any of them out.
7
+
8
+ | Skill | Author and source | License | Taken from |
9
+ |---|---|---|---|
10
+ | `impeccable` | Paul Bakaus, https://github.com/pbakaus/impeccable | Apache-2.0 | commit e103efe, `plugin/skills/impeccable` |
11
+ | `make-interfaces-feel-better` | Jakub Krehel, https://github.com/jakubkrehel/make-interfaces-feel-better | MIT | commit 35545ea, `skills/make-interfaces-feel-better` |
12
+ | `thermo-nuclear-code-quality-review` | Cursor, https://github.com/cursor/plugins | MIT | commit 23e4138, `cursor-team-kit/skills/thermo-nuclear-code-quality-review` |
13
+ | `youtube-transcript` | Mario Zechner, https://github.com/badlogic/pi-skills | MIT | commit 90bb51c, `youtube-transcript` |
14
+ | `playwright-cli` | Microsoft, https://github.com/microsoft/playwright-cli | Apache-2.0 | commit b85c7a7, `skills/playwright-cli` |
15
+
16
+ `impeccable` itself carries a notice (`skills/impeccable/NOTICE.md`) for reference files it
17
+ derives from `ehmo/platform-design-skills` (MIT).
18
+
19
+ The Bruine skills in the same folder (`code-review`, `git-workflow`, `systematic-debugging`,
20
+ `write-tests`, `remotion`) are Bruine's own and fall under Bruine's license.
21
+
22
+ `youtube-transcript` installs its npm dependency (`youtube-transcript-plus`, MIT) the first
23
+ time it is used; nothing of it is shipped. `impeccable`'s launcher may download its helper
24
+ binary on first use.
@@ -0,0 +1,166 @@
1
+ # The bruine bundle patch: an interactive terminal agent over dsh-base.
2
+ # Inserts the bruine plugins (startup, repl, render, approval, web-search, mcp) and
3
+ # the extra tool rows. Cache rule (ARCHITECTURE section 0): the system prompt
4
+ # and the tools array are byte-identical all session long — modes are a
5
+ # bruine-side gate + appended messages, never a prompt/tool swap.
6
+
7
+ # T36: only the harness identity is switched off here. The persona itself is
8
+ # composed at boot by src/profile.ts into the profile's own patch layer, where
9
+ # it carries the model's display name (a local route's model id is a .gguf
10
+ # path) — and where the bruine-bench system-prompt variants are applied, without
11
+ # ever editing this file or dsh.
12
+ - id: system-prompt
13
+ config:
14
+ includeHarnessIdentity: false
15
+
16
+ # bruine's own search provider (SearXNG / Brave / Tavily, configured in
17
+ # bruine.json; "none" by default). Never dsh-free-search. The provider registers
18
+ # itself with the `web` runtime lazily, so no gating is needed here.
19
+ - id: web
20
+ config:
21
+ searchProvider: bruine
22
+ fetchProvider: http
23
+
24
+ # T26: bruine owns the USER skills root. Setup links/copies the chosen skills
25
+ # into $DSH_HOME/skills (dsh's dshHome root, unchanged), so repoint
26
+ # agentsHome at a bruine-owned empty directory: `~/.agents/skills` is no longer
27
+ # read implicitly. Project skills (`.agents/skills`, `.dsh/skills` in the
28
+ # workspace) keep working; `dshHomePath` resolves $DSH_HOME at boot, exactly
29
+ # like the dsh-base rows for sessions/storages.
30
+ - id: skill-filesystem
31
+ config:
32
+ agentsHome: !!js dshHomePath('agents')
33
+
34
+ # T31c: the session-title LLM request fires in parallel with the first turn
35
+ # (dsh-session-title onMainRequest); on a single-slot local server it steals
36
+ # ~1.6 s from the first answer (BOS timing proxy). BRUINE_TITLE_LLM=off — set
37
+ # by the launcher when the default route is private/localhost — disables the
38
+ # provider row; dsh then titles from the first prompt with NO request. Cloud
39
+ # routes keep their LLM title.
40
+ - id: session-title-llm
41
+ disabled: !!js process.env.BRUINE_TITLE_LLM === 'off'
42
+
43
+ # T16: safety comes from bruine's own gate, not dsh's sandbox — commands really
44
+ # run on the machine. The sandbox is left fully open; bruine's pre-tool gate is
45
+ # the only permission layer, so dsh escalation never fires.
46
+ - id: sandbox-policy
47
+ config:
48
+ mode: danger-full-access
49
+ workspaceRoot: !!js process.cwd()
50
+
51
+ # Pin approvals to "ask": when bruine's gate returns `ask`, the approval service
52
+ # must actually reach bruine-approval's select.
53
+ - id: approval
54
+ config:
55
+ policy: ask
56
+
57
+ # The presets service validates that the composed (sandbox, approval) pair
58
+ # matches a preset: bruine-ask = sandbox off + approvals reach bruine's select.
59
+ - id: permission
60
+ name: '@deepseek-ai/dsh-permission-presets'
61
+ config:
62
+ defaultPreset: bruine-ask
63
+ presets:
64
+ read-only:
65
+ sandbox: read-only
66
+ approval: ask
67
+ name: Read Only
68
+ description: Files are read-only for tools.
69
+ workspace-write:
70
+ sandbox: workspace-write
71
+ approval: ask
72
+ name: Workspace Edit
73
+ description: Allow edits within the workspace.
74
+ danger-full-access:
75
+ sandbox: danger-full-access
76
+ approval: never
77
+ name: No Sandbox
78
+ description: No sandbox.
79
+ bruine-ask:
80
+ sandbox: danger-full-access
81
+ approval: ask
82
+ name: bruine gate
83
+ description: Sandbox off; bruine's own gate asks before risky actions.
84
+
85
+ # Keep the model-facing catalog fixed for each session. The launcher reads
86
+ # bruine.json before dsh starts; an absent tools setting selects lean.
87
+ - id: plan-mode
88
+ disabled: !!js process.env.BRUINE_TOOLS !== 'full'
89
+ - id: tool-subagent-control
90
+ disabled: !!js process.env.BRUINE_TOOLS !== 'full'
91
+ - id: tool-subagent-list-agents
92
+ disabled: !!js process.env.BRUINE_TOOLS !== 'full'
93
+ - id: tool-subagent-fork
94
+ disabled: !!js process.env.BRUINE_TOOLS !== 'full'
95
+
96
+ # A sub-agent run in the background is a plain job in the lean catalog: job_output
97
+ # reads it, job_kill stops it and /tasks lists it. The persistent mode needs
98
+ # send_message and list_agents, which only the full catalog mounts; with them off a
99
+ # model has no way to follow the child and polls job_output with an id the job
100
+ # registry has never heard of ("unknown job"), then does the work itself. One-shot
101
+ # also waits for the child by default, which is what a small model expects.
102
+ # (A config here replaces the base row's whole config, so the provider and the tool
103
+ # name are repeated.)
104
+ - id: tool-subagent
105
+ config:
106
+ provider: spawn
107
+ toolName: subagent
108
+ backgroundMode: !!js (process.env.BRUINE_TOOLS === 'full' && 'continuable') || 'one-shot'
109
+ - id: tool-workflow
110
+ disabled: !!js process.env.BRUINE_TOOLS !== 'full'
111
+ - id: tool-goal
112
+ disabled: !!js process.env.BRUINE_TOOLS !== 'full'
113
+ - id: tool-ralph
114
+ disabled: !!js process.env.BRUINE_TOOLS !== 'full'
115
+
116
+ - insert:
117
+ - id: bruine-startup
118
+ name: '@ziamana/bruine/startup'
119
+
120
+ - id: bruine-repl
121
+ name: '@ziamana/bruine/repl'
122
+ inject: [bruineStartup]
123
+ disabled: !!js process.env.BRUINE_HEADLESS === '1'
124
+
125
+ - id: bruine-headless
126
+ name: '@ziamana/bruine/headless'
127
+ inject: [bruineStartup]
128
+ disabled: !!js process.env.BRUINE_HEADLESS !== '1'
129
+
130
+ - id: bruine-render
131
+ name: '@ziamana/bruine/render'
132
+
133
+ - id: bruine-herdr
134
+ name: '@ziamana/bruine/herdr'
135
+ disabled: !!js process.env.BRUINE_HEADLESS === '1'
136
+
137
+ - id: bruine-approval
138
+ name: '@ziamana/bruine/approval'
139
+
140
+ - id: bruine-modes
141
+ name: '@ziamana/bruine/modes'
142
+
143
+ - id: bruine-web-search
144
+ name: '@ziamana/bruine/web-search'
145
+
146
+ # MCP servers as tools (mcpServers in bruine.json, and the project's approved
147
+ # .mcp.json): one dsh MCP client per server, all connected before the first
148
+ # turn so the tools array never changes inside a session.
149
+ - id: bruine-mcp
150
+ name: '@ziamana/bruine/mcp'
151
+
152
+ # An adaptive silence budget on every model stream: longer while the model thinks hard or
153
+ # writes a large file, longer on every retry, and a retryable TIMEOUT when it runs out.
154
+ - id: bruine-silence
155
+ name: '@ziamana/bruine/silence'
156
+
157
+ - id: tool-ask-user
158
+ name: '@deepseek-ai/dsh-tool-ask-user'
159
+
160
+ - id: tool-str-replace-editor
161
+ name: '@deepseek-ai/dsh-tool-str-replace-editor'
162
+ disabled: !!js process.env.BRUINE_TOOLS !== 'full'
163
+
164
+ - id: tool-present
165
+ name: '@deepseek-ai/dsh-tool-present'
166
+ disabled: !!js process.env.BRUINE_TOOLS !== 'full'