@elench/testkit 0.1.88 → 0.1.90
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 +19 -8
- package/lib/cli/agents/index.mjs +48 -9
- package/lib/cli/agents/providers/claude.mjs +3 -3
- package/lib/cli/agents/providers/codex.mjs +3 -3
- package/lib/cli/assistant/bootstrap.mjs +248 -0
- package/lib/cli/assistant/interactive.mjs +52 -0
- package/lib/cli/assistant/prompt-builder.mjs +9 -9
- package/lib/cli/assistant/session.mjs +4 -1
- package/lib/cli/assistant/slash-commands.mjs +24 -10
- package/lib/cli/assistant/state.mjs +15 -13
- package/lib/cli/assistant/tool-registry.mjs +116 -37
- package/lib/cli/commands/assistant.mjs +44 -41
- package/lib/cli/{tui/detail-pane.mjs → context-resources.mjs} +81 -21
- package/lib/cli/entrypoint.mjs +12 -5
- package/lib/cli/presentation/tree-reporter.mjs +0 -101
- package/lib/cli/tui/inspect-app.mjs +7 -88
- package/lib/cli/tui/inspect-state.mjs +0 -117
- package/node_modules/@elench/next-analysis/package.json +1 -1
- package/node_modules/@elench/testkit-bridge/package.json +2 -2
- package/node_modules/@elench/testkit-protocol/package.json +1 -1
- package/node_modules/@elench/ts-analysis/package.json +1 -1
- package/package.json +5 -5
- package/lib/cli/agents/investigate.mjs +0 -75
- package/lib/cli/agents/investigation-context.mjs +0 -102
- package/lib/cli/agents/investigation-interpreter.mjs +0 -320
- package/lib/cli/agents/investigation-log.mjs +0 -37
- package/lib/cli/agents/prompt-builder.mjs +0 -25
- package/lib/cli/tui/assistant-app.mjs +0 -131
|
@@ -9,24 +9,16 @@ import {
|
|
|
9
9
|
applyReporterTaskStarted,
|
|
10
10
|
} from "../tui/inspect-live-adapter.mjs";
|
|
11
11
|
import { suiteSelectionType } from "../../runner/suite-selection.mjs";
|
|
12
|
-
import { startHostedInvestigation } from "../agents/investigate.mjs";
|
|
13
|
-
import { createInvestigationInterpreter } from "../agents/investigation-interpreter.mjs";
|
|
14
|
-
import { writeInvestigationLog } from "../agents/investigation-log.mjs";
|
|
15
12
|
|
|
16
13
|
export function createTreeReporter({ stdout = process.stdout, stderr = process.stderr, productDir } = {}) {
|
|
17
14
|
const inspectState = createInspectState({ dataSource: "live" });
|
|
18
|
-
let activeAgentSession = null;
|
|
19
|
-
let activeInterpreter = null;
|
|
20
|
-
let investigationToken = 0;
|
|
21
15
|
|
|
22
16
|
const app = render(
|
|
23
17
|
createElement(InspectApp, {
|
|
24
18
|
inspectState,
|
|
25
19
|
stdout,
|
|
26
20
|
productDir,
|
|
27
|
-
onInvestigate: startInvestigation,
|
|
28
21
|
onRequestClose: close,
|
|
29
|
-
onCancelInvestigation: cancelInvestigation,
|
|
30
22
|
}),
|
|
31
23
|
{ stdout, exitOnCtrlC: false }
|
|
32
24
|
);
|
|
@@ -98,100 +90,7 @@ export function createTreeReporter({ stdout = process.stdout, stderr = process.s
|
|
|
98
90
|
close,
|
|
99
91
|
};
|
|
100
92
|
|
|
101
|
-
async function startInvestigation({ provider = "auto", userMessage } = {}) {
|
|
102
|
-
const snapshot = inspectState.getSnapshot();
|
|
103
|
-
if (!snapshot.selectedFailure) {
|
|
104
|
-
inspectState.setNotice("No failed file is selected for investigation.");
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (activeAgentSession) {
|
|
108
|
-
inspectState.setNotice("An investigation is already running.");
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const token = ++investigationToken;
|
|
113
|
-
let finalDelivered = false;
|
|
114
|
-
inspectState.beginInvestigation({ provider, userMessage });
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
activeInterpreter = createInvestigationInterpreter();
|
|
118
|
-
activeAgentSession = startHostedInvestigation({
|
|
119
|
-
productDir,
|
|
120
|
-
serviceName: snapshot.selectedFailure.serviceName,
|
|
121
|
-
filePath: snapshot.selectedFailure.filePath,
|
|
122
|
-
provider,
|
|
123
|
-
userMessage,
|
|
124
|
-
onEvent(event) {
|
|
125
|
-
if (token !== investigationToken) return;
|
|
126
|
-
if (event.type === "final") finalDelivered = true;
|
|
127
|
-
const presentation = activeInterpreter?.consumeProviderEvent(event) || null;
|
|
128
|
-
inspectState.recordInvestigationProgress(event, presentation);
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
const result = await activeAgentSession.completion;
|
|
132
|
-
if (token !== investigationToken) return;
|
|
133
|
-
activeAgentSession = null;
|
|
134
|
-
if (result.finalText && !finalDelivered) {
|
|
135
|
-
const finalEvent = { type: "final", text: result.finalText };
|
|
136
|
-
const presentation = activeInterpreter?.consumeProviderEvent(finalEvent) || null;
|
|
137
|
-
inspectState.recordInvestigationProgress(finalEvent, presentation);
|
|
138
|
-
}
|
|
139
|
-
if (result.cancelled) {
|
|
140
|
-
inspectState.cancelAgentSession("Cancelled investigation.");
|
|
141
|
-
persistInvestigationLog();
|
|
142
|
-
activeInterpreter = null;
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
if (result.exitCode !== 0 && !result.finalText) {
|
|
146
|
-
inspectState.failAgentSession(result.stderr || `Agent exited with code ${result.exitCode}`);
|
|
147
|
-
persistInvestigationLog();
|
|
148
|
-
activeInterpreter = null;
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
inspectState.completeAgentSession({
|
|
152
|
-
finalText: result.finalText,
|
|
153
|
-
exitCode: result.exitCode,
|
|
154
|
-
});
|
|
155
|
-
persistInvestigationLog();
|
|
156
|
-
activeInterpreter = null;
|
|
157
|
-
} catch (error) {
|
|
158
|
-
if (token !== investigationToken) return;
|
|
159
|
-
activeAgentSession = null;
|
|
160
|
-
inspectState.failAgentSession(error);
|
|
161
|
-
persistInvestigationLog();
|
|
162
|
-
activeInterpreter = null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function cancelInvestigation() {
|
|
167
|
-
if (!activeAgentSession) {
|
|
168
|
-
inspectState.returnToSummary();
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
investigationToken += 1;
|
|
172
|
-
activeAgentSession.cancel();
|
|
173
|
-
activeAgentSession = null;
|
|
174
|
-
inspectState.cancelAgentSession("Cancelled investigation.");
|
|
175
|
-
persistInvestigationLog();
|
|
176
|
-
activeInterpreter = null;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
93
|
function close() {
|
|
180
|
-
if (activeAgentSession) {
|
|
181
|
-
investigationToken += 1;
|
|
182
|
-
activeAgentSession.cancel();
|
|
183
|
-
activeAgentSession = null;
|
|
184
|
-
}
|
|
185
94
|
app.unmount();
|
|
186
95
|
}
|
|
187
|
-
|
|
188
|
-
function persistInvestigationLog() {
|
|
189
|
-
const snapshot = inspectState.getSnapshot();
|
|
190
|
-
if (!snapshot.selectedFailure || !snapshot.agentSession) return;
|
|
191
|
-
writeInvestigationLog({
|
|
192
|
-
productDir,
|
|
193
|
-
selectedFailure: snapshot.selectedFailure,
|
|
194
|
-
agentSession: snapshot.agentSession,
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
96
|
}
|
|
@@ -13,9 +13,8 @@ import {
|
|
|
13
13
|
} from "../presentation/colors.mjs";
|
|
14
14
|
import { renderSummaryBox } from "../presentation/summary-box.mjs";
|
|
15
15
|
import { getTerminalWidth } from "../presentation/terminal-layout.mjs";
|
|
16
|
-
import {
|
|
16
|
+
import { readContextContent } from "../context-resources.mjs";
|
|
17
17
|
import { applyHighlight } from "./fuzzy-match.mjs";
|
|
18
|
-
import { buildInspectPaneContent } from "./detail-pane.mjs";
|
|
19
18
|
import { FilterBar } from "./filter-bar.mjs";
|
|
20
19
|
|
|
21
20
|
const SPINNER_FRAMES = ["|", "/", "-", "\\"];
|
|
@@ -24,9 +23,7 @@ export function InspectApp({
|
|
|
24
23
|
inspectState,
|
|
25
24
|
stdout,
|
|
26
25
|
productDir,
|
|
27
|
-
onInvestigate,
|
|
28
26
|
onRequestClose,
|
|
29
|
-
onCancelInvestigation,
|
|
30
27
|
} = {}) {
|
|
31
28
|
const { exit } = useApp();
|
|
32
29
|
const [snapshot, setSnapshot] = useState(() => inspectState.getSnapshot());
|
|
@@ -41,25 +38,6 @@ export function InspectApp({
|
|
|
41
38
|
}, [inspectState]);
|
|
42
39
|
|
|
43
40
|
useInput((input, key) => {
|
|
44
|
-
if (snapshot.mode === "investigating") {
|
|
45
|
-
if (input === "q") {
|
|
46
|
-
(onRequestClose || exit)();
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (input === "t") {
|
|
50
|
-
inspectState.toggleInvestigationViewMode();
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
if (input === "x") {
|
|
54
|
-
onCancelInvestigation?.();
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
if (input === "b" && snapshot.agentSession?.status !== "running" && snapshot.agentSession?.status !== "starting") {
|
|
58
|
-
inspectState.returnToSummary();
|
|
59
|
-
}
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
41
|
if (!snapshot.finished) {
|
|
64
42
|
if (input === "q") {
|
|
65
43
|
inspectState.setNotice("Run is still in progress. Wait for completion before closing.");
|
|
@@ -115,20 +93,6 @@ export function InspectApp({
|
|
|
115
93
|
inspectState.cyclePaneMode();
|
|
116
94
|
return;
|
|
117
95
|
}
|
|
118
|
-
if ((input === "y" || input === "i") && snapshot.selectedFailure) {
|
|
119
|
-
onInvestigate?.({
|
|
120
|
-
provider: "auto",
|
|
121
|
-
userMessage: defaultInvestigationMessage(),
|
|
122
|
-
});
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (input === "c" && snapshot.selectedFailure) {
|
|
126
|
-
onInvestigate?.({ provider: "claude", userMessage: defaultInvestigationMessage() });
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
if (input === "o" && snapshot.selectedFailure) {
|
|
130
|
-
onInvestigate?.({ provider: "codex", userMessage: defaultInvestigationMessage() });
|
|
131
|
-
}
|
|
132
96
|
});
|
|
133
97
|
|
|
134
98
|
const terminalWidth = getTerminalWidth(stdout, 100);
|
|
@@ -140,13 +104,11 @@ export function InspectApp({
|
|
|
140
104
|
);
|
|
141
105
|
const paneContent = useMemo(
|
|
142
106
|
() =>
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
paneMode: snapshot.paneMode,
|
|
149
|
-
}),
|
|
107
|
+
readContextContent({
|
|
108
|
+
productDir,
|
|
109
|
+
snapshot,
|
|
110
|
+
mode: snapshot.paneMode,
|
|
111
|
+
}),
|
|
150
112
|
[productDir, snapshot]
|
|
151
113
|
);
|
|
152
114
|
const summaryLines = snapshot.finished && snapshot.summaryData
|
|
@@ -188,55 +150,12 @@ export function buildHeaderText(snapshot) {
|
|
|
188
150
|
}
|
|
189
151
|
|
|
190
152
|
export function buildFooterText(snapshot) {
|
|
191
|
-
if (snapshot.mode === "investigating") {
|
|
192
|
-
if (snapshot.agentSession?.status === "running" || snapshot.agentSession?.status === "starting") {
|
|
193
|
-
return "t transcript · x cancel investigation · q quit";
|
|
194
|
-
}
|
|
195
|
-
return "t transcript · b back · q quit";
|
|
196
|
-
}
|
|
197
153
|
if (!snapshot.finished) return "Run in progress";
|
|
198
154
|
if (snapshot.filter.active) {
|
|
199
155
|
return "type to filter · ↑/↓ move · Esc clear filter · q quit";
|
|
200
156
|
}
|
|
201
157
|
const inspectKeys = "↑/↓ move · Enter collapse/expand · Tab cycle pane · / filter";
|
|
202
|
-
|
|
203
|
-
return `${inspectKeys} · y investigate · c Claude · o Codex · q quit`;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export function formatAgentEntry(entry) {
|
|
207
|
-
if (!entry) return "";
|
|
208
|
-
if (entry.kind === "status") return `[status] ${entry.text}`;
|
|
209
|
-
if (entry.kind === "tool") return `[tool] ${entry.text}`;
|
|
210
|
-
if (entry.kind === "error") return `[error] ${entry.text}`;
|
|
211
|
-
return entry.text;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
export function formatTimelineEntry(entry) {
|
|
215
|
-
if (!entry) return "";
|
|
216
|
-
if (entry.kind === "result") return entry.summary || "";
|
|
217
|
-
if (entry.kind === "notice") return `${entry.severity || "info"}: ${entry.message}`;
|
|
218
|
-
return entry.message || entry.summary || "";
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function buildInvestigationLines(snapshot) {
|
|
222
|
-
const transcriptEntries = snapshot.agentSession?.transcriptEntries || [];
|
|
223
|
-
const timeline = snapshot.agentSession?.timeline || [];
|
|
224
|
-
const viewMode = snapshot.agentSession?.viewMode || "summary";
|
|
225
|
-
const status = snapshot.agentSession?.status || "idle";
|
|
226
|
-
const lines = [
|
|
227
|
-
`Status: ${status}`,
|
|
228
|
-
`Phase: ${snapshot.agentSession?.activePhase || "planning"}`,
|
|
229
|
-
"",
|
|
230
|
-
];
|
|
231
|
-
if (viewMode === "transcript") {
|
|
232
|
-
for (const entry of transcriptEntries.slice(-28)) lines.push(formatAgentEntry(entry));
|
|
233
|
-
return lines;
|
|
234
|
-
}
|
|
235
|
-
for (const entry of timeline.slice(-16)) lines.push(formatTimelineEntry(entry));
|
|
236
|
-
if (timeline.length === 0 && snapshot.agentSession?.finalText) {
|
|
237
|
-
lines.push(snapshot.agentSession.finalText);
|
|
238
|
-
}
|
|
239
|
-
return lines;
|
|
158
|
+
return `${inspectKeys} · q quit`;
|
|
240
159
|
}
|
|
241
160
|
|
|
242
161
|
function buildTreeViewport(entries, selectedEntryId, radius) {
|
|
@@ -24,40 +24,13 @@ import {
|
|
|
24
24
|
|
|
25
25
|
export function createInspectState({ dataSource = "live" } = {}) {
|
|
26
26
|
const model = createEmptyInspectModel(dataSource);
|
|
27
|
-
let mode = dataSource === "artifact" ? "complete" : "running";
|
|
28
27
|
let notice = null;
|
|
29
|
-
let agentSession = null;
|
|
30
28
|
const listeners = new Set();
|
|
31
29
|
|
|
32
30
|
function notify() {
|
|
33
31
|
for (const callback of listeners) callback();
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
function appendTranscriptEntry(kind, text) {
|
|
37
|
-
if (!agentSession || !text) return;
|
|
38
|
-
const normalizedText = String(text);
|
|
39
|
-
const entries = agentSession.transcriptEntries || [];
|
|
40
|
-
const lastEntry = entries.at(-1);
|
|
41
|
-
if (kind === "assistant" && lastEntry?.kind === "assistant") {
|
|
42
|
-
lastEntry.text += normalizedText;
|
|
43
|
-
agentSession.updatedAt = Date.now();
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
entries.push({ kind, text: normalizedText });
|
|
47
|
-
agentSession.transcriptEntries = entries;
|
|
48
|
-
agentSession.updatedAt = Date.now();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function finishAgentSession(status, extra = {}) {
|
|
52
|
-
if (!agentSession) return;
|
|
53
|
-
agentSession = {
|
|
54
|
-
...agentSession,
|
|
55
|
-
...extra,
|
|
56
|
-
status,
|
|
57
|
-
endedAt: Date.now(),
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
34
|
function moveCursor(delta) {
|
|
62
35
|
const snapshot = getSnapshot();
|
|
63
36
|
if (snapshot.visibleEntries.length === 0) return;
|
|
@@ -70,9 +43,7 @@ export function createInspectState({ dataSource = "live" } = {}) {
|
|
|
70
43
|
function getSnapshot() {
|
|
71
44
|
return {
|
|
72
45
|
...buildSnapshot(model),
|
|
73
|
-
mode,
|
|
74
46
|
notice,
|
|
75
|
-
agentSession,
|
|
76
47
|
};
|
|
77
48
|
}
|
|
78
49
|
|
|
@@ -84,15 +55,12 @@ export function createInspectState({ dataSource = "live" } = {}) {
|
|
|
84
55
|
|
|
85
56
|
hydrateFromArtifact(artifact) {
|
|
86
57
|
applyArtifactToModel(model, artifact);
|
|
87
|
-
mode = model.finished ? "complete" : "running";
|
|
88
58
|
notify();
|
|
89
59
|
},
|
|
90
60
|
|
|
91
61
|
resetForLive() {
|
|
92
62
|
resetInspectModel(model, "live");
|
|
93
|
-
mode = "running";
|
|
94
63
|
notice = null;
|
|
95
|
-
agentSession = null;
|
|
96
64
|
notify();
|
|
97
65
|
},
|
|
98
66
|
|
|
@@ -138,7 +106,6 @@ export function createInspectState({ dataSource = "live" } = {}) {
|
|
|
138
106
|
|
|
139
107
|
finish(results, durationMs, regressionReport) {
|
|
140
108
|
finishModel(model, results, durationMs, regressionReport);
|
|
141
|
-
mode = "complete";
|
|
142
109
|
notify();
|
|
143
110
|
},
|
|
144
111
|
|
|
@@ -220,90 +187,6 @@ export function createInspectState({ dataSource = "live" } = {}) {
|
|
|
220
187
|
notify();
|
|
221
188
|
},
|
|
222
189
|
|
|
223
|
-
beginInvestigation({ provider, userMessage } = {}) {
|
|
224
|
-
mode = "investigating";
|
|
225
|
-
notice = null;
|
|
226
|
-
agentSession = {
|
|
227
|
-
provider: provider || "auto",
|
|
228
|
-
userMessage: userMessage || "",
|
|
229
|
-
status: "starting",
|
|
230
|
-
startedAt: Date.now(),
|
|
231
|
-
updatedAt: Date.now(),
|
|
232
|
-
rawEvents: [],
|
|
233
|
-
transcriptEntries: [],
|
|
234
|
-
timeline: [],
|
|
235
|
-
summary: null,
|
|
236
|
-
activePhase: "planning",
|
|
237
|
-
activeStep: null,
|
|
238
|
-
viewMode: "summary",
|
|
239
|
-
};
|
|
240
|
-
notify();
|
|
241
|
-
},
|
|
242
|
-
|
|
243
|
-
recordInvestigationProgress(event, presentation = null) {
|
|
244
|
-
if (!agentSession || !event) return;
|
|
245
|
-
agentSession.rawEvents.push({ ...event });
|
|
246
|
-
if (event.type === "start") {
|
|
247
|
-
agentSession.status = "running";
|
|
248
|
-
} else if (event.type === "delta") {
|
|
249
|
-
appendTranscriptEntry("assistant", event.text || "");
|
|
250
|
-
} else if (event.type === "final") {
|
|
251
|
-
if (event.text && !(agentSession.transcriptEntries || []).some((entry) => entry.kind === "assistant")) {
|
|
252
|
-
appendTranscriptEntry("assistant", event.text);
|
|
253
|
-
}
|
|
254
|
-
agentSession.finalText = event.text || agentSession.finalText || "";
|
|
255
|
-
} else if (event.type === "tool") {
|
|
256
|
-
appendTranscriptEntry("tool", event.detail ? `${event.name}: ${event.detail}` : event.name);
|
|
257
|
-
} else if (event.type === "status") {
|
|
258
|
-
appendTranscriptEntry("status", event.message || "");
|
|
259
|
-
} else if (event.type === "error") {
|
|
260
|
-
appendTranscriptEntry("error", event.message || "Agent error");
|
|
261
|
-
} else if (event.type === "exit") {
|
|
262
|
-
agentSession.exitCode = event.code;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (presentation) {
|
|
266
|
-
agentSession.activePhase = presentation.phase || agentSession.activePhase || null;
|
|
267
|
-
agentSession.activeStep = presentation.activeStep || null;
|
|
268
|
-
agentSession.timeline = presentation.timeline || [];
|
|
269
|
-
agentSession.summary = presentation.summary || null;
|
|
270
|
-
}
|
|
271
|
-
notify();
|
|
272
|
-
},
|
|
273
|
-
|
|
274
|
-
toggleInvestigationViewMode() {
|
|
275
|
-
if (!agentSession) return;
|
|
276
|
-
agentSession.viewMode = agentSession.viewMode === "summary" ? "transcript" : "summary";
|
|
277
|
-
notify();
|
|
278
|
-
},
|
|
279
|
-
|
|
280
|
-
completeAgentSession(result = {}) {
|
|
281
|
-
finishAgentSession("complete", {
|
|
282
|
-
finalText: result.finalText || agentSession?.finalText || "",
|
|
283
|
-
exitCode: result.exitCode ?? agentSession?.exitCode ?? 0,
|
|
284
|
-
});
|
|
285
|
-
notify();
|
|
286
|
-
},
|
|
287
|
-
|
|
288
|
-
failAgentSession(error) {
|
|
289
|
-
finishAgentSession("error", {
|
|
290
|
-
error: error instanceof Error ? error.message : String(error || "Agent error"),
|
|
291
|
-
});
|
|
292
|
-
notify();
|
|
293
|
-
},
|
|
294
|
-
|
|
295
|
-
cancelAgentSession(message = "Cancelled investigation.") {
|
|
296
|
-
finishAgentSession("cancelled");
|
|
297
|
-
mode = "complete";
|
|
298
|
-
notice = message;
|
|
299
|
-
notify();
|
|
300
|
-
},
|
|
301
|
-
|
|
302
|
-
returnToSummary() {
|
|
303
|
-
mode = "complete";
|
|
304
|
-
notify();
|
|
305
|
-
},
|
|
306
|
-
|
|
307
190
|
subscribe(callback) {
|
|
308
191
|
listeners.add(callback);
|
|
309
192
|
return () => listeners.delete(callback);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elench/testkit-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.90",
|
|
4
4
|
"description": "Browser bridge helpers for testkit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@elench/testkit-protocol": "0.1.
|
|
25
|
+
"@elench/testkit-protocol": "0.1.90"
|
|
26
26
|
},
|
|
27
27
|
"private": false
|
|
28
28
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elench/testkit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.90",
|
|
4
4
|
"description": "CLI for discovering and running local HTTP, DAL, and Playwright test suites",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -82,10 +82,10 @@
|
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
84
|
"@babel/code-frame": "^7.29.0",
|
|
85
|
-
"@elench/next-analysis": "0.1.
|
|
86
|
-
"@elench/testkit-bridge": "0.1.
|
|
87
|
-
"@elench/testkit-protocol": "0.1.
|
|
88
|
-
"@elench/ts-analysis": "0.1.
|
|
85
|
+
"@elench/next-analysis": "0.1.90",
|
|
86
|
+
"@elench/testkit-bridge": "0.1.90",
|
|
87
|
+
"@elench/testkit-protocol": "0.1.90",
|
|
88
|
+
"@elench/ts-analysis": "0.1.90",
|
|
89
89
|
"@oclif/core": "^4.10.6",
|
|
90
90
|
"esbuild": "^0.25.11",
|
|
91
91
|
"execa": "^9.5.0",
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { loadInvestigationContext } from "./investigation-context.mjs";
|
|
2
|
-
import { buildInvestigationPrompt } from "./prompt-builder.mjs";
|
|
3
|
-
import { startAgentSession, startInteractiveAgentHandoff } from "./index.mjs";
|
|
4
|
-
|
|
5
|
-
export function createInvestigationRequest({
|
|
6
|
-
productDir,
|
|
7
|
-
serviceName,
|
|
8
|
-
filePath,
|
|
9
|
-
provider = "auto",
|
|
10
|
-
userMessage,
|
|
11
|
-
} = {}) {
|
|
12
|
-
const context = loadInvestigationContext({
|
|
13
|
-
productDir,
|
|
14
|
-
serviceName,
|
|
15
|
-
filePath,
|
|
16
|
-
});
|
|
17
|
-
const prompt = buildInvestigationPrompt({ context, userMessage });
|
|
18
|
-
return {
|
|
19
|
-
provider,
|
|
20
|
-
context,
|
|
21
|
-
prompt,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function startHostedInvestigation({
|
|
26
|
-
productDir,
|
|
27
|
-
serviceName,
|
|
28
|
-
filePath,
|
|
29
|
-
provider = "auto",
|
|
30
|
-
userMessage,
|
|
31
|
-
onEvent,
|
|
32
|
-
} = {}) {
|
|
33
|
-
const request = createInvestigationRequest({
|
|
34
|
-
productDir,
|
|
35
|
-
serviceName,
|
|
36
|
-
filePath,
|
|
37
|
-
provider,
|
|
38
|
-
userMessage,
|
|
39
|
-
});
|
|
40
|
-
const session = startAgentSession({
|
|
41
|
-
provider: request.provider,
|
|
42
|
-
cwd: productDir,
|
|
43
|
-
prompt: request.prompt,
|
|
44
|
-
onEvent,
|
|
45
|
-
});
|
|
46
|
-
return {
|
|
47
|
-
...session,
|
|
48
|
-
request,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export async function runInteractiveInvestigation({
|
|
53
|
-
productDir,
|
|
54
|
-
serviceName,
|
|
55
|
-
filePath,
|
|
56
|
-
provider = "auto",
|
|
57
|
-
userMessage,
|
|
58
|
-
} = {}) {
|
|
59
|
-
const request = createInvestigationRequest({
|
|
60
|
-
productDir,
|
|
61
|
-
serviceName,
|
|
62
|
-
filePath,
|
|
63
|
-
provider,
|
|
64
|
-
userMessage,
|
|
65
|
-
});
|
|
66
|
-
const result = await startInteractiveAgentHandoff({
|
|
67
|
-
provider: request.provider,
|
|
68
|
-
cwd: productDir,
|
|
69
|
-
prompt: request.prompt,
|
|
70
|
-
});
|
|
71
|
-
return {
|
|
72
|
-
...result,
|
|
73
|
-
request,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import {
|
|
3
|
-
collectArtifactEntries,
|
|
4
|
-
formatArtifactPreview,
|
|
5
|
-
formatFileDetail,
|
|
6
|
-
getServiceLogRefs,
|
|
7
|
-
getSetupOperationsForService,
|
|
8
|
-
loadCurrentRunArtifact,
|
|
9
|
-
resolveFileSubject,
|
|
10
|
-
} from "../viewer.mjs";
|
|
11
|
-
import { readLogTail } from "../../runner/logs.mjs";
|
|
12
|
-
|
|
13
|
-
export function loadInvestigationContext({
|
|
14
|
-
productDir,
|
|
15
|
-
serviceName,
|
|
16
|
-
filePath,
|
|
17
|
-
logTail = 20,
|
|
18
|
-
failureLimit = 5,
|
|
19
|
-
previewLength = 6,
|
|
20
|
-
} = {}) {
|
|
21
|
-
if (!productDir) throw new Error("productDir is required");
|
|
22
|
-
if (!serviceName) throw new Error("serviceName is required");
|
|
23
|
-
if (!filePath) throw new Error("filePath is required");
|
|
24
|
-
|
|
25
|
-
const runArtifact = loadCurrentRunArtifact(productDir);
|
|
26
|
-
const subject = resolveFileSubject(runArtifact, filePath, serviceName);
|
|
27
|
-
const detailLines = formatFileDetail(productDir, runArtifact, subject, {
|
|
28
|
-
logTail,
|
|
29
|
-
failureLimit,
|
|
30
|
-
previewLength,
|
|
31
|
-
});
|
|
32
|
-
const artifacts = collectArtifactEntries(productDir, runArtifact, subject.file.path, subject.service.name).map((entry) => ({
|
|
33
|
-
name: entry.artifactRef.name,
|
|
34
|
-
kind: entry.artifactRef.kind || null,
|
|
35
|
-
summary: entry.artifactRef.summary || null,
|
|
36
|
-
path: entry.artifactRef.path,
|
|
37
|
-
previewLines: formatArtifactPreview(entry.payload, previewLength),
|
|
38
|
-
}));
|
|
39
|
-
const setupOperations = getSetupOperationsForService(runArtifact, subject.service.name).slice(0, 8).map((operation) => ({
|
|
40
|
-
stage: operation.stage,
|
|
41
|
-
status: operation.status,
|
|
42
|
-
summary: operation.summary || null,
|
|
43
|
-
durationMs: operation.durationMs ?? null,
|
|
44
|
-
logPath: operation.logRef?.path || null,
|
|
45
|
-
error: operation.error || null,
|
|
46
|
-
}));
|
|
47
|
-
const backendLogs = getServiceLogRefs(runArtifact, subject.service.name).map((logRef) => ({
|
|
48
|
-
runtimeLabel: logRef.runtimeLabel,
|
|
49
|
-
path: logRef.path,
|
|
50
|
-
lines: readLogTail(path.join(productDir, logRef.path), logTail),
|
|
51
|
-
}));
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
productDir,
|
|
55
|
-
runArtifact,
|
|
56
|
-
subject,
|
|
57
|
-
detailLines,
|
|
58
|
-
summary: {
|
|
59
|
-
service: subject.service.name,
|
|
60
|
-
suite: `${subject.suite.type}:${subject.suite.name}`,
|
|
61
|
-
file: subject.file.path,
|
|
62
|
-
status: subject.file.status,
|
|
63
|
-
durationMs: subject.file.durationMs || 0,
|
|
64
|
-
error: subject.file.error || null,
|
|
65
|
-
failureCount: Array.isArray(subject.file.failureDetails) ? subject.file.failureDetails.length : 0,
|
|
66
|
-
},
|
|
67
|
-
setupOperations,
|
|
68
|
-
artifacts,
|
|
69
|
-
backendLogs,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function formatInvestigationTranscriptContext(context) {
|
|
74
|
-
if (!context) return "";
|
|
75
|
-
const artifactSummary = context.artifacts.length === 0
|
|
76
|
-
? "none"
|
|
77
|
-
: context.artifacts
|
|
78
|
-
.map((entry) => `${entry.name}${entry.kind ? ` [${entry.kind}]` : ""}: ${entry.path}`)
|
|
79
|
-
.join("\n");
|
|
80
|
-
const logSummary = context.backendLogs.length === 0
|
|
81
|
-
? "none"
|
|
82
|
-
: context.backendLogs.map((entry) => `${entry.runtimeLabel}: ${entry.path}`).join("\n");
|
|
83
|
-
|
|
84
|
-
return [
|
|
85
|
-
`Service: ${context.summary.service}`,
|
|
86
|
-
`Suite: ${context.summary.suite}`,
|
|
87
|
-
`File: ${context.summary.file}`,
|
|
88
|
-
`Status: ${context.summary.status}`,
|
|
89
|
-
context.summary.error ? `Error: ${context.summary.error}` : null,
|
|
90
|
-
"",
|
|
91
|
-
"Artifacts:",
|
|
92
|
-
artifactSummary,
|
|
93
|
-
"",
|
|
94
|
-
"Backend Logs:",
|
|
95
|
-
logSummary,
|
|
96
|
-
"",
|
|
97
|
-
"Detailed Failure View:",
|
|
98
|
-
...context.detailLines,
|
|
99
|
-
]
|
|
100
|
-
.filter(Boolean)
|
|
101
|
-
.join("\n");
|
|
102
|
-
}
|