miii-cli 0.2.4 → 0.2.6

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/README.md CHANGED
@@ -1,42 +1,53 @@
1
1
  # miii
2
2
 
3
- > A local AI coding assistant that actually works. No cloud. No Python. No API keys required.
3
+ > Claude Code-level terminal AI runs on your machine, zero cloud required.
4
4
 
5
5
  ```
6
- ╭──────────────────────────────────────────────────────────────╮
7
- │ miii — Claude Code-level workflows, fully offline
8
- ╰──────────────────────────────────────────────────────────────╯
6
+ ╭──────────────────────────────────────────────────────────────────────╮
7
+ │ miii v0.2.5
8
+ │ model: qwen2.5-coder:7b │
9
+ ├──────────────────────────────────────────────────────────────────────┤
10
+ │ ✦ cross-referencing vibes… 12s │
11
+ │ ⚙ running patch_file… │
12
+ │ ⚙ running run_tests… │
13
+ ├──────────────────────────────────────────────────────────────────────┤
14
+ │ ❯ █ │
15
+ │ @ file / command enter send ctrl+c exit │
16
+ ╰──────────────────────────────────────────────────────────────────────╯
9
17
  ```
10
18
 
11
19
  [![npm version](https://img.shields.io/npm/v/miii-cli)](https://www.npmjs.com/package/miii-cli)
20
+ [![npm downloads](https://img.shields.io/npm/dm/miii-cli)](https://www.npmjs.com/package/miii-cli)
12
21
  [![license](https://img.shields.io/npm/l/miii-cli)](LICENSE)
13
22
  [![node](https://img.shields.io/node/v/miii-cli)](https://nodejs.org)
14
23
 
15
- ![miii demo](mii-cli.gif)
16
-
17
24
  ---
18
25
 
19
- ## Why miii exists
26
+ ## What is this
20
27
 
21
- Every local AI coding tool is either too clunky to set up, requires cloud APIs, or has terminal output that's genuinely painful to read. miii is what happens when you build a local coding assistant that takes UX seriously — real Ink-based terminal UI, automatic git context, multi-file refactors with task queues, and a context compactor that keeps local models on-track.
28
+ A local AI coding assistant with the workflow depth of Claude Code file editing, multi-file refactors, test running, git integration, web search except it runs entirely on your machine using Ollama, or any OpenAI-compatible API.
22
29
 
23
- Your code never leaves your machine.
30
+ No Python. No cloud. No API key required to start. 176K bundle.
24
31
 
25
32
  ---
26
33
 
27
- ## What makes it different
34
+ ## Why it beats the alternatives
28
35
 
29
36
  | Feature | miii | aider | shell_gpt | open-interpreter |
30
- |---|---|---|---|---|
31
- | Ink terminal UI | ✅ | ❌ | ❌ | ❌ |
37
+ |---|:---:|:---:|:---:|:---:|
38
+ | Ink terminal UI (not raw text) | ✅ | ❌ | ❌ | ❌ |
32
39
  | Zero Python | ✅ | ❌ | ❌ | ❌ |
33
- | Auto git context | ✅ | ✅ | ❌ | ❌ |
34
- | Context compaction | ✅ | ✅ | ❌ | ❌ |
40
+ | Auto git context injection | ✅ | ✅ | ❌ | ❌ |
35
41
  | Multi-file refactor queue | ✅ | partial | ❌ | ❌ |
36
- | `.miiiignore` | ✅ | ✅ | ❌ | ❌ |
37
- | Session persistence | ✅ | ❌ | ❌ | ❌ |
42
+ | Context compaction (keeps local models on-track) | ✅ | ✅ | ❌ | ❌ |
43
+ | Auto-runs tests after file edits | ✅ | ❌ | ❌ | ❌ |
44
+ | Web search + extract (Tavily) | ✅ | ❌ | ❌ | partial |
45
+ | npm skill plugin system | ✅ | ❌ | ❌ | ❌ |
38
46
  | Planning mode | ✅ | ❌ | ❌ | ❌ |
39
- | Bundle size | 176K | ~50MB | ~40MB | ~100MB |
47
+ | Named sessions + persistence | | | | |
48
+ | `.miiiignore` | ✅ | ✅ | ❌ | ❌ |
49
+ | Live model switching mid-session | ✅ | ❌ | ❌ | ❌ |
50
+ | Bundle size | **176K** | ~50MB | ~40MB | ~100MB |
40
51
 
41
52
  ---
42
53
 
@@ -46,24 +57,21 @@ Your code never leaves your machine.
46
57
  npm install -g miii-cli
47
58
  ```
48
59
 
49
- **Requirements:** Node.js 18+ and [Ollama](https://ollama.com) (or any OpenAI-compatible API)
60
+ **Requires:** Node.js 18+ and [Ollama](https://ollama.com)
61
+
62
+ Or any OpenAI-compatible API — see [configuration](#configuration).
50
63
 
51
64
  ---
52
65
 
53
66
  ## Quick start
54
67
 
55
68
  ```bash
56
- # Start Ollama
57
69
  ollama serve
58
-
59
- # Pull a model
60
70
  ollama pull qwen2.5-coder:7b
61
-
62
- # Launch miii
63
71
  miii
64
72
  ```
65
73
 
66
- A model picker opens on launch. Select a model and start coding.
74
+ Model picker opens on launch. Select a model. Start coding.
67
75
 
68
76
  ```bash
69
77
  miii # default session
@@ -72,11 +80,17 @@ miii --session myproject # named session
72
80
  miii -s work -m codellama # short flags
73
81
  ```
74
82
 
83
+ miii checks for updates on startup and lets you know when a new version is available:
84
+
85
+ ```
86
+ ├── miii v0.2.5 → v0.2.6 available run: npm install -g miii-cli ───┤
87
+ ```
88
+
75
89
  ---
76
90
 
77
91
  ## Auto git context
78
92
 
79
- miii automatically detects changed files via `git status` and injects their contents into the model's context — no `@file` needed for files you're actively working on.
93
+ miii watches `git status` and silently injects your changed files into context — before you even type `@file`.
80
94
 
81
95
  ```
82
96
  ❯ fix the type error in the auth middleware
@@ -84,201 +98,228 @@ miii automatically detects changed files via `git status` and injects their cont
84
98
  [auto-loaded 3 changed file(s)]
85
99
  ```
86
100
 
87
- Only fires for code-related messages. Pure questions ("what is a closure?") skip the git scan entirely. Deduped — same files don't re-inject unless they change.
101
+ Smart enough to skip it for non-code questions. Deduped — same files don't re-inject unless they change on disk. Disable per-project:
88
102
 
89
- Disable per-project:
90
103
  ```json
91
104
  { "gitContext": false }
92
105
  ```
93
106
 
94
107
  ---
95
108
 
96
- ## File context with `@`
109
+ ## Multi-file refactor
97
110
 
98
- Type `@` to fuzzy-search and inject any project file:
111
+ One goal, executed across the whole codebase:
99
112
 
100
113
  ```
101
- review the auth logic in @src/auth/middleware.ts
102
- ❯ what does @src/utils/parser.ts return when input is empty?
114
+ /refactor extract all database queries into a repository layer
115
+ /refactor rename UserService to AccountService everywhere
116
+ /refactor add input validation to all API route handlers
103
117
  ```
104
118
 
105
- Automatically excluded: `node_modules`, `dist`, `.git`, lock files, binaries, images.
119
+ How it works: model plans which files change → reads all in parallel → per-file LLM call with isolated context → writes queued changes → runs tests. Each file gets its own fresh context so local models never lose the thread.
106
120
 
107
121
  ---
108
122
 
109
- ## `.miiiignore`
123
+ ## Auto-test after edits
110
124
 
111
- Create `.miiiignore` in your project root to exclude files from `@` picker and auto-context:
125
+ Every time the model edits a file, miii runs your test suite automatically and feeds results back into the conversation — without you asking.
112
126
 
113
127
  ```
114
- # .miiiignore
115
- secrets/
116
- *.generated.ts
117
- fixtures/
118
- *.sql
128
+ running run_tests…
129
+ ● src/auth/middleware.test.ts — 2 tests failed
119
130
  ```
120
131
 
121
- Supports exact names, relative paths, and `*.ext` glob patterns.
132
+ Model sees the failures and fixes them on the next hop. Supports jest, vitest, mocha auto-detected from `package.json`.
122
133
 
123
134
  ---
124
135
 
125
- ## Git integration
136
+ ## Web search
126
137
 
127
- Full git workflow from the terminal:
138
+ Add a Tavily key and the model can search the web and scrape pages as tools, mid-conversation:
128
139
 
140
+ ```bash
141
+ /tavily-key tvly-your-key-here
129
142
  ```
130
- /git status show working tree
131
- /git diff unstaged changes
132
- /git diff --staged staged changes
133
- /git log recent commits
134
- /git review AI reviews current changes for bugs + improvements
135
- /git branch list branches
136
- /git commit <msg> stage all and commit
143
+
144
+ Get a free key at [tavily.com](https://tavily.com) — 1000 free searches/month.
145
+
146
+ ```
147
+ what's the latest breaking change in React 19?
148
+ find the docs for the Hono.js routing API and implement it here
149
+ search for the error: "Cannot read properties of undefined (reading 'map')"
137
150
  ```
138
151
 
139
- The model also has access to git tools directly it can check status, read diffs, and commit as part of autonomous workflows.
152
+ Tools available to the model: `web_search` (semantic search, configurable depth) and `web_extract` (scrape and summarize any URL). API key stored at `~/.config/miii/tavily.key` with mode 600.
140
153
 
141
154
  ---
142
155
 
143
- ## Multi-file refactor
156
+ ## npm skill ecosystem
144
157
 
145
- Describe a goal, miii plans and executes across multiple files with isolated context per file:
158
+ Write your own:
146
159
 
147
- ```
148
- /refactor extract all database queries into a repository layer
149
- /refactor rename UserService to AccountService everywhere
150
- /refactor add input validation to all API route handlers
160
+ ```typescript
161
+ // miii-skill-mytool/index.js
162
+ export default {
163
+ name: 'mytool',
164
+ ns: 'custom',
165
+ description: 'does something useful',
166
+ execute: async (args, ctx) => {
167
+ ctx.setSystemPrompt(ctx.getSystemPrompt() + '\nExtra context here.')
168
+ return 'skill activated'
169
+ }
170
+ }
151
171
  ```
152
172
 
153
- Uses a priority task queue (P0=blocking, P1=reads parallel, P2=writes sequential, P3=verify). Each file gets its own fresh context the local model never loses the thread on large codebases.
173
+ Markdown skills still work too drop a `.md` file in `~/.config/miii/skills/` and it becomes a `/command` instantly.
154
174
 
155
175
  ---
156
176
 
157
177
  ## Planning mode
158
178
 
159
- Structured step-by-step planning before writing any code:
179
+ Think before you code:
160
180
 
161
181
  ```
162
182
  /plan add OAuth2 to this Express app
163
183
  /plan refactor the frontend to use React Query
164
184
  ```
165
185
 
166
- In planning mode:
186
+ Switches the model into a structured planning mode — no code, just questions, breakdowns, and concrete steps. Then:
187
+
167
188
  ```
168
- /plan:next suggest next concrete steps
169
- /plan:breakdown break topic into subtasks
189
+ /plan:next next concrete steps
190
+ /plan:breakdown break into subtasks
170
191
  /plan:review critique the plan so far
171
- /plan:done exit planning mode
192
+ /plan:done exit, go build
172
193
  ```
173
194
 
174
195
  ---
175
196
 
176
- ## Built-in commands
197
+ ## File context with `@`
177
198
 
178
- Type `/` to open the command palette with fuzzy search.
199
+ Type `@` anywhere to fuzzy-search and inject any project file into context:
179
200
 
180
- | Command | Description |
181
- |---|---|
182
- | `/model <name>` | Switch model mid-session no restart needed |
183
- | `/models` | Open model picker, pull new Ollama models |
184
- | `/session <name>` | Switch to or create a named session |
185
- | `/sessions` | List all sessions with message counts |
186
- | `/new` | Start a fresh auto-named session |
187
- | `/clear` | Clear current session history |
188
- | `/plan [topic]` | Enter planning mode |
189
- | `/refactor <goal>` | Multi-file AI refactor |
190
- | `/git <subcommand>` | Git commands (see above) |
191
- | `/list` | Show all loaded skills |
192
- | `/exit` | Exit miii |
201
+ ```
202
+ ❯ review the auth logic in @src/auth/middleware.ts
203
+ what does @src/utils/parser.ts return when input is empty?
204
+ ```
205
+
206
+ Files auto-excluded: `node_modules`, `dist`, `.git`, lock files, binaries, images.
193
207
 
194
208
  ---
195
209
 
196
- ## Built-in tools
210
+ ## `.miiiignore`
197
211
 
198
- The model calls these autonomously reads, writes, edits, runs, tests, and commits on its own.
212
+ Exclude files from `@` fuzzy picker and git auto-context:
199
213
 
200
- | Tool | Description |
201
- |---|---|
202
- | `read_file` | Read any file |
203
- | `list_files` | List directory contents |
204
- | `create_file` | Create a new file (fails if exists) |
205
- | `edit_file` | Create or fully rewrite a file |
206
- | `patch_file` | Targeted string replacement — throws if match is ambiguous |
207
- | `delete_file` | Delete a file |
208
- | `move_file` | Move or rename a file or directory |
209
- | `create_folder` | Create directory with parents |
210
- | `run_command` | Run shell command in cwd (30s timeout) |
211
- | `run_tests` | Run test suite, auto-detects jest/vitest/mocha from package.json |
212
- | `git_status` | Working tree status |
213
- | `git_diff` | Unstaged or staged diff (truncated at 8K) |
214
- | `git_log` | Recent commit history |
215
- | `git_commit` | Stage files and commit |
214
+ ```
215
+ # .miiiignore
216
+ secrets/
217
+ *.generated.ts
218
+ fixtures/
219
+ *.sql
220
+ ```
216
221
 
217
- Tool calls chain up to 6 hops — model reads, edits, runs tests, and verifies autonomously.
222
+ Supports exact names, relative paths, and `*.ext` glob patterns.
218
223
 
219
224
  ---
220
225
 
221
- ## Context compaction
226
+ ## Git integration
227
+
228
+ ```
229
+ /git status working tree
230
+ /git diff unstaged changes
231
+ /git diff --staged staged diff
232
+ /git log recent commits (n optional: /git log 20)
233
+ /git review AI reviews current changes for bugs + improvements
234
+ /git branch list branches
235
+ /git commit <msg> stage all and commit
236
+ ```
222
237
 
223
- Local models lose the thread after ~15-20 messages. miii auto-compacts context at threshold: keeps system prompt + original goal + tool result summary + last 6 exchanges. The session history is preserved on disk — only the LLM context window gets trimmed.
238
+ The model also has `git_status`, `git_diff`, `git_log`, `git_commit` as autonomous tools it checks status and commits without being asked.
224
239
 
225
240
  ---
226
241
 
227
- ## Thinking indicator
242
+ ## All built-in tools
228
243
 
229
- ```
230
- ✦ staring into the abyss (it blinked)… [0:12]
244
+ The model calls these autonomously as needed:
231
245
 
232
- running patch_file…
233
- ⚙ running run_tests…
234
- ```
246
+ | Tool | What it does |
247
+ |---|---|
248
+ | `read_file` | Read any file in cwd |
249
+ | `list_files` | List directory, respects `.miiiignore` |
250
+ | `create_file` | Create new file — throws if already exists |
251
+ | `edit_file` | Create or fully rewrite a file |
252
+ | `patch_file` | Targeted string replace — throws on ambiguous match |
253
+ | `delete_file` | Delete a file |
254
+ | `move_file` | Move or rename |
255
+ | `create_folder` | mkdir -p |
256
+ | `run_command` | Shell command, cwd, 30s timeout |
257
+ | `run_tests` | Run test suite (jest/vitest/mocha auto-detected) |
258
+ | `git_status` | Working tree status |
259
+ | `git_diff` | Diff, staged or unstaged, 8K truncated |
260
+ | `git_log` | Commit history |
261
+ | `git_commit` | Stage + commit |
262
+ | `web_search` | Tavily semantic search (requires API key) |
263
+ | `web_extract` | Scrape + summarize URLs (requires API key) |
235
264
 
236
- Phrase rotates every 5 seconds. Tool name updates live as each call fires. Elapsed time shown throughout.
265
+ Chains up to 6 tool hops per response read, edit, test, verify, commit in one shot.
237
266
 
238
267
  ---
239
268
 
240
269
  ## Sessions
241
270
 
242
- Every conversation persists automatically.
271
+ Every conversation persists automatically to disk.
243
272
 
244
273
  ```bash
245
- miii # resumes "default" session
274
+ miii # resumes last session
246
275
  miii --session feature-auth # resumes or creates "feature-auth"
247
276
  ```
248
277
 
249
- Sessions stored at `~/.config/miii/sessions/`. History capped at 100 messages in memory, full history on disk.
278
+ ```
279
+ /session <name> switch to a session (creates if new)
280
+ /sessions list all sessions with message counts
281
+ /new fresh auto-named session
282
+ /clear clear current session
283
+ ```
284
+
285
+ Sessions at `~/.config/miii/sessions/`. History capped at 100 messages in-context, full history on disk. Debounced writes — no I/O on every message.
250
286
 
251
287
  ---
252
288
 
253
- ## Skills
289
+ ## Context compaction
254
290
 
255
- Custom `/` commands via Markdown in `~/.config/miii/skills/`:
291
+ Local models lose coherence around 15–20 messages. miii auto-compacts when context gets long: keeps system prompt + original goal + tool result summary + last 6 exchanges. You keep going without restarting. Session history always preserved on disk — only the LLM window gets trimmed.
256
292
 
257
- ```markdown
258
- ---
259
- name: review
260
- description: review current changes for bugs and improvements
261
293
  ---
262
294
 
263
- Review the code I'm about to share. Look for bugs, edge cases, and improvements.
264
- Be direct and specific. No markdown.
265
- ```
295
+ ## All commands
266
296
 
267
- ```
268
- /review
269
- ```
297
+ Type `/` to open the command palette with fuzzy search.
270
298
 
271
- TypeScript skills with `execute` functions available for programmatic behavior.
299
+ | Command | Description |
300
+ |---|---|
301
+ | `/model <name>` | Switch model mid-session — no restart |
302
+ | `/models` | Model picker, pull new Ollama models |
303
+ | `/session <name>` | Switch or create session |
304
+ | `/sessions` | List all sessions |
305
+ | `/new` | Fresh auto-named session |
306
+ | `/clear` | Clear current history |
307
+ | `/plan [topic]` | Planning mode |
308
+ | `/refactor <goal>` | Multi-file refactor |
309
+ | `/git <sub>` | Git commands |
310
+ | `/skills <sub>` | Install / uninstall / list npm skills |
311
+ | `/tavily-key <key>` | Set web search API key |
312
+ | `/version` | Show current version |
313
+ | `/list` | List all loaded skills |
314
+ | `/exit` | Exit |
272
315
 
273
316
  ---
274
317
 
275
318
  ## Configuration
276
319
 
277
- Config loaded from (in order):
278
- 1. `.miii.json` in current directory
279
- 2. `~/.config/miii/config.json`
320
+ Loaded in order from `.miii.json` (project) → `~/.config/miii/config.json` (global).
280
321
 
281
- **Ollama:**
322
+ **Ollama (default):**
282
323
  ```json
283
324
  {
284
325
  "model": "qwen2.5-coder:7b",
@@ -287,7 +328,7 @@ Config loaded from (in order):
287
328
  }
288
329
  ```
289
330
 
290
- **OpenAI-compatible (LM Studio, vLLM, Groq, Together, etc.):**
331
+ **Any OpenAI-compatible API** (LM Studio, vLLM, Groq, Together, OpenRouter…):
291
332
  ```json
292
333
  {
293
334
  "model": "gpt-4o",
@@ -305,32 +346,34 @@ Config loaded from (in order):
305
346
  "baseUrl": "http://localhost:11434",
306
347
  "apiKey": "",
307
348
  "gitContext": true,
349
+ "tavilyApiKey": "tvly-...",
308
350
  "systemPrompt": "optional override"
309
351
  }
310
352
  ```
311
353
 
312
354
  ---
313
355
 
314
- ## Keybindings
356
+ ## Security
315
357
 
316
- | Key | Action |
358
+ | Threat | Defense |
317
359
  |---|---|
318
- | `enter` | Send message |
319
- | `↑ / ↓` | Navigate command palette or file picker |
320
- | `esc` | Close overlay / abort request |
321
- | `ctrl+c` | Abort current request or exit |
360
+ | Path traversal (OWASP A01) | All file ops restricted to cwd via `guardPath()` |
361
+ | `@file` injection | Refs validated against cwd before reading |
362
+ | Session name injection | Names sanitized to alphanumeric + hyphens |
363
+ | Shell injection (OWASP A03) | `run_command` enforces 30s hard timeout |
364
+ | Config injection (OWASP A08) | Config key whitelist; session data validated as array |
365
+ | API key exposure | Tavily key stored at `~/.config/miii/tavily.key` mode 600 |
322
366
 
323
367
  ---
324
368
 
325
- ## Security
369
+ ## Keybindings
326
370
 
327
- | Issue | Fix |
371
+ | Key | Action |
328
372
  |---|---|
329
- | Path traversal (OWASP A01) | All file operations restricted to cwd via `guardPath()` |
330
- | Path traversal (OWASP A01) | `@file` refs validated against cwd before reading |
331
- | Path traversal (OWASP A01) | Session names sanitized to alphanumeric + hyphens |
332
- | Injection (OWASP A03) | `run_command` enforces 30s execution timeout |
333
- | Insecure deserialization (OWASP A08) | Config whitelists allowed keys; session data validated as array |
373
+ | `enter` | Send |
374
+ | `↑ / ↓` | Navigate command palette or file picker |
375
+ | `esc` | Close overlay / abort in-flight request |
376
+ | `ctrl+c` | Abort current request or exit |
334
377
 
335
378
  ---
336
379
 
@@ -342,20 +385,18 @@ cd miii-cli
342
385
  npm install
343
386
  npm run build
344
387
  npm link
388
+ npm test # 8 integration tests
345
389
  ```
346
390
 
347
391
  ---
348
392
 
349
- ## What's in 0.2.x
350
-
351
- - **Auto git context** — changed files injected automatically, code-gated heuristic
352
- - **`.miiiignore`**per-project file exclusions
353
- - **Multi-file refactor** — macro/micro task queue, isolated context per file
354
- - **Planning mode** — structured `/plan` workflow
355
- - **Git integration** — full git toolkit in commands and model tools
356
- - **`run_tests` tool** — model runs and retries tests autonomously
357
- - **`/model` live switch** — change models mid-session
358
- - **Context compaction** — auto-trim at 18 messages, keep goal + summary + recent
359
- - **Ambiguous patch detection** — `patch_file` throws on multiple matches instead of silently corrupting
360
- - **176K bundle** — removed dead workers, sourcemaps, unused deps (was 468K)
361
- - **Debounced session saves** — writes at most once per 2s, no I/O on every keypress
393
+ ## What's new in 0.2.5
394
+
395
+ - **Web search** — `web_search` + `web_extract` tools powered by Tavily
396
+ - **npm skill ecosystem** install/uninstall `miii-skill-*` packages, write your own
397
+ - **Auto-test after edits** — model runs test suite after every file change, feeds failures back
398
+ - **Live model switching** — `/model <name>` mid-session, no restart
399
+ - **Update check** — startup banner when a new version is available
400
+ - **Hook architecture** — `useSession`, `useModelPicker`, `useRunLoop` for clean internals
401
+ - **Ambiguous patch detection** — `patch_file` throws on multiple matches
402
+ - **176K bundle** — vs ~50MB for the Python alternatives
@@ -0,0 +1,50 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { writeFileSync, unlinkSync, readFileSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { looksCodeRelated } from '../tui/git-context.js';
5
+ import { tools } from '../tools/index.js';
6
+ // patch_file uses guardPath which restricts to CWD — use a local scratch file
7
+ const SCRATCH = join(process.cwd(), '.miii-test-scratch.txt');
8
+ // ─── looksCodeRelated ─────────────────────────────────────────────────────────
9
+ describe('looksCodeRelated', () => {
10
+ it('true: file extension in message', () => {
11
+ expect(looksCodeRelated('fix the bug in auth.ts')).toBe(true);
12
+ });
13
+ it('true: code keyword present', () => {
14
+ expect(looksCodeRelated('refactor the user login function')).toBe(true);
15
+ });
16
+ it('true: backtick token', () => {
17
+ expect(looksCodeRelated('what does `useEffect` do')).toBe(true);
18
+ });
19
+ it('false: too short', () => {
20
+ expect(looksCodeRelated('hi')).toBe(false);
21
+ });
22
+ it('false: plain prose, no code signal', () => {
23
+ expect(looksCodeRelated('what is the weather like in london today')).toBe(false);
24
+ });
25
+ });
26
+ // ─── patch_file ───────────────────────────────────────────────────────────────
27
+ describe('patch_file', () => {
28
+ const patchTool = tools.find(t => t.name === 'patch_file');
29
+ afterEach(() => {
30
+ try {
31
+ unlinkSync(SCRATCH);
32
+ }
33
+ catch { }
34
+ });
35
+ it('applies a unique patch correctly', async () => {
36
+ writeFileSync(SCRATCH, 'hello world\ngoodbye world\n');
37
+ await patchTool.execute({ path: SCRATCH, old: 'hello world', new: 'hello earth' });
38
+ expect(readFileSync(SCRATCH, 'utf-8')).toBe('hello earth\ngoodbye world\n');
39
+ });
40
+ it('throws when old text not found', async () => {
41
+ writeFileSync(SCRATCH, 'hello world\n');
42
+ await expect(patchTool.execute({ path: SCRATCH, old: 'no such text', new: 'x' }))
43
+ .rejects.toThrow('old text not found');
44
+ });
45
+ it('throws on ambiguous match (2+ occurrences)', async () => {
46
+ writeFileSync(SCRATCH, 'hello world\nhello world\n');
47
+ await expect(patchTool.execute({ path: SCRATCH, old: 'hello world', new: 'hi' }))
48
+ .rejects.toThrow('ambiguous');
49
+ });
50
+ });
package/dist/config.js CHANGED
@@ -6,7 +6,7 @@ const defaults = {
6
6
  provider: 'ollama',
7
7
  baseUrl: 'http://localhost:11434',
8
8
  };
9
- const ALLOWED_KEYS = new Set(['model', 'provider', 'baseUrl', 'systemPrompt', 'apiKey']);
9
+ const ALLOWED_KEYS = new Set(['model', 'provider', 'baseUrl', 'systemPrompt', 'apiKey', 'gitContext', 'tavilyApiKey']);
10
10
  export function loadConfig() {
11
11
  const candidates = [
12
12
  join(process.cwd(), '.miii.json'),