@elench/testkit 0.1.90 → 0.1.92

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.
@@ -1,280 +1,284 @@
1
- import { discoverTests } from "../../discovery/index.mjs";
2
- import { runDoctor } from "../../app/doctor.mjs";
3
- import { resolveProductDir } from "../../config/index.mjs";
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
- import { createAssistantRunReporter } from "./tool-run-reporter.mjs";
12
- import { buildRunRequest } from "../command-helpers.mjs";
13
- import * as runner from "../../runner/index.mjs";
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
- { name: "run_tests", description: "Run testkit-managed tests with optional type/service/file filters." },
18
- { name: "focus_file", description: "Focus a specific test file and summarize it." },
19
- { name: "focus_service", description: "Focus a specific service and summarize it." },
20
- { name: "inspect_focus", description: "Inspect the current focus inline in the conversation." },
21
- { name: "read_logs", description: "Read backend logs for the current focus or an explicit service." },
22
- { name: "read_artifacts", description: "Read persisted artifacts for the current focus or an explicit file." },
23
- { name: "read_setup", description: "Read setup operations for the current focus or an explicit service." },
24
- { name: "discover_tests", description: "Discover managed tests and summarize them." },
25
- { name: "show_status", description: "Show the current local testkit state for the product." },
26
- { name: "run_doctor", description: "Run built-in testkit doctor checks." },
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 "run_tests":
34
- return runTestsTool(args, context);
35
- case "focus_file":
36
- return focusFileTool(args, context);
37
- case "focus_service":
38
- return focusServiceTool(args, context);
39
- case "inspect_focus":
40
- return inspectFocusTool(args, context);
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 runTestsTool(args, context) {
59
- const request = await buildRunRequest(
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
- context.inspectState.resetForLive();
80
- const liveReporter = createAssistantRunReporter({
81
- inspectState: context.inspectState,
82
- onStatus(message) {
83
- context.onEvent?.({ type: "tool-status", tool: "run_tests", message });
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
- try {
88
- const result = await runner.runAll(
89
- request.configs,
90
- request.typeValues,
91
- request.suiteSelectors,
92
- {
93
- ...request.runOptions,
94
- reporter: liveReporter,
95
- },
96
- request.allConfigs
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
- function focusFileTool(args, context) {
114
- const file = args.file || args.path || null;
115
- if (!file) throw new Error("focus_file requires a file argument");
116
- ensureArtifactLoaded(context);
117
- const artifact = context.inspectState.getSnapshot().runArtifact;
118
- const subject = resolveFileSubject(artifact, file, args.service || null);
119
- context.inspectState.revealFile(subject.service.name, subject.file.path);
120
- return inspectFocusTool({}, context);
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
- function focusServiceTool(args, context) {
124
- const service = args.service || args.name || null;
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
- return inspectFocusTool({}, context);
131
- }
94
+ context.commandLog?.refresh?.();
132
95
 
133
- function inspectFocusTool(_args, context) {
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: true,
142
- title: content.title,
143
- text: formatContextToolText(content.title, content.lines),
98
+ ok: (result.exitCode ?? 0) === 0,
99
+ title: shellCommand.title,
100
+ text: lines.join("\n"),
144
101
  data: {
145
- title: content.title,
146
- lines: content.lines,
147
- selection: content.selection,
148
- mode: "detail",
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 readLogsTool(args, context) {
154
- if (args.service) {
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: "logs",
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: formatContextToolText(content.title, content.lines),
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: "logs",
140
+ mode: content.mode,
175
141
  },
176
142
  };
177
143
  }
178
144
 
179
- function readArtifactsTool(args, context) {
180
- if (args.file || args.path) {
181
- ensureArtifactLoaded(context);
182
- const artifact = context.inspectState.getSnapshot().runArtifact;
183
- const subject = resolveFileSubject(artifact, args.file || args.path, args.service || null);
184
- context.inspectState.revealFile(subject.service.name, subject.file.path);
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
- const content = readContextContent({
187
- productDir: context.productDir,
188
- snapshot: context.inspectState.getSnapshot(),
189
- mode: "artifacts",
190
- logTail: 12,
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: content.title,
195
- text: formatContextToolText(content.title, content.lines),
166
+ title,
167
+ text: selected.join("\n"),
196
168
  data: {
197
- title: content.title,
198
- lines: content.lines,
199
- selection: content.selection,
200
- mode: "artifacts",
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 readSetupTool(args, context) {
206
- if (args.service) {
207
- ensureArtifactLoaded(context);
208
- if (!context.inspectState.revealService(args.service)) {
209
- throw new Error(`Unknown service "${args.service}"`);
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 content = readContextContent({
213
- productDir: context.productDir,
214
- snapshot: context.inspectState.getSnapshot(),
215
- mode: "setup",
216
- logTail: 12,
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: true,
220
- title: content.title,
221
- text: formatContextToolText(content.title, content.lines),
195
+ ok: (result.exitCode ?? 1) === 0,
196
+ title: `Search ${query}`,
197
+ text: lines.join("\n"),
222
198
  data: {
223
- title: content.title,
224
- lines: content.lines,
225
- selection: content.selection,
226
- mode: "setup",
199
+ query,
200
+ matches: combined,
201
+ exitCode: result.exitCode ?? 1,
227
202
  },
228
203
  };
229
204
  }
230
205
 
231
- async function discoverTestsTool(args, context) {
232
- const productDir = resolveProductDir(process.cwd(), context.productDir);
233
- const fileNames = resolveRequestedFiles(normalizeArray(args.file), productDir, process.cwd());
234
- const result = await discoverTests({
235
- dir: productDir,
236
- service: args.service || null,
237
- type: normalizeArray(args.type),
238
- suite: normalizeArray(args.suite),
239
- file: fileNames,
240
- runnableOnly: Boolean(args.runnableOnly),
241
- diagnostics: args.strict ? "error" : "report",
242
- });
243
- const lines = buildDiscoveryReportLines(result, { outputMode: args.outputMode || "compact" });
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
- ok: true,
246
- title: "Discovery",
247
- text: lines.join("\n"),
248
- data: result,
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 showStatusTool(_args, context) {
253
- const { output } = captureConsoleOutput(() => runner.showStatus(context.configs[0]));
254
- const lines = output.length > 0 ? output : ["No state"];
255
- return {
256
- ok: true,
257
- title: "Status",
258
- text: formatContextToolText("Status", lines),
259
- data: { lines },
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
- async function runDoctorTool(args, context) {
264
- const result = await runDoctor({
265
- dir: context.productDir,
266
- typecheck: args.typecheck ?? true,
267
- });
268
- const lines = [
269
- `testkit doctor ${result.ok ? "passed" : "failed"} for ${result.productDir}`,
270
- ...result.checks.map((check) => `${String(check.level || "").toUpperCase()} ${check.code} ${check.message}`),
271
- ];
272
- return {
273
- ok: result.ok,
274
- title: "Doctor",
275
- text: formatContextToolText("Doctor", lines),
276
- data: result,
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 normalizeArray(value) {
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
- const result = fn();
311
- return { result, output };
312
- } finally {
313
- console.log = originalLog;
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 = "Launch a native Codex or Claude session with testkit context";
7
+ static summary = "Open the testkit assistant shell";
8
8
 
9
9
  static enableJsonFlag = true;
10
10
 
@@ -13,7 +13,20 @@ export default class AssistantCommand extends Command {
13
13
  provider: Flags.string({
14
14
  description: "Assistant provider",
15
15
  options: ["auto", "claude", "codex"],
16
- default: "auto",
16
+ }),
17
+ model: Flags.string({
18
+ description: "Assistant model",
19
+ }),
20
+ effort: Flags.string({
21
+ description: "Assistant effort",
22
+ options: ["low", "medium", "high", "xhigh", "max"],
23
+ }),
24
+ "provider-arg": Flags.string({
25
+ description: "Advanced provider-specific argument, repeatable",
26
+ multiple: true,
27
+ }),
28
+ "reset-assistant-settings": Flags.boolean({
29
+ description: "Reset persisted assistant provider/model settings before starting",
17
30
  }),
18
31
  file: Flags.string({
19
32
  description: "Initial file selection",
@@ -47,6 +60,10 @@ export default class AssistantCommand extends Command {
47
60
  const assistantState = createAssistantState({
48
61
  productDir,
49
62
  provider: flags.provider,
63
+ model: flags.model,
64
+ effort: flags.effort,
65
+ providerArgs: flags["provider-arg"],
66
+ resetSettings: flags["reset-assistant-settings"],
50
67
  configs: allConfigs,
51
68
  env: process.env,
52
69
  });
@@ -69,6 +86,11 @@ export default class AssistantCommand extends Command {
69
86
  const snapshot = assistantState.getSnapshot();
70
87
  if (!this.jsonEnabled()) {
71
88
  for (const message of snapshot.messages) {
89
+ if (message.role === "tool" && message.title) {
90
+ this.log(`tool: ${message.title}`);
91
+ if (message.text) this.log(message.text);
92
+ continue;
93
+ }
72
94
  this.log(`${message.role}: ${message.text}`);
73
95
  }
74
96
  }
@@ -78,10 +100,15 @@ export default class AssistantCommand extends Command {
78
100
  return runInteractiveAssistant({
79
101
  productDir,
80
102
  provider: flags.provider,
103
+ model: flags.model,
104
+ effort: flags.effort,
105
+ providerArgs: flags["provider-arg"],
106
+ resetSettings: flags["reset-assistant-settings"],
81
107
  file: flags.file || null,
82
108
  service: flags.service || null,
83
109
  prompt: flags.prompt || null,
84
110
  env: process.env,
111
+ configs: allConfigs,
85
112
  });
86
113
  }
87
114
  }
@@ -35,6 +35,9 @@ export function normalizeCliArgs(argv) {
35
35
  "--tail",
36
36
  "--log-tail",
37
37
  "--provider",
38
+ "--model",
39
+ "--effort",
40
+ "--provider-arg",
38
41
  "--message",
39
42
  "--prompt",
40
43
  ]);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/next-analysis",
3
- "version": "0.1.90",
3
+ "version": "0.1.92",
4
4
  "description": "SWC-backed Next.js source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-bridge",
3
- "version": "0.1.90",
3
+ "version": "0.1.92",
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.90"
25
+ "@elench/testkit-protocol": "0.1.92"
26
26
  },
27
27
  "private": false
28
28
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-protocol",
3
- "version": "0.1.90",
3
+ "version": "0.1.92",
4
4
  "description": "Shared browser protocol for testkit bridge and extension consumers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/ts-analysis",
3
- "version": "0.1.90",
3
+ "version": "0.1.92",
4
4
  "description": "TypeScript compiler-backed source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {