miii-cli 0.3.0 β 0.3.1
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 -56
- package/dist/tui/InputBar.js +4 -3
- package/dist/tui/components/InputArea.js +27 -13
- package/dist/tui/hooks/useRunLoop.js +25 -0
- package/dist/tui/printer.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,87 +9,90 @@
|
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[](https://nodejs.org)
|
|
11
11
|
|
|
12
|
-
## π
|
|
12
|
+
## π The Competitive Edge
|
|
13
13
|
|
|
14
14
|
| Feature | **Miii** | Claude Code | Codex CLI | Aider |
|
|
15
15
|
|---|---|---|---|---|
|
|
16
|
-
| **
|
|
17
|
-
| **
|
|
18
|
-
| **Cost** | π Free (
|
|
19
|
-
| **Runtime** | β‘
|
|
20
|
-
| **
|
|
21
|
-
| **
|
|
22
|
-
| **Web
|
|
23
|
-
| **
|
|
24
|
-
| **
|
|
25
|
-
| **
|
|
26
|
-
| **
|
|
27
|
-
|
|
28
|
-
> β
=
|
|
16
|
+
| **Execution Environment** | β
Local / Hybrid | β Cloud only | β Cloud only | β
Local + cloud |
|
|
17
|
+
| **Data Privacy** | β
Air-gapped possible | β Cloud-streamed | β Cloud-streamed | β οΈ Model-dependent |
|
|
18
|
+
| **Cost Structure** | π Free (Your Compute) | π³ Token-based | π³ Token-based | π Free (local) |
|
|
19
|
+
| **Runtime Efficiency** | β‘ TS (Instant Start) | π Node (Fast) | π Node | π’ Python (Heavy) |
|
|
20
|
+
| **Research Engine** | β
Deep Think Mode | β | β | β |
|
|
21
|
+
| **Validation Loop** | β
Auto-test (Jest/Vitest) | β οΈ Manual | β | β οΈ Manual |
|
|
22
|
+
| **Live Web Access** | β
Tavily Integrated | β | β | β |
|
|
23
|
+
| **Edit Precision** | β
Surgical `patch_file` | β
| β οΈ | β
|
|
|
24
|
+
| **State Persistence** | β
Named Sessions | β
| β | β οΈ Basic |
|
|
25
|
+
| **Extensibility** | β
npm + `.md` Skills | β οΈ MCP only | β | β |
|
|
26
|
+
| **License** | β
MIT | β | β | β
Apache 2.0 |
|
|
27
|
+
|
|
28
|
+
> β
= Native | β οΈ = Partial | β = Unsupported
|
|
29
29
|
|
|
30
30
|
## β‘οΈ Quick Start
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Deploy Miii in your environment in 30 seconds:
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
+
# 1. Pull a capable local model
|
|
35
36
|
ollama pull qwen2.5-coder:7b
|
|
37
|
+
|
|
38
|
+
# 2. Install the CLI globally
|
|
36
39
|
npm install -g miii-cli
|
|
40
|
+
|
|
41
|
+
# 3. Start engineering
|
|
37
42
|
miii
|
|
38
43
|
```
|
|
39
44
|
|
|
40
45
|
## π§ Why Miii?
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
The industry is saturated with heavy Python wrappers and expensive monthly subscriptions that trade your intellectual property for convenience. **Miii breaks this cycle.**
|
|
43
48
|
|
|
44
|
-
- **
|
|
45
|
-
- **
|
|
46
|
-
- **
|
|
47
|
-
- **
|
|
49
|
+
- **Privacy by Default**: Your codebase never leaves your machine. Period.
|
|
50
|
+
- **Zero Latency**: Built with TypeScript for near-instant startup. No virtual environments, no dependency hell, just raw performance.
|
|
51
|
+
- **True Autonomy**: Miii isn't a chatbot; it's a junior engineer. It plans, edits, runs tests, and iterates until the PR is ready.
|
|
52
|
+
- **Architectural Intelligence**: By analyzing git diffs and project structure, Miii understands context without requiring manual copy-pasting.
|
|
48
53
|
|
|
49
54
|
## π₯ Killer Features
|
|
50
55
|
|
|
51
|
-
- **π Precision
|
|
52
|
-
- **π
|
|
53
|
-
- **π
|
|
54
|
-
- **π§ Deep Think**:
|
|
55
|
-
- **π Planning
|
|
56
|
-
- **π
|
|
57
|
-
- **π¦ Skill System**: Extend
|
|
56
|
+
- **π Surgical Precision**: Instead of overwriting files, Miii uses `patch_file` to inject changes, preserving your formatting and reducing token waste.
|
|
57
|
+
- **π The Self-Healing Loop**: Miii executes your test suite (Jest, Vitest, Mocha) after every change. If a test fails, it analyzes the trace and fixes the code autonomously.
|
|
58
|
+
- **π Real-time Intelligence**: Integrated `web_search` and `web_extract` via Tavily allow Miii to reference the latest documentation and API changes.
|
|
59
|
+
- **π§ Deep Think Engine**: A sophisticated two-phase research mode that gathers data before synthesizing a solution.
|
|
60
|
+
- **π Strategic Planning**: Use `/plan` to map out complex refactors before a single character is typed.
|
|
61
|
+
- **π Persistent Context**: Workflows are saved as named sessions. Jump back into a specific feature branch with `miii --session feature-auth`.
|
|
62
|
+
- **π¦ Modular Skill System**: Extend Miii's capabilities using npm plugins or simple Markdown-based skill files.
|
|
58
63
|
|
|
59
|
-
##
|
|
64
|
+
## π¬ Deep Think Explained
|
|
60
65
|
|
|
61
|
-
Deep
|
|
66
|
+
Deep Think is a recursive research engine designed to eliminate "hallucinations" by grounding the AI in facts:
|
|
62
67
|
|
|
63
|
-
1. **Gather
|
|
64
|
-
|
|
68
|
+
1. **Gather Phase**: A constrained, read-only loop utilizing `read_file`, `list_files`, `git_status`, `git_log`, `git_diff`, and web tools.
|
|
69
|
+
- **Guardrails**: Strict limit of 6 tool calls and 4 web calls to prevent infinite loops.
|
|
70
|
+
- **Safety**: Zero write permissions. No mutations.
|
|
71
|
+
2. **Synthesize Phase**: All gathered intelligence is aggregated and fed into the main execution loop for a grounded, verified response.
|
|
65
72
|
|
|
66
|
-
**
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
/think
|
|
70
|
-
/think
|
|
71
|
-
/think latest breaking changes in react 19
|
|
73
|
+
**Trigger Research:**
|
|
74
|
+
```bash
|
|
75
|
+
/think "How does the auth middleware handle token expiry?"
|
|
76
|
+
/think "Analyze the project structure and explain the data flow."
|
|
77
|
+
/think "What are the breaking changes in React 19 for this project?"
|
|
72
78
|
```
|
|
73
|
-
|
|
74
|
-
The LLM can also call `deep_think` autonomously mid-conversation when it decides a question needs multi-source research before answering.
|
|
75
|
-
|
|
76
|
-
> Requires a Tavily key (`/tavily-key tvly-...`) for web calls. File/git research works without it.
|
|
79
|
+
*The LLM also triggers `deep_think` autonomously when it detects a high-complexity query.*
|
|
77
80
|
|
|
78
81
|
## β¨οΈ Command Cheat Sheet
|
|
79
82
|
|
|
80
|
-
| Command |
|
|
81
|
-
|
|
82
|
-
| `/think <query>` | Deep
|
|
83
|
-
| `/refactor <goal>` |
|
|
84
|
-
| `/git <sub>` | Instant
|
|
85
|
-
| `/plan` |
|
|
86
|
-
| `/model <name>` |
|
|
87
|
-
| `/tavily-key <key>` | Enable real-time
|
|
88
|
-
| `/sessions` |
|
|
83
|
+
| Command | Purpose | Impact |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `/think <query>` | Deep Research | High-fidelity synthesis of files + web |
|
|
86
|
+
| `/refactor <goal>` | Full-scale Engineering | Plan $\rightarrow$ Edit $\rightarrow$ Test loop |
|
|
87
|
+
| `/git <sub>` | Git Automation | Instant status, diffs, and AI commit messages |
|
|
88
|
+
| `/plan` | Architecture Mode | Structured blueprinting before coding |
|
|
89
|
+
| `/model <name>` | LLM Hot-swap | Switch models instantly based on task |
|
|
90
|
+
| `/tavily-key <key>` | Enable Web Access | Unlocks real-time internet browsing |
|
|
91
|
+
| `/sessions` | Context Recovery | Resume previous engineering sessions |
|
|
89
92
|
|
|
90
93
|
## βοΈ Configuration
|
|
91
94
|
|
|
92
|
-
|
|
95
|
+
Fine-tune your agent in `.miii.json` or `~/.config/miii/config.json`:
|
|
93
96
|
|
|
94
97
|
```json
|
|
95
98
|
{
|
|
@@ -110,13 +113,12 @@ cd miii-cli && npm install && npm run build && npm link
|
|
|
110
113
|
|
|
111
114
|
## π Community & Philosophy
|
|
112
115
|
|
|
113
|
-
**
|
|
116
|
+
**Stop renting your intelligence. Own your AI stack.**
|
|
114
117
|
|
|
115
|
-
|
|
118
|
+
Miii is built for engineers who value privacy, speed, and total control. If this tool has accelerated your workflow, support the project:
|
|
116
119
|
- π **Star the repo** on GitHub
|
|
117
|
-
- π¦ **Share on X
|
|
118
|
-
- π€ **
|
|
119
|
-
- π¬ **Tell a fellow developer**
|
|
120
|
+
- π¦ **Share your wins** on X
|
|
121
|
+
- π€ **Discuss the future** on Reddit
|
|
120
122
|
|
|
121
123
|
## π License
|
|
122
124
|
MIT
|
package/dist/tui/InputBar.js
CHANGED
|
@@ -8,6 +8,7 @@ import { tools } from '../tools/index.js';
|
|
|
8
8
|
import { readFile } from '../files/ops.js';
|
|
9
9
|
import { generateId } from '../types.js';
|
|
10
10
|
import * as printer from './printer.js';
|
|
11
|
+
import { toolArgSummary } from './printer.js';
|
|
11
12
|
import { loadSession, saveSession, listSessions, deleteSession } from '../sessions.js';
|
|
12
13
|
import { MacroQueue, MicroQueue } from '../tasks/queue.js';
|
|
13
14
|
import { TaskExecutor } from '../tasks/executor.js';
|
|
@@ -61,7 +62,7 @@ export function InputBar({ config, skills, cwd, session, version }) {
|
|
|
61
62
|
},
|
|
62
63
|
}), [config]);
|
|
63
64
|
const allTools = useMemo(() => [...tools, deepThinkTool], [deepThinkTool]);
|
|
64
|
-
const { status, setStatus, tick, currentTool, setCurrentTool, taskLabel, setTaskLabel, thinkingStartRef, runLoop, handleAbort, } = useRunLoop(config, currentModelRef, pushHistory, allTools, abortRef);
|
|
65
|
+
const { status, setStatus, tick, currentTool, setCurrentTool, taskLabel, setTaskLabel, thinkingStartRef, runLoop, handleAbort, permissionRequest, resolvePermission, } = useRunLoop(config, currentModelRef, pushHistory, allTools, abortRef);
|
|
65
66
|
// βββ refactor βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
66
67
|
const runRefactor = useCallback(async (goal) => {
|
|
67
68
|
printer.systemMsg(`refactor: ${goal}`);
|
|
@@ -514,7 +515,7 @@ export function InputBar({ config, skills, cwd, session, version }) {
|
|
|
514
515
|
}, [skills, runLoop, openPicker]);
|
|
515
516
|
const skillList = skills.list();
|
|
516
517
|
// βββ render ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
517
|
-
return (_jsxs(Box, { flexDirection: "column", children: [pickerOpen ? (_jsxs(_Fragment, { children: [_jsx(ModelPicker, { models: pickerModels, current: currentModel, loading: pickerLoading, error: pickerError, pull: pullState, onSelect: handleModelSelect, onPull: handleModelPull, onClose: () => { setPickerOpen(false); } }), _jsx(Divider, { cols: cols })] })) : (status === 'thinking' || status === 'tool') ? (_jsxs(_Fragment, { children: [_jsx(Box, { flexDirection: "column", paddingX: 1, children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: status === 'thinking'
|
|
518
|
+
return (_jsxs(Box, { flexDirection: "column", children: [pickerOpen ? (_jsxs(_Fragment, { children: [_jsx(ModelPicker, { models: pickerModels, current: currentModel, loading: pickerLoading, error: pickerError, pull: pullState, onSelect: handleModelSelect, onPull: handleModelPull, onClose: () => { setPickerOpen(false); } }), _jsx(Divider, { cols: cols })] })) : permissionRequest ? (_jsxs(_Fragment, { children: [_jsx(Box, { flexDirection: "column", paddingX: 1, paddingY: 0, children: _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "yellow", children: "\u26A0" }), _jsx(Text, { color: "white", bold: true, children: permissionRequest.toolName }), _jsx(Text, { color: "gray", children: toolArgSummary(permissionRequest.args) })] }) }), _jsx(Divider, { cols: cols })] })) : (status === 'thinking' || status === 'tool') ? (_jsxs(_Fragment, { children: [_jsx(Box, { flexDirection: "column", paddingX: 1, children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: status === 'thinking'
|
|
518
519
|
? _jsxs(_Fragment, { children: [_jsxs(Text, { color: "yellow", children: [SPARKLE[tick % SPARKLE.length], " "] }), _jsx(Text, { color: "gray", dimColor: true, italic: true, children: THINKING_PHRASES[phraseSeq[Math.floor(tick / 62) % phraseSeq.length]] })] })
|
|
519
|
-
: _jsxs(Text, { color: "yellow", dimColor: true, children: ["\u2699 running ", currentTool ?? 'tool', "\u2026"] }) }), _jsxs(Box, { gap: 2, children: [_jsxs(Text, { color: "gray", dimColor: true, children: [Math.floor((Date.now() - thinkingStartRef.current) / 1000), "s"] }), taskLabel && _jsx(Text, { color: "cyan", dimColor: true, children: taskLabel })] })] }) }), _jsx(Divider, { cols: cols })] })) : null, _jsx(InputArea, { status: status, skills: skillList, cwd: cwd, planningMode: planningMode, onSubmit: handleSubmit, onAbort: handleAbort })] }));
|
|
520
|
+
: _jsxs(Text, { color: "yellow", dimColor: true, children: ["\u2699 running ", currentTool ?? 'tool', "\u2026"] }) }), _jsxs(Box, { gap: 2, children: [_jsxs(Text, { color: "gray", dimColor: true, children: [Math.floor((Date.now() - thinkingStartRef.current) / 1000), "s"] }), taskLabel && _jsx(Text, { color: "cyan", dimColor: true, children: taskLabel })] })] }) }), _jsx(Divider, { cols: cols })] })) : null, _jsx(InputArea, { status: status, skills: skillList, cwd: cwd, planningMode: planningMode, permissionRequest: permissionRequest, onPermissionResponse: resolvePermission, onSubmit: handleSubmit, onAbort: handleAbort })] }));
|
|
520
521
|
}
|
|
@@ -34,7 +34,7 @@ const PLANNING_COMMANDS = [
|
|
|
34
34
|
];
|
|
35
35
|
const PASTE_MIN_LINES = 3;
|
|
36
36
|
const PASTE_MIN_CHARS = 200;
|
|
37
|
-
export function InputArea({ status, skills, cwd, planningMode, onSubmit, onAbort }) {
|
|
37
|
+
export function InputArea({ status, skills, cwd, planningMode, permissionRequest, onPermissionResponse, onSubmit, onAbort }) {
|
|
38
38
|
const [lines, setLines] = useState(['']);
|
|
39
39
|
const [cursor, setCursor] = useState({ row: 0, col: 0 });
|
|
40
40
|
const [overlay, setOverlay] = useState('none');
|
|
@@ -148,6 +148,18 @@ export function InputArea({ status, skills, cwd, planningMode, onSubmit, onAbort
|
|
|
148
148
|
setOverlayIdx(0);
|
|
149
149
|
}
|
|
150
150
|
useInput((input, key) => {
|
|
151
|
+
// Permission prompt intercepts all input
|
|
152
|
+
if (permissionRequest && onPermissionResponse) {
|
|
153
|
+
if (input === 'y' || input === 'Y') {
|
|
154
|
+
onPermissionResponse(true);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (input === 'n' || input === 'N' || key.escape) {
|
|
158
|
+
onPermissionResponse(false);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
151
163
|
// ESC: close overlay, abort stream, or clear input
|
|
152
164
|
if (key.escape) {
|
|
153
165
|
if (overlay !== 'none') {
|
|
@@ -296,19 +308,21 @@ export function InputArea({ status, skills, cwd, planningMode, onSubmit, onAbort
|
|
|
296
308
|
}
|
|
297
309
|
});
|
|
298
310
|
const isProcessing = status !== 'idle';
|
|
299
|
-
const borderColor = isProcessing ? 'yellow' : 'cyan';
|
|
300
|
-
const hint =
|
|
301
|
-
? '
|
|
302
|
-
:
|
|
303
|
-
? '
|
|
304
|
-
:
|
|
305
|
-
? '
|
|
306
|
-
: overlay === '
|
|
311
|
+
const borderColor = permissionRequest ? 'yellow' : isProcessing ? 'yellow' : 'cyan';
|
|
312
|
+
const hint = permissionRequest
|
|
313
|
+
? 'y approve n deny'
|
|
314
|
+
: isProcessing
|
|
315
|
+
? 'esc to abort'
|
|
316
|
+
: pasteLines > 0
|
|
317
|
+
? 'backspace removes paste enter to send'
|
|
318
|
+
: overlay === 'command' && !commandQuery.includes(' ')
|
|
307
319
|
? 'ββ navigate enter select esc close'
|
|
308
|
-
:
|
|
309
|
-
? '
|
|
310
|
-
:
|
|
311
|
-
|
|
320
|
+
: overlay === 'at'
|
|
321
|
+
? 'ββ navigate enter select esc close'
|
|
322
|
+
: planningMode
|
|
323
|
+
? 'π planning mode / suggestions enter send /plan:done to exit'
|
|
324
|
+
: '@ file / command enter send ctrl+c exit';
|
|
325
|
+
return (_jsxs(Box, { flexDirection: "column", children: [overlay === 'command' && (_jsx(CommandPalette, { skills: allCommands, query: commandQuery, idx: overlayIdx })), overlay === 'at' && (_jsx(AtPicker, { files: filteredFiles, query: atQuery, idx: overlayIdx })), _jsxs(Box, { borderStyle: "round", borderColor: borderColor, paddingX: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: borderColor, bold: true, children: 'β― ' }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: permissionRequest ? (_jsxs(Box, { gap: 2, children: [_jsx(Text, { color: "green", bold: true, children: "y yes" }), _jsx(Text, { color: "red", bold: true, children: "n no" })] })) : pasteLines > 0 ? (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "cyan", children: "\u2398" }), _jsxs(Text, { color: "cyan", children: ["pasted ", pasteLines, " lines"] }), (lines.length > 1 || lines[0]) && (_jsx(Text, { color: "gray", dimColor: true, children: "+ typed text" }))] })) : lines.length === 1 && !lines[0] ? (_jsx(Text, { color: isActive ? 'white' : 'gray', dimColor: isProcessing, children: isActive ? 'β' : 'processing...' })) : (lines.map((line, i) => (_jsx(Text, { wrap: "wrap", children: i === cursor.row
|
|
312
326
|
? renderLineWithCursor(line, cursor.col, isActive)
|
|
313
327
|
: line }, i)))) })] }), _jsx(Text, { color: "gray", dimColor: true, children: hint })] })] }));
|
|
314
328
|
}
|
|
@@ -7,16 +7,24 @@ import * as printer from '../printer.js';
|
|
|
7
7
|
const MAX_TOOL_DEPTH = 6;
|
|
8
8
|
const FILE_EDIT_TOOLS = new Set(['edit_file', 'create_file', 'patch_file', 'delete_file']);
|
|
9
9
|
const SHOW_RESULT_TOOLS = new Set(['run_tests', 'git_commit']);
|
|
10
|
+
const PERMISSION_TOOLS = new Set(['edit_file', 'patch_file', 'delete_file', 'create_file', 'move_file', 'run_command', 'git_commit']);
|
|
10
11
|
export function useRunLoop(config, currentModelRef, pushHistory, extraTools = [], abortRef) {
|
|
11
12
|
const [status, setStatus] = useState('idle');
|
|
12
13
|
const [tick, setTick] = useState(0);
|
|
13
14
|
const [currentTool, setCurrentTool] = useState();
|
|
14
15
|
const [taskLabel, setTaskLabel] = useState();
|
|
16
|
+
const [permissionRequest, setPermissionRequest] = useState(null);
|
|
17
|
+
const permissionResolveRef = useRef(null);
|
|
15
18
|
const thinkingStartRef = useRef(0);
|
|
16
19
|
const extraToolsRef = useRef(extraTools);
|
|
17
20
|
extraToolsRef.current = extraTools;
|
|
18
21
|
const pushHistoryRef = useRef(pushHistory);
|
|
19
22
|
useEffect(() => { pushHistoryRef.current = pushHistory; }, [pushHistory]);
|
|
23
|
+
const resolvePermission = useCallback((approved) => {
|
|
24
|
+
permissionResolveRef.current?.(approved);
|
|
25
|
+
permissionResolveRef.current = null;
|
|
26
|
+
setPermissionRequest(null);
|
|
27
|
+
}, []);
|
|
20
28
|
useEffect(() => {
|
|
21
29
|
if (status === 'idle')
|
|
22
30
|
return;
|
|
@@ -78,6 +86,17 @@ export function useRunLoop(config, currentModelRef, pushHistory, extraTools = []
|
|
|
78
86
|
const allTools = [...staticTools, ...extraToolsRef.current];
|
|
79
87
|
const tool = allTools.find(t => t.name === tc.name);
|
|
80
88
|
setCurrentTool(tc.name);
|
|
89
|
+
if (PERMISSION_TOOLS.has(tc.name)) {
|
|
90
|
+
const approved = await new Promise(resolve => {
|
|
91
|
+
permissionResolveRef.current = resolve;
|
|
92
|
+
setPermissionRequest({ toolName: tc.name, args: tc.args });
|
|
93
|
+
});
|
|
94
|
+
if (!approved) {
|
|
95
|
+
printer.systemMsg(`denied: ${tc.name}`);
|
|
96
|
+
next.push({ role: 'user', content: `Tool ${tc.name} was denied by the user` });
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
81
100
|
if (tool) {
|
|
82
101
|
try {
|
|
83
102
|
printer.toolCallStart(tc.name, tc.args);
|
|
@@ -136,6 +155,11 @@ export function useRunLoop(config, currentModelRef, pushHistory, extraTools = []
|
|
|
136
155
|
}, [config]);
|
|
137
156
|
const handleAbort = useCallback(() => {
|
|
138
157
|
abortRef.current?.abort();
|
|
158
|
+
if (permissionResolveRef.current) {
|
|
159
|
+
permissionResolveRef.current(false);
|
|
160
|
+
permissionResolveRef.current = null;
|
|
161
|
+
setPermissionRequest(null);
|
|
162
|
+
}
|
|
139
163
|
setStatus('idle');
|
|
140
164
|
}, []);
|
|
141
165
|
return {
|
|
@@ -144,5 +168,6 @@ export function useRunLoop(config, currentModelRef, pushHistory, extraTools = []
|
|
|
144
168
|
taskLabel, setTaskLabel,
|
|
145
169
|
thinkingStartRef,
|
|
146
170
|
runLoop, handleAbort,
|
|
171
|
+
permissionRequest, resolvePermission,
|
|
147
172
|
};
|
|
148
173
|
}
|
package/dist/tui/printer.js
CHANGED
|
@@ -63,7 +63,7 @@ function formatContent(text) {
|
|
|
63
63
|
function truncate(s, n) {
|
|
64
64
|
return s.length > n ? s.slice(0, n) + 'β¦' : s;
|
|
65
65
|
}
|
|
66
|
-
function toolArgSummary(args) {
|
|
66
|
+
export function toolArgSummary(args) {
|
|
67
67
|
if (args.message)
|
|
68
68
|
return `"${truncate(String(args.message), 60)}"`;
|
|
69
69
|
if (args.path)
|