miii-agent 0.1.30 โ 0.1.31
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 +58 -11
- package/dist/cli.js +89 -23
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
<h1 align="center">miii</h1>
|
|
1
|
+
<h1 align="center">miii โ Local AI Coding Agent for Your Terminal</h1>
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<strong>
|
|
5
|
-
|
|
6
|
-
Private by default. Free forever.
|
|
4
|
+
<strong>An open-source, offline alternative to Claude Code, Cursor, and GitHub Copilot.</strong><br>
|
|
5
|
+
A private AI pair-programmer in your terminal, powered by Ollama and any local LLM.<br>
|
|
6
|
+
Private by default. Free forever. Works offline.
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
@@ -15,26 +15,41 @@
|
|
|
15
15
|
</p>
|
|
16
16
|
|
|
17
17
|
<p align="center">
|
|
18
|
-
<img src="demo3.gif" alt="miii demo">
|
|
18
|
+
<img src="demo3.gif" alt="miii local AI coding agent terminal demo powered by Ollama">
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<p align="center">
|
|
22
|
+
๐ <strong>100% local</strong> โ your code never leaves your machine ยท
|
|
23
|
+
๐ธ <strong>Free</strong> โ no API keys, no per-token billing ยท
|
|
24
|
+
โก <strong>Offline</strong> โ runs on your own GPU
|
|
19
25
|
</p>
|
|
20
26
|
|
|
21
27
|
---
|
|
22
28
|
|
|
23
|
-
## What is
|
|
29
|
+
## What is miii? โ a local AI coding agent
|
|
24
30
|
|
|
25
31
|
miii lives in your terminal and codes alongside you โ reading files, writing features, running tests, fixing bugs. The twist: it runs on **your** hardware, powered by [Ollama](https://ollama.com) (or any local OpenAI-compatible server like [llama.cpp](https://github.com/ggml-org/llama.cpp) / [LM Studio](https://lmstudio.ai)).
|
|
26
32
|
|
|
27
|
-
Your code never leaves your disk. There's nothing to log in to. Pull a model, type `miii`, go.
|
|
33
|
+
Your code never leaves your disk. There's nothing to log in to. Pull a model, type `miii`, go. It's the open-source, offline answer to cloud coding assistants like Claude Code, Cursor, and GitHub Copilot.
|
|
28
34
|
|
|
29
|
-
##
|
|
35
|
+
## Install (macOS, Linux, Windows)
|
|
30
36
|
|
|
31
37
|
**macOS / Linux:**
|
|
32
38
|
|
|
33
39
|
```bash
|
|
34
40
|
ollama pull qwen2.5-coder:14b # any coding model works
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Which model should I use?**
|
|
44
|
+
- **Low VRAM (8GB):** `qwen2.5-coder:7b` (Fast, capable)
|
|
45
|
+
- **Mid VRAM (16-24GB):** `qwen2.5-coder:14b` (Sweet spot)
|
|
46
|
+
- **High VRAM (48GB+):** `qwen2.5-coder:32b` (Powerhouse)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
35
49
|
curl -fsSL https://raw.githubusercontent.com/maruakshay/miii-cli/main/install.sh | sh
|
|
36
50
|
miii
|
|
37
51
|
```
|
|
52
|
+
*(The installer downloads the pre-compiled binary and adds it to your local path)*
|
|
38
53
|
|
|
39
54
|
**Windows (PowerShell):**
|
|
40
55
|
|
|
@@ -77,7 +92,7 @@ miii --version # what you're running
|
|
|
77
92
|
Opt out of background updates by adding `"autoUpdate": false` to `~/.miii/config.json`,
|
|
78
93
|
or re-run the install script (`curl โฆ | sh`) any time to update by hand.
|
|
79
94
|
|
|
80
|
-
## Why local-first?
|
|
95
|
+
## Why local-first? Private, free, offline
|
|
81
96
|
|
|
82
97
|
Most "AI coding tools" are just wrappers around a cloud API โ slow, metered, and they ship your private codebase to someone else's server.
|
|
83
98
|
|
|
@@ -89,13 +104,16 @@ Most "AI coding tools" are just wrappers around a cloud API โ slow, metered, a
|
|
|
89
104
|
| Offline | No | Yes |
|
|
90
105
|
| Latency | Network + queue | Your GPU only |
|
|
91
106
|
|
|
92
|
-
It doesn't just chat, either โ it
|
|
107
|
+
It doesn't just chat, either โ it follows a **Plan $\rightarrow$ Act $\rightarrow$ Observe** loop:
|
|
108
|
+
1. **Plan**: Decomposes the problem into a sequence of concrete steps.
|
|
109
|
+
2. **Act**: Calls the necessary tools to gather context or modify code.
|
|
110
|
+
3. **Observe**: Verifies the result and adjusts the plan until the goal is met.
|
|
93
111
|
|
|
94
112
|
## Five letters, five ideas
|
|
95
113
|
|
|
96
114
|
**s**mall ยท **s**imple ยท **s**mart ยท **s**trategic ยท **s**emantic โ a tiny codebase you can read in an afternoon, no config ceremony, plans before it acts, and operates on the *meaning* of your code, not blind text matching.
|
|
97
115
|
|
|
98
|
-
##
|
|
116
|
+
## Features
|
|
99
117
|
|
|
100
118
|
- **๐งช `miii doctor`** โ not every local model can drive an agent. Doctor runs your models through real engineering tasks and tells you which ones actually deliver.
|
|
101
119
|
```bash
|
|
@@ -189,6 +207,15 @@ The model pages through the middle with ranged `read_file` reads. Spill files ar
|
|
|
189
207
|
<details>
|
|
190
208
|
<summary><strong>Development</strong></summary>
|
|
191
209
|
|
|
210
|
+
**Project Architecture:**
|
|
211
|
+
```text
|
|
212
|
+
src/
|
|
213
|
+
โโโ agent/ # The core reasoning loop
|
|
214
|
+
โโโ tools/ # Implementation of read/write/bash
|
|
215
|
+
โโโ terminal/ # UI and input handling
|
|
216
|
+
โโโ config/ # Settings and provider logic
|
|
217
|
+
```
|
|
218
|
+
|
|
192
219
|
```bash
|
|
193
220
|
git clone https://github.com/maruakshay/miii-cli.git
|
|
194
221
|
cd miii-cli
|
|
@@ -211,6 +238,26 @@ npm run build && npm link # restore later with: npm install -g miii-agent
|
|
|
211
238
|
|
|
212
239
|
---
|
|
213
240
|
|
|
241
|
+
## FAQ
|
|
242
|
+
|
|
243
|
+
**Does miii work without internet?**
|
|
244
|
+
Yes. Once you've pulled a model with Ollama, miii runs fully offline. No network calls, no account, no cloud.
|
|
245
|
+
|
|
246
|
+
**Is my code sent anywhere?**
|
|
247
|
+
No. Every file read, edit, and model inference happens on your machine. Your codebase never leaves your disk.
|
|
248
|
+
|
|
249
|
+
**Which model is best for coding?**
|
|
250
|
+
Depends on VRAM: `qwen2.5-coder:7b` (8GB), `qwen2.5-coder:14b` (16โ24GB, the sweet spot), `qwen2.5-coder:32b` (48GB+). Run `miii doctor` to grade your installed models on real engineering tasks.
|
|
251
|
+
|
|
252
|
+
**How is miii different from Claude Code, Cursor, or GitHub Copilot?**
|
|
253
|
+
Claude Code, Cursor, and Copilot are cloud services โ metered, account-gated, and they ship your code to a third-party server. miii is open-source, free, and runs entirely on your hardware. Same terminal-agent workflow as Claude Code, but on your own local model.
|
|
254
|
+
|
|
255
|
+
**How is it different from Continue.dev?**
|
|
256
|
+
Continue.dev is an IDE extension. miii is a standalone terminal agent โ no editor required โ with a Plan โ Act โ Observe loop, permission-gated tools, and lossless output spill built in.
|
|
257
|
+
|
|
258
|
+
**Do I need a GPU?**
|
|
259
|
+
No, but it helps. Smaller models run on CPU; a GPU makes larger models fast enough for real work.
|
|
260
|
+
|
|
214
261
|
## Status
|
|
215
262
|
|
|
216
263
|
**MVP.** Core agent loop is stable; actively refining tool execution, streaming, and the permission model. PRs welcome โ fork it, break it, improve it.
|
package/dist/cli.js
CHANGED
|
@@ -1445,6 +1445,7 @@ Ask in a numbered list. One round of questions per turn. Then wait.
|
|
|
1445
1445
|
- After a tool result, move directly to the next tool call or the final answer. Do not restate what the previous tool did.
|
|
1446
1446
|
- Every tool call MUST carry a complete, valid arguments object: all required fields present, correct types, valid JSON. Never emit a call with empty, partial, or placeholder arguments.
|
|
1447
1447
|
- WRONG (leaks as text, nothing runs): writing \`call:some_tool{"foo":"bar"}\` or a fenced JSON block in your reply. RIGHT: emit it as a native function call with a full arguments object.
|
|
1448
|
+
- Batch independent tool calls in a SINGLE turn \u2014 parallel, not serial. If two reads, greps, or searches do not depend on each other's output, emit them together. Only serialize when a later call needs an earlier result.
|
|
1448
1449
|
|
|
1449
1450
|
# Tools
|
|
1450
1451
|
You have access to the following tools. Call them via the function-calling interface.
|
|
@@ -1454,14 +1455,33 @@ ${toolLines}
|
|
|
1454
1455
|
- When you need to act on the filesystem or run a command, emit a tool call.
|
|
1455
1456
|
- After each tool result, decide: more tool calls, or a final plain-text answer.
|
|
1456
1457
|
- Stop emitting tool calls when GOAL is met. Reply with a concise plain-text final message confirming CRITERION is satisfied.
|
|
1458
|
+
- After the work is done, always close by asking the user what they want to do next \u2014 a brief, specific prompt (offer the most likely follow-ups when obvious). One line, no filler.
|
|
1457
1459
|
|
|
1458
1460
|
# Rules
|
|
1459
1461
|
- Always read a file before updating it. Never edit, overwrite, or create-over a file you have not read first this turn.
|
|
1460
1462
|
- Prefer editing existing files over creating new ones.
|
|
1463
|
+
- To change an existing file, use edit_file with a small, targeted old_str/new_str diff \u2014 never rewrite the whole file with write_file. Reserve write_file for brand-new files or small ones. A full-file write_file on a large file risks getting cut off at the output token limit mid-write; a targeted edit_file stays small and avoids that.
|
|
1464
|
+
- When a new file's content is large, create it with write_file for the first portion, then append the rest with successive edit_file calls. Keep every write small.
|
|
1461
1465
|
- For edit_file, make old_str unique by including surrounding context, or set replace_all to change every occurrence.
|
|
1462
1466
|
- Never invent file paths. Read, glob, or grep before editing.
|
|
1463
1467
|
- No empty filler or robotic boilerplate. A brief, genuine warm touch (see Tone and voice) is welcome; hollow pleasantries and reflexive apologies are not.
|
|
1464
1468
|
|
|
1469
|
+
# Scope discipline
|
|
1470
|
+
- Do ONLY what the user asked. No unrequested refactors, renames, reformatting, or "while I'm here" edits.
|
|
1471
|
+
- If you spot an unrelated issue worth fixing, mention it in your final message \u2014 do not fix it unprompted.
|
|
1472
|
+
- Touch the fewest files needed. A one-line request gets a one-line change, not a redesign.
|
|
1473
|
+
|
|
1474
|
+
# Secrets and safety
|
|
1475
|
+
- Never print, log, or echo secrets, API keys, tokens, passwords, or \`.env\` values. Redact them if you must reference one.
|
|
1476
|
+
- Never write credentials into source, commits, or output. If a secret is needed, read it from the environment or config.
|
|
1477
|
+
- Do not exfiltrate file contents to external services without the user asking.
|
|
1478
|
+
|
|
1479
|
+
# Git and commits
|
|
1480
|
+
- Do NOT commit, push, or create branches/PRs unless the user explicitly asks.
|
|
1481
|
+
- When asked to commit: never commit on the main branch \u2014 branch first. Stage only files relevant to the change; never blanket \`git add -A\` without checking what it sweeps in.
|
|
1482
|
+
- Write a concise commit message stating what changed and why. Do not add credentials or generated noise.
|
|
1483
|
+
- Never force-push, rebase shared history, or run destructive git commands without explicit confirmation.
|
|
1484
|
+
|
|
1465
1485
|
# Context discipline
|
|
1466
1486
|
- read_file returns line numbers and accepts offset/limit. For large files, grep or glob to the relevant region first, then read only that range with offset/limit. Do not read a whole large file when you need a few functions \u2014 it wastes the context window.
|
|
1467
1487
|
- Reference code by the line numbers read_file returns.
|
|
@@ -2298,7 +2318,7 @@ var InputBar = memo(function InputBar2({ input, caret, disabled, processingLabel
|
|
|
2298
2318
|
const [frame, setFrame] = useState(0);
|
|
2299
2319
|
useEffect(() => {
|
|
2300
2320
|
if (!disabled) return;
|
|
2301
|
-
const t = setInterval(() => setFrame((f) => (f + 1) % SPIN.length),
|
|
2321
|
+
const t = setInterval(() => setFrame((f) => (f + 1) % SPIN.length), 200);
|
|
2302
2322
|
return () => clearInterval(t);
|
|
2303
2323
|
}, [disabled]);
|
|
2304
2324
|
return /* @__PURE__ */ jsx2(
|
|
@@ -3347,14 +3367,26 @@ function clipTail(rendered, max) {
|
|
|
3347
3367
|
if (lines.length <= max) return { text: rendered, clipped: 0 };
|
|
3348
3368
|
return { text: lines.slice(-max).join("\n"), clipped: lines.length - max };
|
|
3349
3369
|
}
|
|
3370
|
+
var ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
3371
|
+
function stripAnsi(s) {
|
|
3372
|
+
return s.replace(ANSI_RE, "");
|
|
3373
|
+
}
|
|
3374
|
+
function visualHeight(text, width) {
|
|
3375
|
+
const w = Math.max(1, width);
|
|
3376
|
+
let rows = 0;
|
|
3377
|
+
for (const line of text.split("\n")) {
|
|
3378
|
+
rows += Math.max(1, Math.ceil(stripAnsi(line).length / w));
|
|
3379
|
+
}
|
|
3380
|
+
return rows;
|
|
3381
|
+
}
|
|
3350
3382
|
function clipTailVisual(content, maxRows, width) {
|
|
3351
3383
|
const w = Math.max(1, width);
|
|
3352
3384
|
const lines = content.split("\n");
|
|
3353
|
-
const
|
|
3385
|
+
const visualRows2 = (line) => Math.max(1, Math.ceil(stripAnsi(line).length / w));
|
|
3354
3386
|
let rows = 0;
|
|
3355
3387
|
let start = lines.length;
|
|
3356
3388
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3357
|
-
const h =
|
|
3389
|
+
const h = visualRows2(lines[i]);
|
|
3358
3390
|
if (rows + h > maxRows && start < lines.length) break;
|
|
3359
3391
|
rows += h;
|
|
3360
3392
|
start = i;
|
|
@@ -3367,9 +3399,20 @@ function liveFrameRows() {
|
|
|
3367
3399
|
return Math.max(6, rows - 8);
|
|
3368
3400
|
}
|
|
3369
3401
|
var COLLAPSED_LINES = 3;
|
|
3402
|
+
function visualRows(text, width, cap) {
|
|
3403
|
+
const w = Math.max(1, width);
|
|
3404
|
+
let rows = 0;
|
|
3405
|
+
const lines = text.split("\n");
|
|
3406
|
+
for (const line of lines) {
|
|
3407
|
+
rows += Math.max(1, Math.ceil(line.length / w));
|
|
3408
|
+
if (rows >= cap) return cap;
|
|
3409
|
+
}
|
|
3410
|
+
return rows;
|
|
3411
|
+
}
|
|
3370
3412
|
function estimateToolRows(use, result) {
|
|
3371
3413
|
const input = use.input ?? {};
|
|
3372
3414
|
const noErr = !result?.is_error;
|
|
3415
|
+
const w = contentWidth();
|
|
3373
3416
|
if (use.name === "write_file" && noErr) {
|
|
3374
3417
|
const total = countLines(String(input.content ?? ""));
|
|
3375
3418
|
const shown = Math.min(total, COLLAPSED_LINES);
|
|
@@ -3385,10 +3428,10 @@ function estimateToolRows(use, result) {
|
|
|
3385
3428
|
const lines = (result.content ?? "").split("\n");
|
|
3386
3429
|
const multi = (use.name === "run_bash" || use.name === "grep" || use.name === "glob" || result.is_error) && lines.length > 1;
|
|
3387
3430
|
if (multi) {
|
|
3388
|
-
const
|
|
3389
|
-
rows += 1 +
|
|
3431
|
+
const shownLines = lines.slice(0, COLLAPSED_LINES).join("\n");
|
|
3432
|
+
rows += 1 + visualRows(shownLines, w, COLLAPSED_LINES * 4) + (lines.length > COLLAPSED_LINES ? 1 : 0);
|
|
3390
3433
|
} else {
|
|
3391
|
-
rows +=
|
|
3434
|
+
rows += visualRows(lines[0] ?? "", w, 4);
|
|
3392
3435
|
}
|
|
3393
3436
|
}
|
|
3394
3437
|
return rows;
|
|
@@ -3422,7 +3465,7 @@ function ThinkingBlock({ content }) {
|
|
|
3422
3465
|
const [frame, setFrame] = useState2(0);
|
|
3423
3466
|
const visible = useThinkingVisible();
|
|
3424
3467
|
useEffect2(() => {
|
|
3425
|
-
const t = setInterval(() => setFrame((f) => (f + 1) % FRAMES.length),
|
|
3468
|
+
const t = setInterval(() => setFrame((f) => (f + 1) % FRAMES.length), 100);
|
|
3426
3469
|
return () => clearInterval(t);
|
|
3427
3470
|
}, []);
|
|
3428
3471
|
const label = "thinking";
|
|
@@ -3801,7 +3844,8 @@ function ChatView({
|
|
|
3801
3844
|
permissionCursor = 0,
|
|
3802
3845
|
activeToolUses,
|
|
3803
3846
|
activeToolResults,
|
|
3804
|
-
header
|
|
3847
|
+
header,
|
|
3848
|
+
logEpoch = 0
|
|
3805
3849
|
}) {
|
|
3806
3850
|
const empty = messages.length === 0 && !streaming && !thinking && !pendingPermission && !error;
|
|
3807
3851
|
const log = [];
|
|
@@ -3816,8 +3860,12 @@ function ChatView({
|
|
|
3816
3860
|
let streamNode = null;
|
|
3817
3861
|
let streamRows = 0;
|
|
3818
3862
|
if (streaming && streamingContent) {
|
|
3819
|
-
const
|
|
3820
|
-
|
|
3863
|
+
const raw = clipTail(streamingContent, liveBudget);
|
|
3864
|
+
const width = contentWidth();
|
|
3865
|
+
const rendered = clipTailVisual(renderMarkdownStreaming(raw.text), liveBudget, width);
|
|
3866
|
+
const text = rendered.text;
|
|
3867
|
+
const clipped = raw.clipped + rendered.clipped;
|
|
3868
|
+
streamRows = visualHeight(text, width) + (clipped > 0 ? 1 : 0);
|
|
3821
3869
|
streamNode = /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginBottom: 1, children: [
|
|
3822
3870
|
clipped > 0 && /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: `\u2191 ${clipped} more line${clipped === 1 ? "" : "s"} above \u2014 streaming\u2026` }),
|
|
3823
3871
|
/* @__PURE__ */ jsxs12(Box12, { flexDirection: "row", children: [
|
|
@@ -3847,7 +3895,7 @@ function ChatView({
|
|
|
3847
3895
|
] });
|
|
3848
3896
|
}
|
|
3849
3897
|
return /* @__PURE__ */ jsxs12(Fragment2, { children: [
|
|
3850
|
-
/* @__PURE__ */ jsx12(Static, { items: log, children: (item) => item.key === "header" ? /* @__PURE__ */ jsx12(Box12, { children: item.node }, item.key) : /* @__PURE__ */ jsx12(Box12, { marginLeft: 1, children: item.node }, item.key) }),
|
|
3898
|
+
/* @__PURE__ */ jsx12(Static, { items: log, children: (item) => item.key === "header" ? /* @__PURE__ */ jsx12(Box12, { children: item.node }, item.key) : /* @__PURE__ */ jsx12(Box12, { marginLeft: 1, children: item.node }, item.key) }, logEpoch),
|
|
3851
3899
|
/* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginLeft: 1, marginBottom: 1, children: [
|
|
3852
3900
|
empty && /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginBottom: 1, children: [
|
|
3853
3901
|
/* @__PURE__ */ jsx12(Text12, { dimColor: true, children: EMPTY_STATE_TITLE }),
|
|
@@ -4015,8 +4063,8 @@ function useAgentRunner(model, activeCtx) {
|
|
|
4015
4063
|
case "turn-end": {
|
|
4016
4064
|
flushStream(true);
|
|
4017
4065
|
flushThink(true);
|
|
4018
|
-
setStreaming(false);
|
|
4019
4066
|
if (ev.stop_reason === "tool_use") {
|
|
4067
|
+
setStreaming(false);
|
|
4020
4068
|
flushTurn(null);
|
|
4021
4069
|
setThinking(true);
|
|
4022
4070
|
thinkingAcc = "";
|
|
@@ -4288,6 +4336,7 @@ function useKeyboard(opts) {
|
|
|
4288
4336
|
sessions,
|
|
4289
4337
|
setSessions,
|
|
4290
4338
|
setNotice,
|
|
4339
|
+
setLogEpoch,
|
|
4291
4340
|
switchProvider
|
|
4292
4341
|
} = opts;
|
|
4293
4342
|
const {
|
|
@@ -4321,6 +4370,7 @@ function useKeyboard(opts) {
|
|
|
4321
4370
|
setError(null);
|
|
4322
4371
|
setNotice(null);
|
|
4323
4372
|
clearPasteStore();
|
|
4373
|
+
setLogEpoch((n) => n + 1);
|
|
4324
4374
|
}
|
|
4325
4375
|
const effort = cfg.effort ?? "medium";
|
|
4326
4376
|
useInput((char, key) => {
|
|
@@ -4779,6 +4829,7 @@ function App() {
|
|
|
4779
4829
|
sessionIdRef.current = sessionId;
|
|
4780
4830
|
const [sessions, setSessions] = useState5([]);
|
|
4781
4831
|
const [notice, setNotice] = useState5(null);
|
|
4832
|
+
const [logEpoch, setLogEpoch] = useState5(0);
|
|
4782
4833
|
const [input, setInput] = useState5("");
|
|
4783
4834
|
const [caret, setCaret] = useState5(0);
|
|
4784
4835
|
const [paletteCursor, setPaletteCursor] = useState5(0);
|
|
@@ -4899,6 +4950,7 @@ function App() {
|
|
|
4899
4950
|
sessions,
|
|
4900
4951
|
setSessions,
|
|
4901
4952
|
setNotice,
|
|
4953
|
+
setLogEpoch,
|
|
4902
4954
|
switchProvider
|
|
4903
4955
|
});
|
|
4904
4956
|
const effort = cfg.effort ?? "medium";
|
|
@@ -4910,7 +4962,7 @@ function App() {
|
|
|
4910
4962
|
return Math.round(used / activeCtx * 100);
|
|
4911
4963
|
})();
|
|
4912
4964
|
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, children: [
|
|
4913
|
-
state !== "ready" && /* @__PURE__ */ jsx13(WelcomeBlock, { model: cfg.model, activeCtx, effort, cwd, error: agent.error, updateAvailable, updateStatus }),
|
|
4965
|
+
state !== "ready" && state !== "sessions" && state !== "models" && /* @__PURE__ */ jsx13(WelcomeBlock, { model: cfg.model, activeCtx, effort, cwd, error: agent.error, updateAvailable, updateStatus }),
|
|
4914
4966
|
state === "loading" && !agent.error && /* @__PURE__ */ jsx13(Box13, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: `connecting to ${provName}\u2026` }) }),
|
|
4915
4967
|
agent.error && state !== "ready" && /* @__PURE__ */ jsx13(
|
|
4916
4968
|
ChatView,
|
|
@@ -4922,7 +4974,7 @@ function App() {
|
|
|
4922
4974
|
error: agent.error
|
|
4923
4975
|
}
|
|
4924
4976
|
),
|
|
4925
|
-
|
|
4977
|
+
state === "select-model" && /* @__PURE__ */ jsx13(
|
|
4926
4978
|
ModelsView,
|
|
4927
4979
|
{
|
|
4928
4980
|
models: filteredModels,
|
|
@@ -4933,7 +4985,7 @@ function App() {
|
|
|
4933
4985
|
providerType: provEntry.type,
|
|
4934
4986
|
effort,
|
|
4935
4987
|
query: pickerQuery,
|
|
4936
|
-
requireSelection:
|
|
4988
|
+
requireSelection: true
|
|
4937
4989
|
}
|
|
4938
4990
|
),
|
|
4939
4991
|
state === "providers" && /* @__PURE__ */ jsx13(
|
|
@@ -4945,8 +4997,7 @@ function App() {
|
|
|
4945
4997
|
query: pickerQuery
|
|
4946
4998
|
}
|
|
4947
4999
|
),
|
|
4948
|
-
state === "sessions" && /* @__PURE__ */
|
|
4949
|
-
state === "ready" && /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
5000
|
+
(state === "ready" || state === "sessions" || state === "models") && /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
4950
5001
|
notice && /* @__PURE__ */ jsx13(Box13, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: "green", children: `\u2713 ${notice}` }) }),
|
|
4951
5002
|
/* @__PURE__ */ jsx13(
|
|
4952
5003
|
ChatView,
|
|
@@ -4961,18 +5012,33 @@ function App() {
|
|
|
4961
5012
|
permissionCursor: agent.permissionCursor,
|
|
4962
5013
|
activeToolUses: agent.activeToolUses,
|
|
4963
5014
|
activeToolResults: agent.activeToolResults,
|
|
4964
|
-
header: /* @__PURE__ */ jsx13(WelcomeBlock, { model: cfg.model, activeCtx, effort, cwd })
|
|
5015
|
+
header: /* @__PURE__ */ jsx13(WelcomeBlock, { model: cfg.model, activeCtx, effort, cwd }),
|
|
5016
|
+
logEpoch
|
|
4965
5017
|
}
|
|
4966
5018
|
),
|
|
4967
|
-
input.startsWith("/") && /* @__PURE__ */ jsx13(CommandPalette, { filter: input, cursor: paletteCursor }),
|
|
4968
|
-
contextWarning !== null && /* @__PURE__ */ jsx13(Box13, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: "yellow", children: `\u26A0 context ${contextWarning}% full \u2014 run /clear and start fresh` }) }),
|
|
4969
|
-
!input.startsWith("/") && (() => {
|
|
5019
|
+
state === "ready" && input.startsWith("/") && /* @__PURE__ */ jsx13(CommandPalette, { filter: input, cursor: paletteCursor }),
|
|
5020
|
+
state === "ready" && contextWarning !== null && /* @__PURE__ */ jsx13(Box13, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: "yellow", children: `\u26A0 context ${contextWarning}% full \u2014 run /clear and start fresh` }) }),
|
|
5021
|
+
state === "ready" && !input.startsWith("/") && (() => {
|
|
4970
5022
|
const m = parseMention(input);
|
|
4971
5023
|
if (!m) return null;
|
|
4972
5024
|
return /* @__PURE__ */ jsx13(FilePicker, { matches: searchFiles(process.cwd(), m.query), cursor: filePickerCursor });
|
|
4973
5025
|
})(),
|
|
4974
|
-
/* @__PURE__ */ jsx13(InputBar, { input, caret, disabled: agent.busy, processingLabel: agent.processingLabel }),
|
|
4975
|
-
|
|
5026
|
+
state === "ready" && /* @__PURE__ */ jsx13(InputBar, { input, caret, disabled: agent.busy, processingLabel: agent.processingLabel }),
|
|
5027
|
+
state === "sessions" && /* @__PURE__ */ jsx13(SessionsView, { sessions, cursor }),
|
|
5028
|
+
state === "models" && /* @__PURE__ */ jsx13(
|
|
5029
|
+
ModelsView,
|
|
5030
|
+
{
|
|
5031
|
+
models: filteredModels,
|
|
5032
|
+
cursor,
|
|
5033
|
+
model: cfg.model,
|
|
5034
|
+
host: provEntry.baseUrl,
|
|
5035
|
+
provider: provName,
|
|
5036
|
+
providerType: provEntry.type,
|
|
5037
|
+
effort,
|
|
5038
|
+
query: pickerQuery
|
|
5039
|
+
}
|
|
5040
|
+
),
|
|
5041
|
+
state === "ready" && !agent.busy && /* @__PURE__ */ jsx13(Box13, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: providerDown ? "provider unavailable \u2014 /provider to switch \xB7 /models to pick a model" : "type / to see commands" }) }),
|
|
4976
5042
|
updateAvailable && /* @__PURE__ */ jsx13(Box13, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: updateStatus === "failed" ? "red" : updateStatus === "installed" ? "green" : "yellow", children: updateBannerText(updateAvailable, updateStatus) }) })
|
|
4977
5043
|
] })
|
|
4978
5044
|
] });
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "miii-agent",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.31",
|
|
4
|
+
"description": "Local AI coding agent for your terminal โ an open-source, offline alternative to Claude Code, Cursor, and GitHub Copilot, powered by Ollama. Private by default, free forever.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"miii": "dist/cli.js"
|
|
@@ -52,7 +52,14 @@
|
|
|
52
52
|
"pair-programming",
|
|
53
53
|
"code-generation",
|
|
54
54
|
"llama-cpp",
|
|
55
|
-
"lm-studio"
|
|
55
|
+
"lm-studio",
|
|
56
|
+
"copilot",
|
|
57
|
+
"copilot-alternative",
|
|
58
|
+
"cursor-alternative",
|
|
59
|
+
"claude-code",
|
|
60
|
+
"claude-code-alternative",
|
|
61
|
+
"developer-tools",
|
|
62
|
+
"qwen"
|
|
56
63
|
],
|
|
57
64
|
"license": "MIT",
|
|
58
65
|
"dependencies": {
|