@elench/testkit 0.1.106 → 0.1.107
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.
|
@@ -43,6 +43,8 @@ export async function runInteractiveAssistant({
|
|
|
43
43
|
assistantState.revealService(service);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
clearInteractiveScreen({ stdout, env });
|
|
47
|
+
|
|
46
48
|
const app = render(
|
|
47
49
|
createElement(AssistantApp, {
|
|
48
50
|
assistantState,
|
|
@@ -59,3 +61,9 @@ export async function runInteractiveAssistant({
|
|
|
59
61
|
|
|
60
62
|
return app.waitUntilExit();
|
|
61
63
|
}
|
|
64
|
+
|
|
65
|
+
export function clearInteractiveScreen({ stdout = process.stdout, env = process.env } = {}) {
|
|
66
|
+
if (env.TESTKIT_ASSISTANT_CLEAR_SCREEN === "0") return;
|
|
67
|
+
if (!stdout?.isTTY) return;
|
|
68
|
+
stdout.write("\x1b[2J\x1b[H");
|
|
69
|
+
}
|
|
@@ -8,6 +8,10 @@ import {
|
|
|
8
8
|
} from "../config.mjs";
|
|
9
9
|
import { createRunState } from "../state/run/state.mjs";
|
|
10
10
|
import { buildContextSelection } from "../../results/context.mjs";
|
|
11
|
+
import { renderDiscoverResult } from "../renderers/discover/text.mjs";
|
|
12
|
+
import { renderDoctorResult } from "../renderers/doctor/text.mjs";
|
|
13
|
+
import { renderStatusResult } from "../renderers/status/text.mjs";
|
|
14
|
+
import { renderTypecheckResult } from "../renderers/typecheck/text.mjs";
|
|
11
15
|
import { isProviderInstalled } from "./providers/index.mjs";
|
|
12
16
|
import { parseSlashCommand, formatSlashHelpLines } from "./slash-commands.mjs";
|
|
13
17
|
import { executeAssistantAction } from "./actions.mjs";
|
|
@@ -627,7 +631,12 @@ function handleAssistantToolEvent(state, event, appendMessage) {
|
|
|
627
631
|
toolName: event.command?.kind || "testkit",
|
|
628
632
|
title: formatObservedCommandTitle(event.command),
|
|
629
633
|
text: formatObservedCommandText(event.command),
|
|
630
|
-
data:
|
|
634
|
+
data: {
|
|
635
|
+
...(event.command || {}),
|
|
636
|
+
command: formatObservedCommandLine(event.command),
|
|
637
|
+
exitCode: event.command?.exitCode ?? null,
|
|
638
|
+
testkitRelated: true,
|
|
639
|
+
},
|
|
631
640
|
});
|
|
632
641
|
}
|
|
633
642
|
}
|
|
@@ -893,11 +902,18 @@ function formatObservedCommandTitle(command) {
|
|
|
893
902
|
return `testkit ${kind}`;
|
|
894
903
|
}
|
|
895
904
|
|
|
905
|
+
function formatObservedCommandLine(command) {
|
|
906
|
+
if (!command) return "testkit";
|
|
907
|
+
const argv = Array.isArray(command.argv) ? command.argv : [];
|
|
908
|
+
return `testkit ${argv.join(" ") || command.kind || ""}`.trim();
|
|
909
|
+
}
|
|
910
|
+
|
|
896
911
|
function formatObservedCommandText(command) {
|
|
897
912
|
if (!command) return "Observed Testkit command.";
|
|
913
|
+
const rendered = renderObservedCommandResult(command);
|
|
914
|
+
if (rendered.length > 0) return rendered.join("\n");
|
|
915
|
+
|
|
898
916
|
const lines = [];
|
|
899
|
-
lines.push(`$ testkit ${command.argv?.join(" ") || command.kind || ""}`.trim());
|
|
900
|
-
if (Number.isInteger(command.exitCode)) lines.push(`exit code: ${command.exitCode}`);
|
|
901
917
|
if (command.kind === "run" && command.result?.runArtifact) {
|
|
902
918
|
for (const file of collectRunArtifactFiles(command.result.runArtifact)) {
|
|
903
919
|
lines.push(`${formatObservedFileStatus(file.status)} ${file.serviceName} ${file.type} ${file.path}`);
|
|
@@ -913,6 +929,23 @@ function formatObservedCommandText(command) {
|
|
|
913
929
|
return lines.join("\n");
|
|
914
930
|
}
|
|
915
931
|
|
|
932
|
+
function renderObservedCommandResult(command) {
|
|
933
|
+
const result = command?.result;
|
|
934
|
+
if (!result) return [];
|
|
935
|
+
if (command.kind === "discover") return normalizeRenderedLines(renderDiscoverResult(result, { outputMode: "compact" }));
|
|
936
|
+
if (command.kind === "status") {
|
|
937
|
+
return normalizeRenderedLines((result.results || []).flatMap((entry) => renderStatusResult(entry)));
|
|
938
|
+
}
|
|
939
|
+
if (command.kind === "doctor") return normalizeRenderedLines(renderDoctorResult(result));
|
|
940
|
+
if (command.kind === "typecheck") return normalizeRenderedLines(renderTypecheckResult(result));
|
|
941
|
+
return [];
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
function normalizeRenderedLines(lines) {
|
|
945
|
+
return (Array.isArray(lines) ? lines : [])
|
|
946
|
+
.flatMap((line) => String(line ?? "").replace(/\r/g, "").split("\n"));
|
|
947
|
+
}
|
|
948
|
+
|
|
916
949
|
function collectRunArtifactFiles(artifact) {
|
|
917
950
|
const files = [];
|
|
918
951
|
for (const service of artifact?.services || []) {
|
|
@@ -4,6 +4,7 @@ import { formatContextRemaining } from "./context-window.mjs";
|
|
|
4
4
|
const PROVIDER_COMMAND_OUTPUT_PREVIEW_LINES = 12;
|
|
5
5
|
const PROVIDER_FILE_READ_OUTPUT_PREVIEW_LINES = 8;
|
|
6
6
|
const PROVIDER_DIFF_PREVIEW_LINES = 80;
|
|
7
|
+
const TESTKIT_COMMAND_OUTPUT_PREVIEW_LINES = 18;
|
|
7
8
|
|
|
8
9
|
export function buildAssistantViewModel(snapshot, { cwd = process.cwd(), terminalWidth = 100 } = {}) {
|
|
9
10
|
const providerLabel = buildProviderLabel(snapshot);
|
|
@@ -57,6 +58,7 @@ export function buildTranscriptBlocks(messages) {
|
|
|
57
58
|
return (messages || []).map((message) => {
|
|
58
59
|
const role = message.role || "system";
|
|
59
60
|
if (role === "tool") {
|
|
61
|
+
const outputPreview = summarizeOutput(message.text || "", TESTKIT_COMMAND_OUTPUT_PREVIEW_LINES);
|
|
60
62
|
return {
|
|
61
63
|
id: message.id,
|
|
62
64
|
kind: classifyToolBlock(message),
|
|
@@ -67,6 +69,9 @@ export function buildTranscriptBlocks(messages) {
|
|
|
67
69
|
status: message.status || null,
|
|
68
70
|
command: message.data?.command || null,
|
|
69
71
|
exitCode: message.data?.exitCode ?? null,
|
|
72
|
+
outputPreview,
|
|
73
|
+
outputLineCount: countLines(message.text || ""),
|
|
74
|
+
omittedOutputLineCount: outputPreview.omittedLineCount,
|
|
70
75
|
};
|
|
71
76
|
}
|
|
72
77
|
if (role === "provider-tool") {
|
|
@@ -159,7 +164,7 @@ function buildProviderCommandModel(message) {
|
|
|
159
164
|
const output = event.output || event.data?.aggregated_output || event.data?.output || "";
|
|
160
165
|
const exitCode = event.data?.exit_code ?? event.data?.exitCode ?? null;
|
|
161
166
|
const status = message.status || providerCommandStatus(event);
|
|
162
|
-
const diffText = extractProviderDiffText(event);
|
|
167
|
+
const diffText = extractProviderDiffText(event, rawCommand);
|
|
163
168
|
const diffPreview = diffText ? summarizeOutput(diffText, PROVIDER_DIFF_PREVIEW_LINES) : null;
|
|
164
169
|
const outputPreview = summarizeOutput(output, providerOutputPreviewLineLimit(event, rawCommand));
|
|
165
170
|
return {
|
|
@@ -188,7 +193,7 @@ function isProviderFileReadCommand(event, rawCommand) {
|
|
|
188
193
|
if (event.name && event.name !== "command") {
|
|
189
194
|
return /^(read|view|cat)$/i.test(String(event.name));
|
|
190
195
|
}
|
|
191
|
-
const command =
|
|
196
|
+
const command = unwrapShellCommand(rawCommand);
|
|
192
197
|
if (!command) return false;
|
|
193
198
|
if (/^(cat|sed|awk|head|tail|nl|less|more)\b/.test(command)) return true;
|
|
194
199
|
if (/\b(rg|grep)\b[\s\S]*\b--files\b/.test(command)) return false;
|
|
@@ -205,24 +210,61 @@ function summarizeProviderEditCommand(event, rawCommand) {
|
|
|
205
210
|
return command;
|
|
206
211
|
}
|
|
207
212
|
|
|
208
|
-
function extractProviderDiffText(event) {
|
|
209
|
-
const candidates =
|
|
213
|
+
function extractProviderDiffText(event, rawCommand) {
|
|
214
|
+
const candidates = providerDiffCandidates(event, rawCommand);
|
|
215
|
+
for (const candidate of candidates) {
|
|
216
|
+
const text = stringifyMaybe(candidate);
|
|
217
|
+
if (looksLikeDiff(text)) return text;
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function providerDiffCandidates(event, rawCommand) {
|
|
223
|
+
const name = String(event.name || "");
|
|
224
|
+
if (name && name !== "command") {
|
|
225
|
+
return [
|
|
226
|
+
event.input,
|
|
227
|
+
event.detail,
|
|
228
|
+
event.text,
|
|
229
|
+
event.output,
|
|
230
|
+
event.data?.arguments,
|
|
231
|
+
event.data?.input,
|
|
232
|
+
event.data?.patch,
|
|
233
|
+
event.data?.diff,
|
|
234
|
+
event.data?.aggregated_output,
|
|
235
|
+
event.data?.output,
|
|
236
|
+
];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const command = String(rawCommand || event.data?.command || event.data?.input || "").trim();
|
|
240
|
+
const inputCandidates = [
|
|
210
241
|
event.input,
|
|
211
|
-
event.detail,
|
|
212
|
-
event.text,
|
|
213
|
-
event.output,
|
|
214
242
|
event.data?.arguments,
|
|
215
243
|
event.data?.input,
|
|
216
244
|
event.data?.patch,
|
|
217
245
|
event.data?.diff,
|
|
218
|
-
event.data?.aggregated_output,
|
|
219
|
-
event.data?.output,
|
|
220
246
|
];
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
247
|
+
if (isDiffProducingShellCommand(command)) {
|
|
248
|
+
return [
|
|
249
|
+
...inputCandidates,
|
|
250
|
+
event.output,
|
|
251
|
+
event.data?.aggregated_output,
|
|
252
|
+
event.data?.output,
|
|
253
|
+
];
|
|
224
254
|
}
|
|
225
|
-
return
|
|
255
|
+
return inputCandidates;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function isDiffProducingShellCommand(command) {
|
|
259
|
+
const normalized = unwrapShellCommand(command);
|
|
260
|
+
return /^((git\s+diff|git\s+show|diff|apply_patch)\b|.*\bapply_patch\b)/.test(normalized);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function unwrapShellCommand(command) {
|
|
264
|
+
const text = String(command || "").trim();
|
|
265
|
+
const match = text.match(/(?:^|\s)(?:[^\s'"]*\/)?(?:bash|sh|zsh)\s+-lc\s+(['"])([\s\S]*)\1\s*$/);
|
|
266
|
+
if (!match) return text;
|
|
267
|
+
return match[2].replace(/\\(["'\\$`])/g, "$1").trim();
|
|
226
268
|
}
|
|
227
269
|
|
|
228
270
|
function stringifyMaybe(value) {
|