@elench/testkit 0.1.90 → 0.1.91
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 +10 -12
- package/lib/cli/agents/index.mjs +0 -31
- package/lib/cli/assistant/app.mjs +210 -0
- package/lib/cli/assistant/context-pack.mjs +191 -0
- package/lib/cli/assistant/interactive.mjs +30 -29
- package/lib/cli/assistant/prompt-builder.mjs +4 -2
- package/lib/cli/assistant/session.mjs +3 -0
- package/lib/cli/assistant/state.mjs +130 -44
- package/lib/cli/assistant/tool-registry.mjs +216 -226
- package/lib/cli/commands/assistant.mjs +7 -1
- 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/assistant/bootstrap.mjs +0 -248
- package/lib/cli/assistant/tool-run-reporter.mjs +0 -80
|
@@ -1,280 +1,284 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { resolveRequestedFiles } from "../args.mjs";
|
|
5
|
-
import { buildDiscoveryReportLines } from "../presentation/discovery-reporter.mjs";
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { execaCommand } from "execa";
|
|
6
4
|
import { loadCurrentRunArtifact, loadLatestRunArtifact, resolveFileSubject } from "../viewer.mjs";
|
|
7
5
|
import {
|
|
8
|
-
formatContextToolText,
|
|
9
6
|
readContextContent,
|
|
10
7
|
} from "../context-resources.mjs";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
|
|
9
|
+
const COMMAND_OUTPUT_LIMIT = 14_000;
|
|
10
|
+
const COMMAND_LINE_LIMIT = 220;
|
|
11
|
+
const FILE_LINE_LIMIT = 160;
|
|
14
12
|
|
|
15
13
|
export function listAssistantTools() {
|
|
16
14
|
return [
|
|
17
|
-
{
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{
|
|
26
|
-
|
|
15
|
+
{
|
|
16
|
+
name: "shell_exec",
|
|
17
|
+
description: "Execute a shell command inside the repository. Prefer real repo commands such as npm, npx, and testkit.",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "read_context",
|
|
21
|
+
description: "Read testkit-managed context such as focused detail, logs, artifacts, setup, or run summary.",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "read_file",
|
|
25
|
+
description: "Read a local file with optional start and end lines.",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "search_repo",
|
|
29
|
+
description: "Search the repository with ripgrep and return matching lines.",
|
|
30
|
+
},
|
|
27
31
|
];
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
export async function executeAssistantTool(name, argumentsObject, context) {
|
|
31
35
|
const args = argumentsObject && typeof argumentsObject === "object" ? argumentsObject : {};
|
|
32
36
|
switch (name) {
|
|
33
|
-
case "
|
|
34
|
-
return
|
|
35
|
-
case "
|
|
36
|
-
return
|
|
37
|
-
case "
|
|
38
|
-
return
|
|
39
|
-
case "
|
|
40
|
-
return
|
|
41
|
-
case "read_logs":
|
|
42
|
-
return readLogsTool(args, context);
|
|
43
|
-
case "read_artifacts":
|
|
44
|
-
return readArtifactsTool(args, context);
|
|
45
|
-
case "read_setup":
|
|
46
|
-
return readSetupTool(args, context);
|
|
47
|
-
case "discover_tests":
|
|
48
|
-
return discoverTestsTool(args, context);
|
|
49
|
-
case "show_status":
|
|
50
|
-
return showStatusTool(args, context);
|
|
51
|
-
case "run_doctor":
|
|
52
|
-
return runDoctorTool(args, context);
|
|
37
|
+
case "shell_exec":
|
|
38
|
+
return shellExecTool(args, context);
|
|
39
|
+
case "read_context":
|
|
40
|
+
return readContextTool(args, context);
|
|
41
|
+
case "read_file":
|
|
42
|
+
return readFileTool(args, context);
|
|
43
|
+
case "search_repo":
|
|
44
|
+
return searchRepoTool(args, context);
|
|
53
45
|
default:
|
|
54
46
|
throw new Error(`Unknown assistant tool "${name}"`);
|
|
55
47
|
}
|
|
56
48
|
}
|
|
57
49
|
|
|
58
|
-
async function
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
dir: context.productDir,
|
|
62
|
-
service: args.service || null,
|
|
63
|
-
type: normalizeArray(args.type),
|
|
64
|
-
suite: normalizeArray(args.suite),
|
|
65
|
-
file: normalizeArray(args.file),
|
|
66
|
-
workers: args.workers == null ? null : String(args.workers),
|
|
67
|
-
"file-timeout-seconds": args.fileTimeoutSeconds == null ? null : String(args.fileTimeoutSeconds),
|
|
68
|
-
shard: args.shard || null,
|
|
69
|
-
seed: args.seed || null,
|
|
70
|
-
"write-status": Boolean(args.writeStatus),
|
|
71
|
-
"allow-partial-status": Boolean(args.allowPartialStatus),
|
|
72
|
-
"ignore-skip-rules": Boolean(args.ignoreSkipRules),
|
|
73
|
-
},
|
|
74
|
-
null,
|
|
75
|
-
context.productDir,
|
|
76
|
-
process.cwd()
|
|
77
|
-
);
|
|
50
|
+
async function shellExecTool(args, context) {
|
|
51
|
+
const command = String(args.command || "").trim();
|
|
52
|
+
if (!command) throw new Error("shell_exec requires a command string");
|
|
78
53
|
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
54
|
+
const shellCommand = classifyShellCommand(command);
|
|
55
|
+
const commandId = `cmd-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
56
|
+
context.commandLog?.appendCommandLog({
|
|
57
|
+
type: "command_start",
|
|
58
|
+
command: shellCommand.command,
|
|
59
|
+
commandId,
|
|
60
|
+
cwd: context.productDir,
|
|
61
|
+
raw: command,
|
|
62
|
+
});
|
|
63
|
+
context.onEvent?.({
|
|
64
|
+
type: "tool-status",
|
|
65
|
+
tool: "shell_exec",
|
|
66
|
+
message: `Running ${shellCommand.display}`,
|
|
85
67
|
});
|
|
86
68
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
await liveReporter.finalize;
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
ok: result.ok,
|
|
102
|
-
title: "Run complete",
|
|
103
|
-
text: summarizeRunResult(context.inspectState.getSnapshot()),
|
|
104
|
-
data: result,
|
|
105
|
-
};
|
|
106
|
-
} catch (error) {
|
|
107
|
-
liveReporter.close();
|
|
108
|
-
await liveReporter.finalize.catch(() => {});
|
|
109
|
-
throw error;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
69
|
+
const result = await execaCommand(command, {
|
|
70
|
+
cwd: context.productDir,
|
|
71
|
+
reject: false,
|
|
72
|
+
shell: true,
|
|
73
|
+
env: {
|
|
74
|
+
...process.env,
|
|
75
|
+
...context.env,
|
|
76
|
+
PATH: [context.commandLog?.binDir, context.env?.PATH, process.env.PATH].filter(Boolean).join(path.delimiter),
|
|
77
|
+
TESTKIT_NO_ASSISTANT_DEFAULT: "1",
|
|
78
|
+
},
|
|
79
|
+
});
|
|
112
80
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
81
|
+
context.commandLog?.appendCommandLog({
|
|
82
|
+
type: "command_exit",
|
|
83
|
+
command: shellCommand.command,
|
|
84
|
+
commandId,
|
|
85
|
+
cwd: context.productDir,
|
|
86
|
+
raw: command,
|
|
87
|
+
code: result.exitCode ?? 0,
|
|
88
|
+
signal: result.signal ?? null,
|
|
89
|
+
});
|
|
122
90
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (!service) throw new Error("focus_service requires a service argument");
|
|
126
|
-
ensureArtifactLoaded(context);
|
|
127
|
-
if (!context.inspectState.revealService(service)) {
|
|
128
|
-
throw new Error(`Unknown service "${service}"`);
|
|
91
|
+
if (shellCommand.testkitRelated) {
|
|
92
|
+
refreshArtifactSelection(context);
|
|
129
93
|
}
|
|
130
|
-
|
|
131
|
-
}
|
|
94
|
+
context.commandLog?.refresh?.();
|
|
132
95
|
|
|
133
|
-
|
|
134
|
-
const content = readContextContent({
|
|
135
|
-
productDir: context.productDir,
|
|
136
|
-
snapshot: context.inspectState.getSnapshot(),
|
|
137
|
-
mode: "detail",
|
|
138
|
-
logTail: 12,
|
|
139
|
-
});
|
|
96
|
+
const lines = formatCommandResult(command, result, shellCommand);
|
|
140
97
|
return {
|
|
141
|
-
ok:
|
|
142
|
-
title:
|
|
143
|
-
text:
|
|
98
|
+
ok: (result.exitCode ?? 0) === 0,
|
|
99
|
+
title: shellCommand.title,
|
|
100
|
+
text: lines.join("\n"),
|
|
144
101
|
data: {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
102
|
+
command,
|
|
103
|
+
stdout: result.stdout || "",
|
|
104
|
+
stderr: result.stderr || "",
|
|
105
|
+
exitCode: result.exitCode ?? 0,
|
|
106
|
+
signal: result.signal ?? null,
|
|
107
|
+
testkitRelated: shellCommand.testkitRelated,
|
|
149
108
|
},
|
|
150
109
|
};
|
|
151
110
|
}
|
|
152
111
|
|
|
153
|
-
function
|
|
154
|
-
if (args.
|
|
112
|
+
function readContextTool(args, context) {
|
|
113
|
+
if (args.file || args.path) {
|
|
114
|
+
ensureArtifactLoaded(context);
|
|
115
|
+
const artifact = context.inspectState.getSnapshot().runArtifact;
|
|
116
|
+
const subject = resolveFileSubject(artifact, args.file || args.path, args.service || null);
|
|
117
|
+
context.inspectState.revealFile(subject.service.name, subject.file.path);
|
|
118
|
+
} else if (args.service) {
|
|
155
119
|
ensureArtifactLoaded(context);
|
|
156
120
|
if (!context.inspectState.revealService(args.service)) {
|
|
157
121
|
throw new Error(`Unknown service "${args.service}"`);
|
|
158
122
|
}
|
|
159
123
|
}
|
|
124
|
+
|
|
160
125
|
const content = readContextContent({
|
|
161
126
|
productDir: context.productDir,
|
|
162
127
|
snapshot: context.inspectState.getSnapshot(),
|
|
163
|
-
mode:
|
|
128
|
+
mode: normalizeContextMode(args.mode),
|
|
164
129
|
logTail: args.logTail == null ? 12 : Number(args.logTail),
|
|
165
130
|
});
|
|
131
|
+
context.commandLog?.refresh?.();
|
|
166
132
|
return {
|
|
167
133
|
ok: true,
|
|
168
134
|
title: content.title,
|
|
169
|
-
text:
|
|
135
|
+
text: content.lines.join("\n"),
|
|
170
136
|
data: {
|
|
171
137
|
title: content.title,
|
|
172
138
|
lines: content.lines,
|
|
173
139
|
selection: content.selection,
|
|
174
|
-
mode:
|
|
140
|
+
mode: content.mode,
|
|
175
141
|
},
|
|
176
142
|
};
|
|
177
143
|
}
|
|
178
144
|
|
|
179
|
-
function
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
145
|
+
function readFileTool(args, context) {
|
|
146
|
+
const file = String(args.path || args.file || "").trim();
|
|
147
|
+
if (!file) throw new Error("read_file requires a path");
|
|
148
|
+
const resolved = resolveRepoPath(context.productDir, file);
|
|
149
|
+
if (!resolved.startsWith(path.resolve(context.productDir))) {
|
|
150
|
+
throw new Error("read_file only supports paths inside the current repository");
|
|
185
151
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
152
|
+
if (!fs.existsSync(resolved)) {
|
|
153
|
+
throw new Error(`File not found: ${file}`);
|
|
154
|
+
}
|
|
155
|
+
const startLine = Math.max(1, Number(args.startLine || args.start || 1) || 1);
|
|
156
|
+
const requestedEnd = Number(args.endLine || args.end || startLine + FILE_LINE_LIMIT - 1) || startLine + FILE_LINE_LIMIT - 1;
|
|
157
|
+
const endLine = Math.max(startLine, Math.min(requestedEnd, startLine + FILE_LINE_LIMIT - 1));
|
|
158
|
+
const lines = fs.readFileSync(resolved, "utf8").split(/\r?\n/);
|
|
159
|
+
const selected = [];
|
|
160
|
+
for (let lineNumber = startLine; lineNumber <= Math.min(endLine, lines.length); lineNumber += 1) {
|
|
161
|
+
selected.push(`${lineNumber}: ${lines[lineNumber - 1]}`);
|
|
162
|
+
}
|
|
163
|
+
const title = `File ${path.relative(context.productDir, resolved) || path.basename(resolved)}`;
|
|
192
164
|
return {
|
|
193
165
|
ok: true,
|
|
194
|
-
title
|
|
195
|
-
text:
|
|
166
|
+
title,
|
|
167
|
+
text: selected.join("\n"),
|
|
196
168
|
data: {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
169
|
+
path: resolved,
|
|
170
|
+
relativePath: path.relative(context.productDir, resolved),
|
|
171
|
+
startLine,
|
|
172
|
+
endLine,
|
|
173
|
+
lines: selected,
|
|
201
174
|
},
|
|
202
175
|
};
|
|
203
176
|
}
|
|
204
177
|
|
|
205
|
-
function
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
178
|
+
async function searchRepoTool(args, context) {
|
|
179
|
+
const query = String(args.query || args.pattern || "").trim();
|
|
180
|
+
if (!query) throw new Error("search_repo requires a query");
|
|
181
|
+
const result = await execaCommand(
|
|
182
|
+
`rg --line-number --smart-case --hidden --glob '!node_modules' --glob '!.git' ${shellQuote(query)} .`,
|
|
183
|
+
{
|
|
184
|
+
cwd: context.productDir,
|
|
185
|
+
reject: false,
|
|
186
|
+
shell: true,
|
|
210
187
|
}
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
});
|
|
188
|
+
);
|
|
189
|
+
const combined = truncateLines((result.stdout || "").split(/\r?\n/).filter(Boolean), COMMAND_LINE_LIMIT);
|
|
190
|
+
const lines =
|
|
191
|
+
combined.length > 0
|
|
192
|
+
? combined
|
|
193
|
+
: [`No matches for ${query}`];
|
|
218
194
|
return {
|
|
219
|
-
ok:
|
|
220
|
-
title:
|
|
221
|
-
text:
|
|
195
|
+
ok: (result.exitCode ?? 1) === 0,
|
|
196
|
+
title: `Search ${query}`,
|
|
197
|
+
text: lines.join("\n"),
|
|
222
198
|
data: {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
mode: "setup",
|
|
199
|
+
query,
|
|
200
|
+
matches: combined,
|
|
201
|
+
exitCode: result.exitCode ?? 1,
|
|
227
202
|
},
|
|
228
203
|
};
|
|
229
204
|
}
|
|
230
205
|
|
|
231
|
-
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
206
|
+
function classifyShellCommand(command) {
|
|
207
|
+
const normalized = command.trim();
|
|
208
|
+
if (/^(testkit)\b/.test(normalized)) {
|
|
209
|
+
return {
|
|
210
|
+
command: "testkit",
|
|
211
|
+
display: normalized,
|
|
212
|
+
title: "testkit command",
|
|
213
|
+
testkitRelated: true,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (/^(npx)\s+testkit\b/.test(normalized)) {
|
|
217
|
+
return {
|
|
218
|
+
command: "npx testkit",
|
|
219
|
+
display: normalized,
|
|
220
|
+
title: "npx testkit",
|
|
221
|
+
testkitRelated: true,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
if (/^(npm)\s+run\s+testkit\b/.test(normalized) || /^(npm)\s+run\s+testkit:/.test(normalized)) {
|
|
225
|
+
return {
|
|
226
|
+
command: "npm run testkit",
|
|
227
|
+
display: normalized,
|
|
228
|
+
title: "npm testkit script",
|
|
229
|
+
testkitRelated: true,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
244
232
|
return {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
233
|
+
command: normalized.split(/\s+/)[0] || "command",
|
|
234
|
+
display: normalized,
|
|
235
|
+
title: "Shell command",
|
|
236
|
+
testkitRelated: false,
|
|
249
237
|
};
|
|
250
238
|
}
|
|
251
239
|
|
|
252
|
-
function
|
|
253
|
-
const
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
240
|
+
function formatCommandResult(command, result, shellCommand) {
|
|
241
|
+
const lines = [`$ ${command}`];
|
|
242
|
+
const stdout = (result.stdout || "").trim();
|
|
243
|
+
const stderr = (result.stderr || "").trim();
|
|
244
|
+
const merged = [];
|
|
245
|
+
if (stdout) merged.push(...stdout.split(/\r?\n/));
|
|
246
|
+
if (stderr) merged.push(...stderr.split(/\r?\n/).map((line) => `stderr: ${line}`));
|
|
247
|
+
if (merged.length === 0) {
|
|
248
|
+
merged.push(`exit ${result.exitCode ?? 0}`);
|
|
249
|
+
}
|
|
250
|
+
const trimmed = truncateLines(merged, COMMAND_LINE_LIMIT).map((line) => truncateText(line, COMMAND_OUTPUT_LIMIT));
|
|
251
|
+
lines.push(...trimmed);
|
|
252
|
+
if ((result.exitCode ?? 0) !== 0) {
|
|
253
|
+
lines.push(`exit code: ${result.exitCode ?? 0}`);
|
|
254
|
+
} else if (!shellCommand.testkitRelated) {
|
|
255
|
+
lines.push("exit code: 0");
|
|
256
|
+
}
|
|
257
|
+
return lines;
|
|
261
258
|
}
|
|
262
259
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
260
|
+
function truncateLines(lines, limit) {
|
|
261
|
+
if (lines.length <= limit) return lines;
|
|
262
|
+
return [...lines.slice(0, limit - 1), `… ${lines.length - limit + 1} more lines omitted`];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function truncateText(value, maxLength) {
|
|
266
|
+
const normalized = String(value || "");
|
|
267
|
+
if (normalized.length <= maxLength) return normalized;
|
|
268
|
+
return `${normalized.slice(0, maxLength - 1)}…`;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function resolveRepoPath(productDir, file) {
|
|
272
|
+
return path.resolve(productDir, file);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function shellQuote(value) {
|
|
276
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function normalizeContextMode(mode) {
|
|
280
|
+
if (mode === "logs" || mode === "artifacts" || mode === "setup") return mode;
|
|
281
|
+
return "detail";
|
|
278
282
|
}
|
|
279
283
|
|
|
280
284
|
function ensureArtifactLoaded(context) {
|
|
@@ -288,28 +292,14 @@ function ensureArtifactLoaded(context) {
|
|
|
288
292
|
return context.inspectState.getSnapshot().runArtifact;
|
|
289
293
|
}
|
|
290
294
|
|
|
291
|
-
function
|
|
292
|
-
if (value == null) return [];
|
|
293
|
-
if (Array.isArray(value)) return value.filter(Boolean);
|
|
294
|
-
return [value].filter(Boolean);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function summarizeRunResult(snapshot) {
|
|
298
|
-
const rows = snapshot?.summaryData?.rows || [];
|
|
299
|
-
if (rows.length === 0) return "Run finished.";
|
|
300
|
-
return rows.map(([label, value]) => `${label}: ${value}`).join("\n");
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function captureConsoleOutput(fn) {
|
|
304
|
-
const output = [];
|
|
305
|
-
const originalLog = console.log;
|
|
306
|
-
console.log = (...args) => {
|
|
307
|
-
output.push(args.map((value) => String(value)).join(" "));
|
|
308
|
-
};
|
|
295
|
+
function refreshArtifactSelection(context) {
|
|
309
296
|
try {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
297
|
+
context.inspectState.hydrateFromArtifact(loadCurrentRunArtifact(context.productDir));
|
|
298
|
+
} catch {
|
|
299
|
+
try {
|
|
300
|
+
context.inspectState.hydrateFromArtifact(loadLatestRunArtifact(context.productDir));
|
|
301
|
+
} catch {
|
|
302
|
+
// Ignore missing artifacts.
|
|
303
|
+
}
|
|
314
304
|
}
|
|
315
305
|
}
|
|
@@ -4,7 +4,7 @@ import { createAssistantState } from "../assistant/state.mjs";
|
|
|
4
4
|
import { runInteractiveAssistant } from "../assistant/interactive.mjs";
|
|
5
5
|
|
|
6
6
|
export default class AssistantCommand extends Command {
|
|
7
|
-
static summary = "
|
|
7
|
+
static summary = "Open the testkit assistant shell";
|
|
8
8
|
|
|
9
9
|
static enableJsonFlag = true;
|
|
10
10
|
|
|
@@ -69,6 +69,11 @@ export default class AssistantCommand extends Command {
|
|
|
69
69
|
const snapshot = assistantState.getSnapshot();
|
|
70
70
|
if (!this.jsonEnabled()) {
|
|
71
71
|
for (const message of snapshot.messages) {
|
|
72
|
+
if (message.role === "tool" && message.title) {
|
|
73
|
+
this.log(`tool: ${message.title}`);
|
|
74
|
+
if (message.text) this.log(message.text);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
72
77
|
this.log(`${message.role}: ${message.text}`);
|
|
73
78
|
}
|
|
74
79
|
}
|
|
@@ -82,6 +87,7 @@ export default class AssistantCommand extends Command {
|
|
|
82
87
|
service: flags.service || null,
|
|
83
88
|
prompt: flags.prompt || null,
|
|
84
89
|
env: process.env,
|
|
90
|
+
configs: allConfigs,
|
|
85
91
|
});
|
|
86
92
|
}
|
|
87
93
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elench/testkit-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.91",
|
|
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.91"
|
|
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.91",
|
|
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.91",
|
|
86
|
+
"@elench/testkit-bridge": "0.1.91",
|
|
87
|
+
"@elench/testkit-protocol": "0.1.91",
|
|
88
|
+
"@elench/ts-analysis": "0.1.91",
|
|
89
89
|
"@oclif/core": "^4.10.6",
|
|
90
90
|
"esbuild": "^0.25.11",
|
|
91
91
|
"execa": "^9.5.0",
|