kimiflare 0.9.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -94
- package/dist/index.js +214 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,36 +11,29 @@
|
|
|
11
11
|
</p>
|
|
12
12
|
|
|
13
13
|
<p align="center">
|
|
14
|
-
A terminal coding agent powered by <
|
|
14
|
+
<strong>A terminal coding agent powered by <a href="https://developers.cloudflare.com/workers-ai/models/kimi-k2.6/">Kimi-K2.6</a> on Cloudflare Workers AI.</strong><br>
|
|
15
|
+
Moonshot's 1T-parameter open-source model, running directly on your Cloudflare account.
|
|
15
16
|
</p>
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
› what files are here?
|
|
22
|
-
✓ glob(*)
|
|
23
|
-
/Users/you/proj/package.json
|
|
24
|
-
/Users/you/proj/src/index.ts
|
|
25
|
-
...
|
|
26
|
-
|
|
27
|
-
› add a /health endpoint to server.ts
|
|
28
|
-
✓ read(src/server.ts)
|
|
29
|
-
◐ edit src/server.ts
|
|
30
|
-
─── permission requested ──────────────────
|
|
31
|
-
@@ -42,6 +42,10 @@
|
|
32
|
-
app.get('/', …)
|
|
33
|
-
+ app.get('/health', (_, res) => res.json({ ok: true }))
|
|
34
|
-
─────────────────────────────────────────────
|
|
35
|
-
[Allow once] [Allow for session] [Deny]
|
|
36
|
-
```
|
|
18
|
+
<p align="center">
|
|
19
|
+
<img src="docs/screenshot.png" alt="kimiflare TUI" width="900">
|
|
20
|
+
</p>
|
|
37
21
|
|
|
38
|
-
##
|
|
22
|
+
## Why kimiflare
|
|
23
|
+
|
|
24
|
+
- **262k context window** — Read entire modules, large configs, and full stack traces without the model losing track.
|
|
25
|
+
- **Direct to Cloudflare** — No AI Gateway, no proxy, no OpenAI SDK. Your traffic goes straight to Workers AI from your account.
|
|
26
|
+
- **Plan mode** — Ask the agent to research and produce a plan without touching your filesystem. Review it, then exit plan mode to execute.
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
39
29
|
|
|
40
30
|
```sh
|
|
41
31
|
npm install -g kimiflare
|
|
32
|
+
kimiflare
|
|
42
33
|
```
|
|
43
34
|
|
|
35
|
+
On first run, an interactive onboarding wizard asks for your Cloudflare Account ID and API Token. That's it — you're ready.
|
|
36
|
+
|
|
44
37
|
Or run without installing:
|
|
45
38
|
|
|
46
39
|
```sh
|
|
@@ -49,6 +42,24 @@ npx kimiflare
|
|
|
49
42
|
|
|
50
43
|
Requires Node.js ≥ 20.
|
|
51
44
|
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
| Feature | What it does |
|
|
48
|
+
|---------|-------------|
|
|
49
|
+
| **Plan / Edit / Auto modes** | `plan` blocks all mutating tools for safe research. `edit` (default) prompts per mutating call. `auto` approves everything for trusted tasks. |
|
|
50
|
+
| **Live task panel** | For multi-step work, the agent publishes a task list with progress icons (■ active, ☐ pending, ✓ done), elapsed time, and token deltas. |
|
|
51
|
+
| **14 terminal themes** | dark, light, high-contrast, dracula, nord, one-dark, monokai, solarized-dark/light, tokyo-night, gruvbox-dark/light, catppuccin-mocha, rose-pine. Interactive picker with live preview (`Ctrl+T`). |
|
|
52
|
+
| **Paste collapse** | Large pastes (≥200 chars or ≥2 newlines) collapse to `[pasted N lines #id]`. Full content still goes to the model — scrollback stays clean. |
|
|
53
|
+
| **Type-ahead queue** | Type your next prompt while the model is still working. Queued prompts show as `⏳ …` and fire in order. `Ctrl-C` aborts current + clears queue. |
|
|
54
|
+
| **Auto-compaction** | At ~80% context usage, kimiflare nudges you to run `/compact`. It summarizes older turns into a dense summary, keeping the last 4 turns intact. |
|
|
55
|
+
| **Streaming reasoning** | Toggle the model's chain-of-thought with `/reasoning` or `Ctrl-R`. See how it thinks in real time. |
|
|
56
|
+
| **Live cost tracking** | Status bar shows real-time cost based on Cloudflare pricing: `$0.95/M input`, `$0.16/M cached`, `$4.00/M output`. |
|
|
57
|
+
| **Session persistence** | Every turn is auto-saved. `/resume` lists past sessions (with message counts) in a paginated picker. |
|
|
58
|
+
| **Smart permissions** | Bash session-allow is keyed by the first token (e.g., allow all `git` commands). Write/edit show a unified diff before you approve. |
|
|
59
|
+
| **Project context (`/init`)** | Scans your repo and writes a concise `KIMI.md` — build commands, layout, conventions. Auto-loaded on every launch. |
|
|
60
|
+
| **Co-author auto-append** | Detects `git commit` commands and auto-injects `Co-authored-by: kimiflare <kimiflare@proton.me>`. |
|
|
61
|
+
| **Resilient transport** | Retries Cloudflare capacity errors (code 3040) and 5xx with exponential backoff up to 5 attempts. |
|
|
62
|
+
|
|
52
63
|
## Configure
|
|
53
64
|
|
|
54
65
|
Get credentials from Cloudflare:
|
|
@@ -79,50 +90,85 @@ chmod 600 ~/.config/kimiflare/config.json
|
|
|
79
90
|
|
|
80
91
|
## Usage
|
|
81
92
|
|
|
93
|
+
### Interactive TUI
|
|
94
|
+
|
|
82
95
|
```sh
|
|
83
|
-
kimiflare #
|
|
84
|
-
kimiflare -p "summarize PLAN.md" # one-shot, streams answer to stdout
|
|
85
|
-
kimiflare -p "..." --dangerously-allow-all # auto-approve mutating tools (for scripts)
|
|
96
|
+
kimiflare # launch TUI
|
|
86
97
|
kimiflare --model @cf/moonshotai/kimi-k2.6 # override model
|
|
87
|
-
kimiflare --reasoning # (print mode) stream chain-of-thought to stderr
|
|
88
98
|
```
|
|
89
99
|
|
|
90
|
-
|
|
100
|
+
### Print mode (one-shot, non-interactive)
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
kimiflare -p "summarize PLAN.md" # stream answer to stdout
|
|
104
|
+
kimiflare -p "..." --dangerously-allow-all # auto-approve mutating tools (for scripts)
|
|
105
|
+
kimiflare -p "..." --reasoning # include chain-of-thought in stderr
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### CLI flags
|
|
109
|
+
|
|
110
|
+
| Flag | Short | Description |
|
|
111
|
+
|------|-------|-------------|
|
|
112
|
+
| `--print <prompt>` | `-p` | One-shot mode: send prompt, stream reply, exit |
|
|
113
|
+
| `--model <id>` | `-m` | Model ID (default: `@cf/moonshotai/kimi-k2.6`) |
|
|
114
|
+
| `--dangerously-allow-all` | — | Auto-approve every permission prompt (print mode only) |
|
|
115
|
+
| `--reasoning` | — | Stream chain-of-thought to stderr (print mode only) |
|
|
116
|
+
| `--version` | `-V` | Show version |
|
|
117
|
+
| `--help` | `-h` | Show help |
|
|
118
|
+
|
|
119
|
+
## Slash commands
|
|
91
120
|
|
|
92
|
-
| Command
|
|
93
|
-
|
|
94
|
-
| `/mode edit\|plan\|auto`
|
|
95
|
-
| `/plan` `/auto` `/edit`
|
|
121
|
+
| Command | Effect |
|
|
122
|
+
|---------|--------|
|
|
123
|
+
| `/mode edit\|plan\|auto` | Switch mode. `edit` prompts for permission (default), `plan` is read-only research, `auto` auto-approves every tool call. |
|
|
124
|
+
| `/plan` `/auto` `/edit` | Shortcuts for the three modes. |
|
|
96
125
|
| `/thinking low\|medium\|high` | Reasoning effort. `low` = fastest, shallow; `medium` = balanced (default); `high` = deepest, slowest. Saved to config. |
|
|
97
|
-
| `/theme
|
|
98
|
-
| `/
|
|
99
|
-
| `/
|
|
100
|
-
| `/
|
|
101
|
-
| `/
|
|
102
|
-
| `/
|
|
103
|
-
| `/
|
|
104
|
-
| `/
|
|
105
|
-
| `/
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
126
|
+
| `/theme` | Interactive theme picker with live preview (`Ctrl+T`). Saved to config. |
|
|
127
|
+
| `/theme NAME` | Set theme by name directly. |
|
|
128
|
+
| `/resume` | Pick a past conversation to restore. |
|
|
129
|
+
| `/compact` | Summarize older turns to free context. Suggested automatically at ~80% full. |
|
|
130
|
+
| `/init` | Scan the repo and write a `KIMI.md` so future agents have project context. |
|
|
131
|
+
| `/reasoning` | Toggle chain-of-thought display. |
|
|
132
|
+
| `/clear` | Reset the current conversation. |
|
|
133
|
+
| `/cost` | Show token usage for the current turn. |
|
|
134
|
+
| `/model` | Show current model. |
|
|
135
|
+
| `/update` | Check for updates manually. |
|
|
136
|
+
| `/logout` | Clear saved credentials. |
|
|
137
|
+
| `/help` | List all commands. |
|
|
138
|
+
| `/exit` | Quit. |
|
|
139
|
+
|
|
140
|
+
## Keyboard shortcuts
|
|
141
|
+
|
|
142
|
+
### Global
|
|
143
|
+
|
|
144
|
+
| Shortcut | Action |
|
|
145
|
+
|----------|--------|
|
|
146
|
+
| `Ctrl+C` | Interrupt current turn (press again to exit) |
|
|
147
|
+
| `Ctrl+R` | Toggle reasoning display |
|
|
148
|
+
| `Ctrl+O` | Toggle verbose tool output |
|
|
149
|
+
| `Ctrl+T` | Open theme picker |
|
|
150
|
+
| `Shift+Tab` | Cycle mode (edit → plan → auto) |
|
|
151
|
+
| `↑` / `↓` | Walk prompt history |
|
|
152
|
+
|
|
153
|
+
### Editing (macOS / Linux)
|
|
154
|
+
|
|
155
|
+
| Shortcut | Action |
|
|
156
|
+
|----------|--------|
|
|
157
|
+
| `⌥←` / `⌥→` | Jump word left/right |
|
|
158
|
+
| `⌘←` / `⌘→` | Jump to start / end of line |
|
|
159
|
+
| `⌥⌫` | Delete word backward |
|
|
160
|
+
| `⌘⌫` | Delete to start of line |
|
|
161
|
+
| `⌥⌦` | Delete word forward |
|
|
162
|
+
| `Ctrl+A` / `Ctrl+E` | Start / end of line |
|
|
163
|
+
| `Ctrl+W` / `Ctrl+U` / `Ctrl+K` | Delete word backward / to start / to end of line |
|
|
164
|
+
|
|
165
|
+
## Modes
|
|
120
166
|
|
|
121
167
|
- **edit** — default. The agent calls tools freely for read-only work; mutating tools (`write`, `edit`, `bash`) pause for your approval.
|
|
122
168
|
- **plan** — read-only. Mutating tools are hard-blocked. Ask "plan a refactor" and the agent will investigate and produce a plan without touching the filesystem. Exit plan mode to execute.
|
|
123
169
|
- **auto** — autonomous. Every tool call is auto-approved. Use for trusted, well-scoped tasks.
|
|
124
170
|
|
|
125
|
-
|
|
171
|
+
## Thinking level (quality vs speed)
|
|
126
172
|
|
|
127
173
|
Kimi-K2.6 always reasons, but you can cap the effort:
|
|
128
174
|
|
|
@@ -132,52 +178,26 @@ Kimi-K2.6 always reasons, but you can cap the effort:
|
|
|
132
178
|
|
|
133
179
|
Set with `/thinking medium` (persists), or per-launch via `KIMI_REASONING_EFFORT=high`.
|
|
134
180
|
|
|
135
|
-
### Type-ahead queue
|
|
136
|
-
|
|
137
|
-
You can type the next prompt while the model is still executing. Submitted prompts show up as `⏳ …` and fire in order as each turn completes. `Ctrl-C` aborts the current turn and clears the queue.
|
|
138
|
-
|
|
139
|
-
### Session persistence
|
|
140
|
-
|
|
141
|
-
Sessions are saved to `~/.local/share/kimiflare/sessions/` after each turn. `/resume` lists the most recent (with first prompt + message count) so you can pick one up later.
|
|
142
|
-
|
|
143
|
-
### Task panel
|
|
144
|
-
|
|
145
|
-
For multi-step requests, the agent can publish a live task list via the `tasks_set` tool. The panel shows progress inline with status icons (`■` active, `☐` pending, `✓` done), elapsed time, and tokens consumed for the current task batch. Press `Ctrl-O` while a turn is running to switch tool output between compact (first line) and verbose (full output) modes.
|
|
146
|
-
|
|
147
|
-
### Paste collapse
|
|
148
|
-
|
|
149
|
-
Paste a large block (≥ 200 chars or ≥ 3 newlines in one paste) into the prompt and the input collapses it to `[pasted N lines #id]`. The full content still goes to the model on submit — only the on-screen display and chat history are collapsed, so scrollback doesn't get buried by a wall of code.
|
|
150
|
-
|
|
151
|
-
### Project context (KIMI.md)
|
|
152
|
-
|
|
153
|
-
Run `/init` inside a repo and kimiflare scans the project (reads `package.json`, `README`, source layout, etc.) and writes a concise `KIMI.md` at the repo root — project overview, build/test commands, conventions, quirks. On every subsequent launch in that directory, `KIMI.md` (or `KIMIFLARE.md` or `AGENT.md`, whichever exists) is auto-loaded into the system prompt so the agent already "knows" the project. If the file already exists, `/init` refuses so you don't overwrite hand-edited context.
|
|
154
|
-
|
|
155
|
-
## Why
|
|
156
|
-
|
|
157
|
-
- **262k context.** Read entire modules without pagination.
|
|
158
|
-
- **Native tool use.** File I/O, shell, globs, grep, web fetch — all wired up, with per-call approval for anything mutating.
|
|
159
|
-
- **Streaming reasoning + content.** The model's chain-of-thought streams separately; toggle with `/reasoning` or `Ctrl-R`.
|
|
160
|
-
- **Pay your own way.** Your Cloudflare account, your credits, your rate limits. `$0.95 / M input`, `$0.16 / M cached input`, `$4.00 / M output`. The bottom status line shows live cost.
|
|
161
|
-
|
|
162
181
|
## Tools
|
|
163
182
|
|
|
164
183
|
All tool calls show inline; mutating ones require per-call approval the first time, with an option to allow for the rest of the session.
|
|
165
184
|
|
|
166
|
-
| Tool
|
|
167
|
-
|
|
168
|
-
| `read`
|
|
169
|
-
| `write`
|
|
170
|
-
| `edit`
|
|
171
|
-
| `bash`
|
|
172
|
-
| `glob`
|
|
173
|
-
| `grep`
|
|
174
|
-
| `web_fetch` | auto
|
|
185
|
+
| Tool | Permission | What it does |
|
|
186
|
+
|------|------------|--------------|
|
|
187
|
+
| `read` | auto | Read a text file (≤ 2MB) with optional line range. |
|
|
188
|
+
| `write` | prompt | Create or overwrite a file. Shows a unified diff before you approve. |
|
|
189
|
+
| `edit` | prompt | Replace an exact substring. Fails unless `old_string` is unique (or `replace_all=true`). |
|
|
190
|
+
| `bash` | prompt | Run a shell command via `bash -lc`. Session-allow is keyed by the first token of the command. |
|
|
191
|
+
| `glob` | auto | Match files by pattern (`**/*.ts`), sorted by mtime. |
|
|
192
|
+
| `grep` | auto | Regex search. Uses `rg` if installed; falls back to a JS walk. |
|
|
193
|
+
| `web_fetch` | auto | Fetch a URL, convert HTML → markdown (≤ 100KB). |
|
|
194
|
+
| `tasks_set` | auto | Publish a live task list for multi-step work. |
|
|
175
195
|
|
|
176
196
|
## How it works
|
|
177
197
|
|
|
178
198
|
```
|
|
179
199
|
┌───────────────────────────────────────────────────────────┐
|
|
180
|
-
│ kimiflare (Node
|
|
200
|
+
│ kimiflare (Node.js TUI) │
|
|
181
201
|
user ─▶ │ │
|
|
182
202
|
│ user msg ─▶ agent loop ─▶ runKimi() ──[POST SSE]──▶ │
|
|
183
203
|
│ ▲ │
|
|
@@ -204,9 +224,23 @@ npm run build
|
|
|
204
224
|
npm link # or: ln -s "$PWD/bin/kimiflare.mjs" ~/.local/bin/kimiflare
|
|
205
225
|
```
|
|
206
226
|
|
|
207
|
-
|
|
227
|
+
Scripts:
|
|
228
|
+
- `npm run build` — bundle with tsup (`dist/` + `bin/kimiflare.mjs`)
|
|
229
|
+
- `npm run dev` — run via tsx (`tsx src/index.tsx`)
|
|
230
|
+
- `npm run typecheck` — `tsc --noEmit`
|
|
231
|
+
- `npm start` — run compiled bin
|
|
232
|
+
|
|
233
|
+
## Contributing
|
|
234
|
+
|
|
235
|
+
Contributions are welcome!
|
|
208
236
|
|
|
209
|
-
|
|
237
|
+
1. Fork the repository
|
|
238
|
+
2. Create a branch: `git checkout -b feat/your-feature`
|
|
239
|
+
3. Make your changes
|
|
240
|
+
4. Run `npm run typecheck` and `npm run build`
|
|
241
|
+
5. Commit: `git commit -m "feat: description"`
|
|
242
|
+
6. Push: `git push origin feat/your-feature`
|
|
243
|
+
7. Open a Pull Request
|
|
210
244
|
|
|
211
245
|
## License
|
|
212
246
|
|
package/dist/index.js
CHANGED
|
@@ -489,21 +489,209 @@ function nextMode(m) {
|
|
|
489
489
|
function isBlockedInPlanMode(toolName) {
|
|
490
490
|
return MUTATING_TOOLS.has(toolName);
|
|
491
491
|
}
|
|
492
|
+
function tokenizeCommand(command) {
|
|
493
|
+
const tokens = [];
|
|
494
|
+
let current = "";
|
|
495
|
+
let inQuote = null;
|
|
496
|
+
for (const ch of command) {
|
|
497
|
+
if (inQuote) {
|
|
498
|
+
if (ch === inQuote) {
|
|
499
|
+
inQuote = null;
|
|
500
|
+
} else {
|
|
501
|
+
current += ch;
|
|
502
|
+
}
|
|
503
|
+
} else if (ch === '"' || ch === "'") {
|
|
504
|
+
inQuote = ch;
|
|
505
|
+
} else if (/\s/.test(ch)) {
|
|
506
|
+
if (current) {
|
|
507
|
+
tokens.push(current);
|
|
508
|
+
current = "";
|
|
509
|
+
}
|
|
510
|
+
} else {
|
|
511
|
+
current += ch;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if (current) tokens.push(current);
|
|
515
|
+
return tokens;
|
|
516
|
+
}
|
|
517
|
+
function isReadOnlyBash(command) {
|
|
518
|
+
const trimmed = command.trim();
|
|
519
|
+
if (!trimmed) return false;
|
|
520
|
+
if (DANGEROUS_PATTERNS.test(trimmed)) return false;
|
|
521
|
+
const tokens = tokenizeCommand(trimmed);
|
|
522
|
+
if (tokens.length === 0) return false;
|
|
523
|
+
const first = tokens[0];
|
|
524
|
+
let cmdIndex = 0;
|
|
525
|
+
if (first === "cd" && tokens.length >= 3 && tokens[1] && tokens[2] === "&&") {
|
|
526
|
+
cmdIndex = 3;
|
|
527
|
+
}
|
|
528
|
+
const cmd = tokens[cmdIndex];
|
|
529
|
+
if (!cmd) return false;
|
|
530
|
+
const args = tokens.slice(cmdIndex + 1);
|
|
531
|
+
if (cmd === "git") {
|
|
532
|
+
const sub = args[0] ?? "";
|
|
533
|
+
const allowed = GIT_READONLY_SUBCOMMANDS[sub];
|
|
534
|
+
if (allowed === void 0) return false;
|
|
535
|
+
if (allowed === true) return true;
|
|
536
|
+
switch (sub) {
|
|
537
|
+
case "branch":
|
|
538
|
+
return !args.some((a) => /^-[dDmMcC]/.test(a));
|
|
539
|
+
case "stash":
|
|
540
|
+
return args[1] === "list";
|
|
541
|
+
case "remote":
|
|
542
|
+
return args[1] === "-v" || args[1] === "--verbose" || args.length === 1;
|
|
543
|
+
case "tag":
|
|
544
|
+
return args[1] === "-l" || args[1] === "--list" || args.length === 1;
|
|
545
|
+
case "config":
|
|
546
|
+
return args[1] === "--list" || args[1]?.startsWith("--get") === true || args.length === 1;
|
|
547
|
+
default:
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
const argCheck = COMMANDS_NEEDING_ARG_CHECK[cmd];
|
|
552
|
+
if (argCheck) {
|
|
553
|
+
return argCheck(args);
|
|
554
|
+
}
|
|
555
|
+
return READONLY_COMMANDS.has(cmd);
|
|
556
|
+
}
|
|
492
557
|
function systemPromptForMode(m) {
|
|
493
558
|
if (m === "plan") {
|
|
494
|
-
return "\n\nPLAN MODE is active. The user wants you to investigate and produce a plan WITHOUT making any changes. Do not call write, edit, or bash.
|
|
559
|
+
return "\n\nPLAN MODE is active. The user wants you to investigate and produce a plan WITHOUT making any changes. Do not call write, edit, or mutating bash commands. You may use read-only bash commands (e.g., git log, git diff, ls, cat) along with read/glob/grep/web-fetch. At the end, present a concise plan (bullets, files to change, approach). The user will review and then exit plan mode to execute.";
|
|
495
560
|
}
|
|
496
561
|
if (m === "auto") {
|
|
497
562
|
return "\n\nAUTO MODE is active. The user has opted into autonomous execution \u2014 every tool call will be auto-approved. Work efficiently, but do not take irreversible destructive actions (rm -rf, git push --force, dropping tables, etc.) without pausing to describe them in chat first. Prefer smaller reversible steps.";
|
|
498
563
|
}
|
|
499
564
|
return "";
|
|
500
565
|
}
|
|
501
|
-
var MODES, MUTATING_TOOLS;
|
|
566
|
+
var MODES, MUTATING_TOOLS, DANGEROUS_PATTERNS, GIT_READONLY_SUBCOMMANDS, READONLY_COMMANDS, COMMANDS_NEEDING_ARG_CHECK;
|
|
502
567
|
var init_mode = __esm({
|
|
503
568
|
"src/mode.ts"() {
|
|
504
569
|
"use strict";
|
|
505
570
|
MODES = ["edit", "plan", "auto"];
|
|
506
571
|
MUTATING_TOOLS = /* @__PURE__ */ new Set(["write", "edit", "bash"]);
|
|
572
|
+
DANGEROUS_PATTERNS = /[<>|;&`$]|\$\(|\$\{|&&|\|\||\b&\s*$/;
|
|
573
|
+
GIT_READONLY_SUBCOMMANDS = {
|
|
574
|
+
log: true,
|
|
575
|
+
diff: true,
|
|
576
|
+
status: true,
|
|
577
|
+
show: true,
|
|
578
|
+
blame: true,
|
|
579
|
+
describe: true,
|
|
580
|
+
"rev-parse": true,
|
|
581
|
+
"ls-files": true,
|
|
582
|
+
reflog: true,
|
|
583
|
+
shortlog: true,
|
|
584
|
+
whatchanged: true,
|
|
585
|
+
grep: true,
|
|
586
|
+
branch: false,
|
|
587
|
+
// needs check: block -d/-D/-m/-M/-c/-C
|
|
588
|
+
stash: false,
|
|
589
|
+
// needs check: only allow "list"
|
|
590
|
+
remote: false,
|
|
591
|
+
// needs check: only allow -v
|
|
592
|
+
tag: false,
|
|
593
|
+
// needs check: only allow -l
|
|
594
|
+
config: false
|
|
595
|
+
// needs check: only allow --list/--get
|
|
596
|
+
};
|
|
597
|
+
READONLY_COMMANDS = /* @__PURE__ */ new Set([
|
|
598
|
+
// File system
|
|
599
|
+
"ls",
|
|
600
|
+
"cat",
|
|
601
|
+
"head",
|
|
602
|
+
"tail",
|
|
603
|
+
"pwd",
|
|
604
|
+
"echo",
|
|
605
|
+
"file",
|
|
606
|
+
"stat",
|
|
607
|
+
"readlink",
|
|
608
|
+
"realpath",
|
|
609
|
+
"dirname",
|
|
610
|
+
"basename",
|
|
611
|
+
"wc",
|
|
612
|
+
"sort",
|
|
613
|
+
"uniq",
|
|
614
|
+
"diff",
|
|
615
|
+
"cmp",
|
|
616
|
+
// Search
|
|
617
|
+
"grep",
|
|
618
|
+
"rg",
|
|
619
|
+
"ag",
|
|
620
|
+
"fd",
|
|
621
|
+
// System info
|
|
622
|
+
"ps",
|
|
623
|
+
"df",
|
|
624
|
+
"du",
|
|
625
|
+
"env",
|
|
626
|
+
"printenv",
|
|
627
|
+
"which",
|
|
628
|
+
"whereis",
|
|
629
|
+
"uname",
|
|
630
|
+
"hostname",
|
|
631
|
+
"uptime",
|
|
632
|
+
"free",
|
|
633
|
+
"date",
|
|
634
|
+
"id",
|
|
635
|
+
"whoami",
|
|
636
|
+
"groups",
|
|
637
|
+
// Dev tools (version/info only)
|
|
638
|
+
"node",
|
|
639
|
+
"npx",
|
|
640
|
+
"python3",
|
|
641
|
+
"ruby",
|
|
642
|
+
"perl",
|
|
643
|
+
// Utilities
|
|
644
|
+
"jq",
|
|
645
|
+
"yq",
|
|
646
|
+
"awk",
|
|
647
|
+
"cut",
|
|
648
|
+
"tr",
|
|
649
|
+
"base64",
|
|
650
|
+
"sha256sum",
|
|
651
|
+
"md5sum",
|
|
652
|
+
"shasum",
|
|
653
|
+
"hexdump",
|
|
654
|
+
"xxd",
|
|
655
|
+
"strings",
|
|
656
|
+
"less",
|
|
657
|
+
"more",
|
|
658
|
+
"man",
|
|
659
|
+
"clear",
|
|
660
|
+
"history",
|
|
661
|
+
// Archive inspection
|
|
662
|
+
"zipinfo",
|
|
663
|
+
// Network
|
|
664
|
+
"ping",
|
|
665
|
+
"netstat",
|
|
666
|
+
"ss",
|
|
667
|
+
"lsof"
|
|
668
|
+
]);
|
|
669
|
+
COMMANDS_NEEDING_ARG_CHECK = {
|
|
670
|
+
find: (args) => !args.some((a) => a === "-delete" || a === "-exec"),
|
|
671
|
+
sed: (args) => !args.some((a) => a === "-i" || a.startsWith("-i")),
|
|
672
|
+
tar: (args) => args[0] === "-tf" || args[0] === "--list",
|
|
673
|
+
unzip: (args) => args[0] === "-l",
|
|
674
|
+
curl: (args) => !args.some((a) => a === "-o" || a === "-O" || a === "-d" || a === "--data" || a.startsWith("-X")),
|
|
675
|
+
wget: (args) => !args.some((a) => a === "-O" || a === "--output-document" || a.startsWith("--post")),
|
|
676
|
+
npm: (args) => ["list", "view", "config"].includes(args[0] ?? "") && !(args[0] === "config" && args[1] && !args[1].startsWith("get") && args[1] !== "list"),
|
|
677
|
+
tsc: (args) => args.every(
|
|
678
|
+
(a) => ["--noEmit", "--version", "--showConfig", "--help", "-h", "--init"].includes(a)
|
|
679
|
+
),
|
|
680
|
+
eslint: (args) => args.every(
|
|
681
|
+
(a) => ["--version", "--print-config", "--help", "-h"].includes(a) || !a.startsWith("-")
|
|
682
|
+
),
|
|
683
|
+
prettier: (args) => args.every(
|
|
684
|
+
(a) => ["--version", "--check", "--help", "-h"].includes(a) || !a.startsWith("-")
|
|
685
|
+
),
|
|
686
|
+
jest: (args) => args.every(
|
|
687
|
+
(a) => ["--version", "--listTests", "--showConfig", "--help", "-h"].includes(a) || !a.startsWith("-")
|
|
688
|
+
),
|
|
689
|
+
vitest: (args) => args.every(
|
|
690
|
+
(a) => ["--version", "--help", "-h"].includes(a) || !a.startsWith("-")
|
|
691
|
+
),
|
|
692
|
+
go: (args) => ["version", "env", "list", "mod"].includes(args[0] ?? "") && !(args[0] === "mod" && args[1] && !["graph", "download", "why", "verify"].includes(args[1])),
|
|
693
|
+
cargo: (args) => ["--version", "-V", "check", "test", "metadata"].includes(args[0] ?? "") && !(args[0] === "test" && args.includes("--no-run") === false)
|
|
694
|
+
};
|
|
507
695
|
}
|
|
508
696
|
});
|
|
509
697
|
|
|
@@ -3711,7 +3899,26 @@ function App({ initialCfg, initialUpdateResult }) {
|
|
|
3711
3899
|
setUsage(u);
|
|
3712
3900
|
},
|
|
3713
3901
|
askPermission: (req) => new Promise((resolve2) => {
|
|
3714
|
-
if (modeRef.current === "auto")
|
|
3902
|
+
if (modeRef.current === "auto") {
|
|
3903
|
+
resolve2("allow");
|
|
3904
|
+
return;
|
|
3905
|
+
}
|
|
3906
|
+
if (modeRef.current === "plan" && isBlockedInPlanMode(req.tool.name)) {
|
|
3907
|
+
if (req.tool.name === "bash" && typeof req.args.command === "string" && isReadOnlyBash(req.args.command)) {
|
|
3908
|
+
resolve2("allow");
|
|
3909
|
+
return;
|
|
3910
|
+
}
|
|
3911
|
+
setEvents((e) => [
|
|
3912
|
+
...e,
|
|
3913
|
+
{
|
|
3914
|
+
kind: "info",
|
|
3915
|
+
key: mkKey(),
|
|
3916
|
+
text: `plan mode blocked ${req.tool.name}; exit plan mode to execute`
|
|
3917
|
+
}
|
|
3918
|
+
]);
|
|
3919
|
+
resolve2("deny");
|
|
3920
|
+
return;
|
|
3921
|
+
}
|
|
3715
3922
|
setPerm({ tool: req.tool, args: req.args, resolve: resolve2 });
|
|
3716
3923
|
})
|
|
3717
3924
|
}
|
|
@@ -4106,6 +4313,10 @@ use: /thinking low | medium | high`
|
|
|
4106
4313
|
return;
|
|
4107
4314
|
}
|
|
4108
4315
|
if (modeRef.current === "plan" && isBlockedInPlanMode(req.tool.name)) {
|
|
4316
|
+
if (req.tool.name === "bash" && typeof req.args.command === "string" && isReadOnlyBash(req.args.command)) {
|
|
4317
|
+
resolve2("allow");
|
|
4318
|
+
return;
|
|
4319
|
+
}
|
|
4109
4320
|
setEvents((e) => [
|
|
4110
4321
|
...e,
|
|
4111
4322
|
{
|