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 +193 -152
- package/dist/__tests__/integration.test.js +50 -0
- package/dist/config.js +1 -1
- package/dist/init.js +67 -5
- package/dist/skills/loader.js +65 -1
- package/dist/tavily/client.js +64 -0
- package/dist/tools/index.js +34 -1
- package/dist/tui/InputBar.js +89 -316
- package/dist/tui/components/InputArea.js +3 -0
- package/dist/tui/git-context.js +59 -0
- package/dist/tui/hooks/useModelPicker.js +63 -0
- package/dist/tui/hooks/useRunLoop.js +146 -0
- package/dist/tui/hooks/useSession.js +50 -0
- package/dist/tui/printer.js +43 -5
- package/dist/tui/thinking.js +53 -0
- package/package.json +5 -3
- package/dist/tui/App.js +0 -285
- package/dist/tui/components/MessageList.js +0 -127
- package/dist/workers/context.worker.js +0 -71
- package/dist/workers/spawn.js +0 -17
package/README.md
CHANGED
|
@@ -1,42 +1,53 @@
|
|
|
1
1
|
# miii
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Claude Code-level terminal AI — runs on your machine, zero cloud required.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
|
-
|
|
7
|
-
│ miii
|
|
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
|
[](https://www.npmjs.com/package/miii-cli)
|
|
20
|
+
[](https://www.npmjs.com/package/miii-cli)
|
|
12
21
|
[](LICENSE)
|
|
13
22
|
[](https://nodejs.org)
|
|
14
23
|
|
|
15
|
-

|
|
16
|
-
|
|
17
24
|
---
|
|
18
25
|
|
|
19
|
-
##
|
|
26
|
+
## What is this
|
|
20
27
|
|
|
21
|
-
|
|
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
|
-
|
|
30
|
+
No Python. No cloud. No API key required to start. 176K bundle.
|
|
24
31
|
|
|
25
32
|
---
|
|
26
33
|
|
|
27
|
-
##
|
|
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
|
-
|
|
|
37
|
-
|
|
|
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
|
-
|
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
##
|
|
109
|
+
## Multi-file refactor
|
|
97
110
|
|
|
98
|
-
|
|
111
|
+
One goal, executed across the whole codebase:
|
|
99
112
|
|
|
100
113
|
```
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
123
|
+
## Auto-test after edits
|
|
110
124
|
|
|
111
|
-
|
|
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
|
-
|
|
115
|
-
|
|
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
|
|
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
|
-
##
|
|
136
|
+
## Web search
|
|
126
137
|
|
|
127
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
156
|
+
## npm skill ecosystem
|
|
144
157
|
|
|
145
|
-
|
|
158
|
+
Write your own:
|
|
146
159
|
|
|
147
|
-
```
|
|
148
|
-
/
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
186
|
+
Switches the model into a structured planning mode — no code, just questions, breakdowns, and concrete steps. Then:
|
|
187
|
+
|
|
167
188
|
```
|
|
168
|
-
/plan:next
|
|
169
|
-
/plan:breakdown break
|
|
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
|
|
192
|
+
/plan:done exit, go build
|
|
172
193
|
```
|
|
173
194
|
|
|
174
195
|
---
|
|
175
196
|
|
|
176
|
-
##
|
|
197
|
+
## File context with `@`
|
|
177
198
|
|
|
178
|
-
Type
|
|
199
|
+
Type `@` anywhere to fuzzy-search and inject any project file into context:
|
|
179
200
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
##
|
|
210
|
+
## `.miiiignore`
|
|
197
211
|
|
|
198
|
-
|
|
212
|
+
Exclude files from `@` fuzzy picker and git auto-context:
|
|
199
213
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
222
|
+
Supports exact names, relative paths, and `*.ext` glob patterns.
|
|
218
223
|
|
|
219
224
|
---
|
|
220
225
|
|
|
221
|
-
##
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
233
|
-
|
|
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
|
-
|
|
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
|
|
274
|
+
miii # resumes last session
|
|
246
275
|
miii --session feature-auth # resumes or creates "feature-auth"
|
|
247
276
|
```
|
|
248
277
|
|
|
249
|
-
|
|
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
|
-
##
|
|
289
|
+
## Context compaction
|
|
254
290
|
|
|
255
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
##
|
|
356
|
+
## Security
|
|
315
357
|
|
|
316
|
-
|
|
|
358
|
+
| Threat | Defense |
|
|
317
359
|
|---|---|
|
|
318
|
-
|
|
|
319
|
-
|
|
|
320
|
-
|
|
|
321
|
-
|
|
|
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
|
-
##
|
|
369
|
+
## Keybindings
|
|
326
370
|
|
|
327
|
-
|
|
|
371
|
+
| Key | Action |
|
|
328
372
|
|---|---|
|
|
329
|
-
|
|
|
330
|
-
|
|
|
331
|
-
|
|
|
332
|
-
|
|
|
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.
|
|
350
|
-
|
|
351
|
-
- **
|
|
352
|
-
-
|
|
353
|
-
- **
|
|
354
|
-
- **
|
|
355
|
-
- **
|
|
356
|
-
-
|
|
357
|
-
-
|
|
358
|
-
- **
|
|
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'),
|