mini-coder 0.5.12 → 0.5.13
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/BENCHMARK.md +15 -316
- package/PROGRESS.md +1 -2
- package/README.md +53 -21
- package/benchmark-baseline.sh +15 -0
- package/bun.lock +265 -90
- package/package.json +8 -7
- package/skills-lock.json +15 -0
- package/src/agent.ts +20 -0
- package/src/cli.ts +2 -1
- package/src/headless.ts +97 -24
- package/src/index.ts +78 -93
- package/src/input.ts +13 -1
- package/src/mcp.ts +609 -0
- package/src/prompt.ts +2 -12
- package/src/settings.ts +199 -7
- package/src/skills.ts +12 -3
- package/src/submit.ts +17 -0
- package/src/theme.ts +186 -3
- package/src/tool-common.ts +2 -0
- package/src/tool-shell.ts +138 -6
- package/src/tools.ts +4 -0
- package/src/ui/agent.ts +7 -0
- package/src/ui/commands.test.ts +475 -4
- package/src/ui/commands.ts +210 -8
- package/src/ui/conversation.test.ts +252 -27
- package/src/ui/conversation.ts +468 -390
- package/src/ui/help.ts +27 -11
- package/src/ui.ts +230 -75
- package/src/plugins.ts +0 -183
package/src/tool-shell.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { Static, Tool } from "@mariozechner/pi-ai";
|
|
8
8
|
import { Type } from "@mariozechner/pi-ai";
|
|
9
9
|
import type { ToolHandler, ToolUpdateCallback } from "./agent.ts";
|
|
10
|
+
import { readFiniteNumber, readString, toRecord } from "./shared.ts";
|
|
10
11
|
import {
|
|
11
12
|
detectLineEnding,
|
|
12
13
|
normalizeLineEndings,
|
|
@@ -34,6 +35,16 @@ export interface ShellOpts {
|
|
|
34
35
|
onUpdate?: ToolUpdateCallback;
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
/** Structured shell result preserved on tool-result messages and events. */
|
|
39
|
+
export interface ShellResultDetails {
|
|
40
|
+
/** Captured stdout text after shell-tool truncation. */
|
|
41
|
+
stdout: string;
|
|
42
|
+
/** Captured stderr text after shell-tool truncation. */
|
|
43
|
+
stderr: string;
|
|
44
|
+
/** Process exit code. */
|
|
45
|
+
exitCode: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
37
48
|
/** pi-ai tool definition for `shell`. */
|
|
38
49
|
export const shellTool: Tool<typeof shellToolParameters> = {
|
|
39
50
|
name: "shell",
|
|
@@ -65,21 +76,136 @@ const DEFAULT_MAX_LINES = 1000;
|
|
|
65
76
|
const DEFAULT_MAX_BYTES = 50_000;
|
|
66
77
|
const SHELL_UPDATE_INTERVAL_MS = 75;
|
|
67
78
|
const SHELL_STREAM_DRAIN_TIMEOUT_MS = 25;
|
|
79
|
+
const LEGACY_SHELL_STDERR_PREFIX = "[stderr]\n";
|
|
80
|
+
const LEGACY_SHELL_STDERR_SEPARATOR = `\n\n${LEGACY_SHELL_STDERR_PREFIX}`;
|
|
68
81
|
|
|
69
|
-
/** Format combined stdout/stderr for
|
|
82
|
+
/** Format combined stdout/stderr for the legacy text payload preserved for model context. */
|
|
70
83
|
function formatShellOutput(stdout: string, stderr: string): string {
|
|
71
84
|
if (stdout && stderr) {
|
|
72
|
-
return `${stdout}
|
|
85
|
+
return `${stdout}${LEGACY_SHELL_STDERR_SEPARATOR}${stderr}`;
|
|
73
86
|
}
|
|
74
87
|
if (stdout) {
|
|
75
88
|
return stdout;
|
|
76
89
|
}
|
|
77
90
|
if (stderr) {
|
|
78
|
-
return
|
|
91
|
+
return `${LEGACY_SHELL_STDERR_PREFIX}${stderr}`;
|
|
79
92
|
}
|
|
80
93
|
return "";
|
|
81
94
|
}
|
|
82
95
|
|
|
96
|
+
/** Format the shell result as the legacy text payload stored in tool content. */
|
|
97
|
+
export function formatShellResultText(result: ShellResultDetails): string {
|
|
98
|
+
const body = formatShellOutput(result.stdout, result.stderr) || "(no output)";
|
|
99
|
+
return `Exit code: ${result.exitCode}\n${body}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Parse structured shell-result details from a persisted tool-result message. */
|
|
103
|
+
export function parseShellResultDetails(
|
|
104
|
+
details: unknown,
|
|
105
|
+
): ShellResultDetails | null {
|
|
106
|
+
const record = toRecord(details);
|
|
107
|
+
if (!record) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const stdout = readString(record, "stdout");
|
|
112
|
+
const stderr = readString(record, "stderr");
|
|
113
|
+
const exitCode = readFiniteNumber(record, "exitCode");
|
|
114
|
+
if (stdout === null || stderr === null || exitCode === null) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return { stdout, stderr, exitCode };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Parse the legacy flattened shell-result text stored by older builds. */
|
|
122
|
+
export function parseLegacyShellResult(
|
|
123
|
+
text: string,
|
|
124
|
+
): ShellResultDetails | null {
|
|
125
|
+
const match = /^Exit code: (\d+)(?:\n([\s\S]*))?$/.exec(
|
|
126
|
+
normalizeLineEndings(text, "\n"),
|
|
127
|
+
);
|
|
128
|
+
if (!match) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const exitCodeText = match[1];
|
|
133
|
+
if (!exitCodeText) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const exitCode = Number.parseInt(exitCodeText, 10);
|
|
138
|
+
const body = match[2] ?? "";
|
|
139
|
+
if (body === "" || body === "(no output)") {
|
|
140
|
+
return { stdout: "", stderr: "", exitCode };
|
|
141
|
+
}
|
|
142
|
+
if (body.startsWith(LEGACY_SHELL_STDERR_PREFIX)) {
|
|
143
|
+
return {
|
|
144
|
+
stdout: "",
|
|
145
|
+
stderr: body.slice(LEGACY_SHELL_STDERR_PREFIX.length),
|
|
146
|
+
exitCode,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const separatorIndex = body.indexOf(LEGACY_SHELL_STDERR_SEPARATOR);
|
|
151
|
+
if (separatorIndex === -1) {
|
|
152
|
+
return { stdout: body, stderr: "", exitCode };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
stdout: body.slice(0, separatorIndex),
|
|
157
|
+
stderr: body.slice(separatorIndex + LEGACY_SHELL_STDERR_SEPARATOR.length),
|
|
158
|
+
exitCode,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function truncateShellResult(
|
|
163
|
+
result: ShellResultDetails,
|
|
164
|
+
maxLines: number,
|
|
165
|
+
maxBytes: number,
|
|
166
|
+
): ShellResultDetails {
|
|
167
|
+
if (result.stdout === "" || result.stderr === "") {
|
|
168
|
+
return {
|
|
169
|
+
stdout:
|
|
170
|
+
result.stdout === ""
|
|
171
|
+
? ""
|
|
172
|
+
: truncateOutput(result.stdout, maxLines, maxBytes),
|
|
173
|
+
stderr:
|
|
174
|
+
result.stderr === ""
|
|
175
|
+
? ""
|
|
176
|
+
: truncateOutput(result.stderr, maxLines, maxBytes),
|
|
177
|
+
exitCode: result.exitCode,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
stdout: truncateOutput(
|
|
183
|
+
result.stdout,
|
|
184
|
+
Math.max(1, Math.ceil(maxLines / 2)),
|
|
185
|
+
Math.max(1, Math.ceil(maxBytes / 2)),
|
|
186
|
+
),
|
|
187
|
+
stderr: truncateOutput(
|
|
188
|
+
result.stderr,
|
|
189
|
+
Math.max(1, Math.floor(maxLines / 2)),
|
|
190
|
+
Math.max(1, Math.floor(maxBytes / 2)),
|
|
191
|
+
),
|
|
192
|
+
exitCode: result.exitCode,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function buildShellToolResult(
|
|
197
|
+
result: ShellResultDetails,
|
|
198
|
+
maxLines: number,
|
|
199
|
+
maxBytes: number,
|
|
200
|
+
): ToolExecResult {
|
|
201
|
+
const truncated = truncateShellResult(result, maxLines, maxBytes);
|
|
202
|
+
return {
|
|
203
|
+
content: [{ type: "text", text: formatShellResultText(truncated) }],
|
|
204
|
+
details: truncated,
|
|
205
|
+
isError: truncated.exitCode !== 0,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
83
209
|
interface ShellCommandLines {
|
|
84
210
|
lines: string[];
|
|
85
211
|
lineEnding: "\n" | "\r\n";
|
|
@@ -648,9 +774,15 @@ export async function executeShell(
|
|
|
648
774
|
opts.onUpdate(textResult(output, false));
|
|
649
775
|
}
|
|
650
776
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
777
|
+
return buildShellToolResult(
|
|
778
|
+
{
|
|
779
|
+
stdout: stdoutCapture?.getOutput().trimEnd() ?? "",
|
|
780
|
+
stderr: stderrCapture?.getOutput().trimEnd() ?? "",
|
|
781
|
+
exitCode,
|
|
782
|
+
},
|
|
783
|
+
maxLines,
|
|
784
|
+
maxBytes,
|
|
785
|
+
);
|
|
654
786
|
} catch (err) {
|
|
655
787
|
cleanupAbort?.();
|
|
656
788
|
cleanupAbort = null;
|
package/src/tools.ts
CHANGED
|
@@ -59,8 +59,12 @@ export {
|
|
|
59
59
|
} from "./tool-read.ts";
|
|
60
60
|
export {
|
|
61
61
|
executeShell,
|
|
62
|
+
formatShellResultText,
|
|
63
|
+
parseLegacyShellResult,
|
|
64
|
+
parseShellResultDetails,
|
|
62
65
|
type ShellArgs,
|
|
63
66
|
type ShellOpts,
|
|
67
|
+
type ShellResultDetails,
|
|
64
68
|
shellTool,
|
|
65
69
|
shellToolHandler,
|
|
66
70
|
truncateOutput,
|
package/src/ui/agent.ts
CHANGED
|
@@ -46,6 +46,8 @@ interface UiAgentRuntime {
|
|
|
46
46
|
requestRender: (priority?: UiRenderPriority) => void;
|
|
47
47
|
/** Re-enable stick-to-bottom behavior for the conversation log. */
|
|
48
48
|
scrollConversationToBottom: () => void;
|
|
49
|
+
/** Clear the readonly queued-input draft after it is committed. */
|
|
50
|
+
clearQueuedInputDraft: () => void;
|
|
49
51
|
/** Start the active-turn divider animation. */
|
|
50
52
|
startDividerAnimation: () => void;
|
|
51
53
|
/** Stop the active-turn divider animation. */
|
|
@@ -116,12 +118,14 @@ export function createUiAgentController(
|
|
|
116
118
|
if (
|
|
117
119
|
pending.toolName === event.name &&
|
|
118
120
|
pending.content === event.result.content &&
|
|
121
|
+
pending.details === event.result.details &&
|
|
119
122
|
pending.isError === event.result.isError
|
|
120
123
|
) {
|
|
121
124
|
return false;
|
|
122
125
|
}
|
|
123
126
|
pending.toolName = event.name;
|
|
124
127
|
pending.content = event.result.content;
|
|
128
|
+
pending.details = event.result.details;
|
|
125
129
|
pending.isError = event.result.isError;
|
|
126
130
|
return true;
|
|
127
131
|
}
|
|
@@ -130,6 +134,7 @@ export function createUiAgentController(
|
|
|
130
134
|
toolCallId: event.toolCallId,
|
|
131
135
|
toolName: event.name,
|
|
132
136
|
content: event.result.content,
|
|
137
|
+
details: event.result.details,
|
|
133
138
|
isError: event.result.isError,
|
|
134
139
|
});
|
|
135
140
|
return true;
|
|
@@ -195,6 +200,7 @@ export function createUiAgentController(
|
|
|
195
200
|
): void => {
|
|
196
201
|
switch (event.type) {
|
|
197
202
|
case "user_message":
|
|
203
|
+
runtime.clearQueuedInputDraft();
|
|
198
204
|
runtime.scrollConversationToBottom();
|
|
199
205
|
runtime.requestRender("normal");
|
|
200
206
|
return;
|
|
@@ -280,6 +286,7 @@ export function createUiAgentController(
|
|
|
280
286
|
onEvent: (event, currentState) => handleAgentEvent(event, currentState),
|
|
281
287
|
onTurnEnd: () => {
|
|
282
288
|
resetStreamingState();
|
|
289
|
+
runtime.clearQueuedInputDraft();
|
|
283
290
|
runtime.stopDividerAnimation();
|
|
284
291
|
runtime.requestRender("normal");
|
|
285
292
|
},
|