@elench/testkit 0.1.103 → 0.1.105

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.
@@ -2,6 +2,7 @@ import React, { createElement, useEffect, useMemo, useRef, useState } from "reac
2
2
  import { Box, Text, useApp, useBoxMetrics, useCursor, useInput, useStdout } from "ink";
3
3
  import { bold, cyan, dim, green, red, yellow } from "../terminal/colors.mjs";
4
4
  import { RunTreeView } from "../components/blocks/run-tree.mjs";
5
+ import { CodeBlock } from "./code-block.mjs";
5
6
  import { getComposerDisplayModel } from "./composer.mjs";
6
7
  import { MarkdownBlock } from "./markdown-block.mjs";
7
8
  import { buildAssistantViewModel } from "./view-model.mjs";
@@ -185,6 +186,7 @@ function Transcript({ view }) {
185
186
 
186
187
  function renderBlock(block) {
187
188
  if (block.format === "markdown") return renderMarkdownBlock(block);
189
+ if (block.format === "command") return renderCommandBlock(block);
188
190
  return renderPlainBlock(block);
189
191
  }
190
192
 
@@ -225,6 +227,70 @@ function renderPlainBlock(block) {
225
227
  return rendered;
226
228
  }
227
229
 
230
+ function renderCommandBlock(block) {
231
+ const marker = colorMarker(block);
232
+ const title = block.title ? bold(block.title) : bold("command");
233
+ const command = formatCommandLine(block);
234
+ const status = formatCommandStatus(block);
235
+ const codeBlock = block.codeBlock || null;
236
+ const outputLines = codeBlock ? [] : block.outputPreview?.lines || [];
237
+ const omitted = codeBlock
238
+ ? codeBlock.omittedLineCount || 0
239
+ : block.outputPreview?.omittedLineCount || block.omittedOutputLineCount || 0;
240
+
241
+ return [
242
+ createElement(
243
+ Box,
244
+ {
245
+ key: `${block.id}-command`,
246
+ borderStyle: "round",
247
+ flexDirection: "column",
248
+ paddingLeft: 1,
249
+ paddingRight: 1,
250
+ marginBottom: 1,
251
+ },
252
+ createElement(Text, { key: "title" }, `${marker} ${title}`),
253
+ command ? createElement(Text, { key: "command" }, `${dim("$")} ${command}`) : null,
254
+ status ? createElement(Text, { key: "status" }, colorCommandStatus(block, status)) : null,
255
+ codeBlock ? createElement(Text, { key: "code-gap" }, "") : null,
256
+ ...(codeBlock ? CodeBlock({ lines: codeBlock.lines, language: codeBlock.language }) : []),
257
+ ...outputLines.map((line, index) => (
258
+ createElement(Text, { key: `output-${index}` }, dim(line))
259
+ )),
260
+ omitted > 0 ? createElement(Text, { key: "omitted" }, dim(`… ${omitted} more line${omitted === 1 ? "" : "s"} omitted`)) : null,
261
+ block.text && !command && outputLines.length === 0
262
+ ? createElement(Text, { key: "text" }, colorBlockText(block, block.text))
263
+ : null
264
+ ),
265
+ ];
266
+ }
267
+
268
+ function formatCommandLine(block) {
269
+ if (!block.command) return null;
270
+ if (typeof block.command === "string") return block.command;
271
+ try {
272
+ return JSON.stringify(block.command);
273
+ } catch {
274
+ return String(block.command);
275
+ }
276
+ }
277
+
278
+ function formatCommandStatus(block) {
279
+ const status = block.status || null;
280
+ if (status === "running") return "running";
281
+ if (status === "error") return block.exitCode == null ? "failed" : `failed · exit code ${block.exitCode}`;
282
+ if (block.exitCode != null) return `completed · exit code ${block.exitCode}`;
283
+ if (status === "done") return "completed";
284
+ if (block.text && !block.command) return null;
285
+ return block.text || null;
286
+ }
287
+
288
+ function colorCommandStatus(block, status) {
289
+ if (block.status === "error") return red(status);
290
+ if (block.status === "running") return yellow(status);
291
+ return green(status);
292
+ }
293
+
228
294
  function ComposerBar({ view, busy }) {
229
295
  const ref = useRef(null);
230
296
  const metrics = useBoxMetrics(ref);
@@ -283,7 +349,7 @@ function colorMarker(block) {
283
349
  if (block.kind === "system") return red(block.marker);
284
350
  if (block.kind === "provider-error") return red(block.marker);
285
351
  if (block.kind === "provider-activity") return dim(block.marker);
286
- if (block.kind === "provider-tool") return yellow(block.marker);
352
+ if (block.kind === "provider-command") return yellow(block.marker);
287
353
  if (block.kind === "tool-running") return yellow(block.marker);
288
354
  if (block.kind === "testkit-run") return green(block.marker);
289
355
  return block.marker;
@@ -294,7 +360,7 @@ function colorBlockText(block, text) {
294
360
  if (block.kind === "system") return red(text);
295
361
  if (block.kind === "provider-error") return red(text);
296
362
  if (block.kind === "provider-activity") return dim(text);
297
- if (block.kind === "provider-tool") return yellow(text);
363
+ if (block.kind === "provider-command") return yellow(text);
298
364
  if (block.kind === "tool-running") return yellow(text);
299
365
  return text;
300
366
  }
@@ -0,0 +1,29 @@
1
+ import React, { createElement } from "react";
2
+ import { Text } from "ink";
3
+ import { dim, green, red, cyan } from "../terminal/colors.mjs";
4
+
5
+ export function CodeBlock({ lines = [], language = "text" } = {}) {
6
+ return lines.map((line, index) => (
7
+ createElement(Text, { key: `code-${index}` }, colorCodeLine(line, language))
8
+ ));
9
+ }
10
+
11
+ export function renderCodeBlockText({ lines = [], omittedLineCount = 0 } = {}) {
12
+ const rendered = [...lines.map((line) => String(line))];
13
+ if (omittedLineCount > 0) {
14
+ rendered.push(`... ${omittedLineCount} more line${omittedLineCount === 1 ? "" : "s"} omitted`);
15
+ }
16
+ return rendered;
17
+ }
18
+
19
+ export function colorCodeLine(line, language = "text") {
20
+ const text = String(line ?? "");
21
+ if (language !== "diff") return dim(text);
22
+ if (/^\+\+\+/.test(text) || /^---/.test(text) || /^diff --git\b/.test(text) || /^index\b/.test(text)) {
23
+ return cyan(text);
24
+ }
25
+ if (/^\+/.test(text)) return green(text);
26
+ if (/^-/.test(text)) return red(text);
27
+ if (/^@@/.test(text) || /^\*\*\*/.test(text)) return cyan(text);
28
+ return dim(text);
29
+ }
@@ -182,7 +182,9 @@ function codexItemStartedEvent(item) {
182
182
  function codexItemUpdatedEvent(item) {
183
183
  if (!item || typeof item !== "object") return null;
184
184
  if (item.type === "agent_message" && typeof item.text === "string") {
185
- return providerAssistantDelta(item.text);
185
+ return providerAssistantDelta(item.text, {
186
+ id: item.id || null,
187
+ });
186
188
  }
187
189
  if (item.type === "command_execution") {
188
190
  return providerToolUpdate("command", {
@@ -204,7 +206,9 @@ function codexItemUpdatedEvent(item) {
204
206
  function codexItemCompletedEvent(item) {
205
207
  if (!item || typeof item !== "object") return null;
206
208
  if (item.type === "agent_message" && typeof item.text === "string") {
207
- return providerAssistantFinal(item.text);
209
+ return providerAssistantFinal(item.text, {
210
+ id: item.id || null,
211
+ });
208
212
  }
209
213
  if (item.type === "command_execution") {
210
214
  return providerToolEnd("command", {
@@ -635,6 +635,8 @@ function handleAssistantToolEvent(state, event, appendMessage) {
635
635
  function createProviderTurnState() {
636
636
  return {
637
637
  assistantMessageId: null,
638
+ assistantMessageIdsByProviderItem: new Map(),
639
+ providerToolMessageIdsByProviderItem: new Map(),
638
640
  lastActivityText: null,
639
641
  };
640
642
  }
@@ -673,12 +675,7 @@ function handleProviderEvent(turn, event, { appendMessage, updateMessage, setSta
673
675
  return;
674
676
  }
675
677
  if (event.type === "tool-start" || event.type === "tool-update" || event.type === "tool-end") {
676
- appendProviderActivity(turn, {
677
- role: "provider-tool",
678
- title: formatProviderToolTitle(event),
679
- text: formatProviderToolText(event),
680
- data: event,
681
- }, { appendMessage, setStatus });
678
+ upsertProviderToolActivity(turn, event, { appendMessage, updateMessage, setStatus });
682
679
  return;
683
680
  }
684
681
  if (event.type === "error") {
@@ -693,15 +690,19 @@ function handleProviderEvent(turn, event, { appendMessage, updateMessage, setSta
693
690
  }
694
691
 
695
692
  function appendAssistantDelta(turn, event, { appendMessage, updateMessage }) {
696
- if (!turn.assistantMessageId) {
697
- turn.assistantMessageId = appendMessage({
693
+ const messageKey = providerAssistantMessageKey(event);
694
+ let messageId = messageKey ? turn.assistantMessageIdsByProviderItem.get(messageKey) : turn.assistantMessageId;
695
+ if (!messageId) {
696
+ messageId = appendMessage({
698
697
  role: "assistant",
699
698
  status: "streaming",
700
699
  provider: event.provider || null,
701
700
  text: "",
702
701
  });
702
+ if (messageKey) turn.assistantMessageIdsByProviderItem.set(messageKey, messageId);
703
+ else turn.assistantMessageId = messageId;
703
704
  }
704
- updateMessage(turn.assistantMessageId, (message) => ({
705
+ updateMessage(messageId, (message) => ({
705
706
  text: `${message.text || ""}${event.text || ""}`,
706
707
  status: "streaming",
707
708
  }));
@@ -709,20 +710,87 @@ function appendAssistantDelta(turn, event, { appendMessage, updateMessage }) {
709
710
 
710
711
  function finalizeAssistantMessage(turn, event, { appendMessage, updateMessage }) {
711
712
  const finalText = event.text || "";
712
- if (!turn.assistantMessageId) {
713
- turn.assistantMessageId = appendMessage({
713
+ const messageKey = providerAssistantMessageKey(event);
714
+ let messageId = messageKey ? turn.assistantMessageIdsByProviderItem.get(messageKey) : turn.assistantMessageId;
715
+ if (!messageId) {
716
+ messageId = appendMessage({
714
717
  role: "assistant",
715
718
  provider: event.provider || null,
716
719
  text: finalText,
717
720
  });
721
+ if (messageKey) turn.assistantMessageIdsByProviderItem.set(messageKey, messageId);
722
+ else turn.assistantMessageId = messageId;
718
723
  return;
719
724
  }
720
- updateMessage(turn.assistantMessageId, (message) => ({
725
+ updateMessage(messageId, (message) => ({
721
726
  text: finalText || message.text || "",
722
727
  status: null,
723
728
  }));
724
729
  }
725
730
 
731
+ function providerAssistantMessageKey(event) {
732
+ if (!event?.id) return null;
733
+ return `${event.provider || "provider"}:${event.id}`;
734
+ }
735
+
736
+ function upsertProviderToolActivity(turn, event, { appendMessage, updateMessage, setStatus }) {
737
+ const messageKey = providerToolMessageKey(event);
738
+ const status = providerToolStatus(event);
739
+ const buildMessage = (data) => ({
740
+ role: "provider-tool",
741
+ title: formatProviderToolTitle(data),
742
+ text: formatProviderToolText(data),
743
+ status,
744
+ data,
745
+ });
746
+
747
+ if (!messageKey) {
748
+ appendProviderActivity(turn, buildMessage(event), { appendMessage, setStatus });
749
+ return;
750
+ }
751
+
752
+ const existingMessageId = turn.providerToolMessageIdsByProviderItem.get(messageKey);
753
+ if (!existingMessageId) {
754
+ const messageId = appendMessage(buildMessage(event));
755
+ turn.providerToolMessageIdsByProviderItem.set(messageKey, messageId);
756
+ setStatus?.(formatProviderToolStatusLine(event));
757
+ return;
758
+ }
759
+
760
+ updateMessage(existingMessageId, (message) => {
761
+ const data = mergeProviderToolEventData(message.data, event);
762
+ return buildMessage(data);
763
+ });
764
+ setStatus?.(formatProviderToolStatusLine(event));
765
+ }
766
+
767
+ function providerToolMessageKey(event) {
768
+ if (!event?.id) return null;
769
+ return `${event.provider || "provider"}:${event.id}`;
770
+ }
771
+
772
+ function mergeProviderToolEventData(previous, event) {
773
+ const merged = {
774
+ ...(previous || {}),
775
+ ...(event || {}),
776
+ };
777
+ if (previous?.input != null && event?.input == null) merged.input = previous.input;
778
+ if (previous?.detail != null && event?.detail == null) merged.detail = previous.detail;
779
+ if (previous?.output != null && event?.output == null) merged.output = previous.output;
780
+ if (previous?.text != null && event?.text == null) merged.text = previous.text;
781
+ merged.data = {
782
+ ...(previous?.data || {}),
783
+ ...(event?.data || {}),
784
+ };
785
+ return merged;
786
+ }
787
+
788
+ function providerToolStatus(event) {
789
+ if (event.type === "tool-start" || event.type === "tool-update") return "running";
790
+ if (event.status === "error") return "error";
791
+ return "done";
792
+ }
793
+
726
794
  function appendProviderActivity(turn, message, { appendMessage, setStatus }) {
727
795
  const text = String(message.text || "").trim();
728
796
  if (!text) return;
@@ -740,21 +808,24 @@ function formatProviderName(provider) {
740
808
  function formatProviderToolTitle(event) {
741
809
  const provider = formatProviderName(event.provider);
742
810
  const name = event.name || "tool";
743
- if (event.type === "tool-end") return `${provider} finished ${name}`;
744
- if (event.type === "tool-update") return `${provider} updated ${name}`;
745
- return `${provider} started ${name}`;
811
+ return `${provider} ${name}`;
746
812
  }
747
813
 
748
814
  function formatProviderToolText(event) {
749
815
  const lines = [];
750
816
  if (event.detail) lines.push(String(event.detail));
751
- if (event.text) lines.push(String(event.text));
752
817
  if (event.input) lines.push(formatProviderData("input", event.input));
753
818
  if (event.output) lines.push(formatProviderData("output", event.output));
754
819
  if (event.status) lines.push(`status: ${event.status}`);
755
820
  return lines.filter(Boolean).join("\n") || event.name || "Provider tool activity";
756
821
  }
757
822
 
823
+ function formatProviderToolStatusLine(event) {
824
+ const title = formatProviderToolTitle(event);
825
+ const status = providerToolStatus(event);
826
+ return `${title}: ${status}`;
827
+ }
828
+
758
829
  function formatProviderData(label, value) {
759
830
  if (value == null) return null;
760
831
  if (typeof value === "string") return `${label}: ${value}`;
@@ -1,5 +1,6 @@
1
1
  import stripAnsi from "strip-ansi";
2
2
  import { buildAssistantViewModel } from "./view-model.mjs";
3
+ import { renderCodeBlockText } from "./code-block.mjs";
3
4
  import { renderMarkdownToAnsi } from "./markdown-block.mjs";
4
5
 
5
6
  export function renderAssistantSnapshotText(snapshot, { cwd = process.cwd(), ansi = false } = {}) {
@@ -25,9 +26,65 @@ function renderBlockLines(block, { ansi = false } = {}) {
25
26
  const normalized = ansi ? rendered : stripAnsi(rendered);
26
27
  return prefixLines(`${marker}${title}`, normalized);
27
28
  }
29
+ if (block.format === "command") {
30
+ return renderCommandBlockLines(block);
31
+ }
28
32
  return prefixLines(`${marker}${title}`, text);
29
33
  }
30
34
 
35
+ function renderCommandBlockLines(block) {
36
+ const header = `${block.marker || ""}${block.title ? ` ${block.title}` : ""}`.trim();
37
+ const body = [];
38
+ const command = formatCommandLine(block.command);
39
+ const status = formatCommandStatus(block);
40
+ if (command) body.push(`$ ${command}`);
41
+ if (status) body.push(status);
42
+ if (block.codeBlock) {
43
+ body.push("");
44
+ body.push(...renderCodeBlockText(block.codeBlock));
45
+ } else {
46
+ for (const line of block.outputPreview?.lines || []) body.push(String(line));
47
+ const omitted = block.outputPreview?.omittedLineCount || block.omittedOutputLineCount || 0;
48
+ if (omitted > 0) body.push(`... ${omitted} more line${omitted === 1 ? "" : "s"} omitted`);
49
+ }
50
+ if (!command && body.length === 0 && block.text) body.push(String(block.text));
51
+ return boxLines([header, ...body].filter(Boolean));
52
+ }
53
+
54
+ function formatCommandLine(command) {
55
+ if (!command) return null;
56
+ if (typeof command === "string") return command;
57
+ try {
58
+ return JSON.stringify(command);
59
+ } catch {
60
+ return String(command);
61
+ }
62
+ }
63
+
64
+ function formatCommandStatus(block) {
65
+ if (block.status === "running") return "running";
66
+ if (block.status === "error") return block.exitCode == null ? "failed" : `failed · exit code ${block.exitCode}`;
67
+ if (block.exitCode != null) return `completed · exit code ${block.exitCode}`;
68
+ if (block.status === "done") return "completed";
69
+ if (block.text && !block.command) return block.text;
70
+ return null;
71
+ }
72
+
73
+ function boxLines(lines) {
74
+ const width = Math.max(1, ...lines.map((line) => visibleWidth(line)));
75
+ const top = `╭${"─".repeat(width + 2)}╮`;
76
+ const bottom = `╰${"─".repeat(width + 2)}╯`;
77
+ return [
78
+ top,
79
+ ...lines.map((line) => `│ ${line}${" ".repeat(width - visibleWidth(line))} │`),
80
+ bottom,
81
+ ];
82
+ }
83
+
84
+ function visibleWidth(value) {
85
+ return stripAnsi(String(value || "")).length;
86
+ }
87
+
31
88
  function prefixLines(prefix, text) {
32
89
  const lines = String(text || "").split(/\r?\n/);
33
90
  if (lines.length === 0 || (lines.length === 1 && lines[0] === "")) return [prefix];
@@ -1,6 +1,9 @@
1
1
  import path from "path";
2
2
  import { formatContextRemaining } from "./context-window.mjs";
3
3
 
4
+ const PROVIDER_COMMAND_OUTPUT_PREVIEW_LINES = 12;
5
+ const PROVIDER_DIFF_PREVIEW_LINES = 80;
6
+
4
7
  export function buildAssistantViewModel(snapshot, { cwd = process.cwd(), terminalWidth = 100 } = {}) {
5
8
  const providerLabel = buildProviderLabel(snapshot);
6
9
  const repoName = path.basename(cwd || process.cwd()) || "repository";
@@ -56,7 +59,7 @@ export function buildTranscriptBlocks(messages) {
56
59
  return {
57
60
  id: message.id,
58
61
  kind: classifyToolBlock(message),
59
- format: "structured",
62
+ format: "command",
60
63
  marker: "●",
61
64
  title: message.title || message.toolName || "Tool",
62
65
  text: message.text || "",
@@ -65,7 +68,19 @@ export function buildTranscriptBlocks(messages) {
65
68
  exitCode: message.data?.exitCode ?? null,
66
69
  };
67
70
  }
68
- if (role === "provider-activity" || role === "provider-tool" || role === "provider-error") {
71
+ if (role === "provider-tool") {
72
+ return {
73
+ id: message.id,
74
+ kind: "provider-command",
75
+ format: "command",
76
+ marker: "◌",
77
+ title: message.title || "provider command",
78
+ text: message.text || "",
79
+ status: message.status || null,
80
+ ...buildProviderCommandModel(message),
81
+ };
82
+ }
83
+ if (role === "provider-activity" || role === "provider-error") {
69
84
  return {
70
85
  id: message.id,
71
86
  kind: role,
@@ -137,6 +152,106 @@ function classifyToolBlock(message) {
137
152
  return "tool-result";
138
153
  }
139
154
 
155
+ function buildProviderCommandModel(message) {
156
+ const event = message.data || {};
157
+ const rawCommand = event.input || event.data?.command || event.data?.input || null;
158
+ const output = event.output || event.data?.aggregated_output || event.data?.output || "";
159
+ const exitCode = event.data?.exit_code ?? event.data?.exitCode ?? null;
160
+ const status = message.status || providerCommandStatus(event);
161
+ const diffText = extractProviderDiffText(event);
162
+ const diffPreview = diffText ? summarizeOutput(diffText, PROVIDER_DIFF_PREVIEW_LINES) : null;
163
+ const outputPreview = summarizeOutput(output, PROVIDER_COMMAND_OUTPUT_PREVIEW_LINES);
164
+ return {
165
+ command: diffPreview ? summarizeProviderEditCommand(event, rawCommand) : rawCommand,
166
+ exitCode,
167
+ codeBlock: diffPreview
168
+ ? {
169
+ language: "diff",
170
+ lines: diffPreview.lines,
171
+ omittedLineCount: diffPreview.omittedLineCount,
172
+ }
173
+ : null,
174
+ outputPreview,
175
+ outputLineCount: countLines(output),
176
+ omittedOutputLineCount: outputPreview.omittedLineCount,
177
+ status,
178
+ };
179
+ }
180
+
181
+ function summarizeProviderEditCommand(event, rawCommand) {
182
+ const name = event.name ? String(event.name) : "edit";
183
+ if (name && name !== "command") return name;
184
+ const command = String(rawCommand || "").trim();
185
+ if (!command) return "file edit";
186
+ if (looksLikeDiff(command)) return "file edit";
187
+ return command;
188
+ }
189
+
190
+ function extractProviderDiffText(event) {
191
+ const candidates = [
192
+ event.input,
193
+ event.detail,
194
+ event.text,
195
+ event.output,
196
+ event.data?.arguments,
197
+ event.data?.input,
198
+ event.data?.patch,
199
+ event.data?.diff,
200
+ event.data?.aggregated_output,
201
+ event.data?.output,
202
+ ];
203
+ for (const candidate of candidates) {
204
+ const text = stringifyMaybe(candidate);
205
+ if (looksLikeDiff(text)) return text;
206
+ }
207
+ return null;
208
+ }
209
+
210
+ function stringifyMaybe(value) {
211
+ if (value == null) return "";
212
+ if (typeof value === "string") return value;
213
+ try {
214
+ return JSON.stringify(value, null, 2);
215
+ } catch {
216
+ return String(value);
217
+ }
218
+ }
219
+
220
+ function looksLikeDiff(value) {
221
+ const text = String(value || "");
222
+ if (!text.trim()) return false;
223
+ if (/\*\*\* Begin Patch[\s\S]*\*\*\* End Patch/.test(text)) return true;
224
+ if (/^diff --git\b/m.test(text)) return true;
225
+ if (/^@@\s/m.test(text)) return true;
226
+ if (/^\+\+\+\s.+\n---\s.+/m.test(text) || /^---\s.+\n\+\+\+\s.+/m.test(text)) return true;
227
+ const changedLines = text.split(/\r?\n/).filter((line) => /^[+-](?![+-]{2})/.test(line));
228
+ return changedLines.length >= 2 && /(patch|diff|apply_patch|edit|update file|add file|delete file)/i.test(text);
229
+ }
230
+
231
+ function providerCommandStatus(event) {
232
+ if (event.type === "tool-start" || event.type === "tool-update") return "running";
233
+ if (event.status === "error") return "error";
234
+ return event.status ? "done" : null;
235
+ }
236
+
237
+ function summarizeOutput(value, maxLines) {
238
+ const text = String(value || "").replace(/\r/g, "");
239
+ if (!text.trim()) return { lines: [], omittedLineCount: 0 };
240
+ const lines = text.split("\n");
241
+ const trimmedLines = lines.at(-1) === "" ? lines.slice(0, -1) : lines;
242
+ return {
243
+ lines: trimmedLines.slice(0, maxLines),
244
+ omittedLineCount: Math.max(0, trimmedLines.length - maxLines),
245
+ };
246
+ }
247
+
248
+ function countLines(value) {
249
+ const text = String(value || "").replace(/\r/g, "");
250
+ if (!text.trim()) return 0;
251
+ const lines = text.split("\n");
252
+ return lines.at(-1) === "" ? lines.length - 1 : lines.length;
253
+ }
254
+
140
255
  function shortenHome(value) {
141
256
  const text = String(value || "");
142
257
  const home = process.env.HOME;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/next-analysis",
3
- "version": "0.1.103",
3
+ "version": "0.1.105",
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.103",
3
+ "version": "0.1.105",
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.103"
25
+ "@elench/testkit-protocol": "0.1.105"
26
26
  },
27
27
  "private": false
28
28
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit-protocol",
3
- "version": "0.1.103",
3
+ "version": "0.1.105",
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.103",
3
+ "version": "0.1.105",
4
4
  "description": "TypeScript compiler-backed source analysis primitives for Erench tools",
5
5
  "type": "module",
6
6
  "exports": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elench/testkit",
3
- "version": "0.1.103",
3
+ "version": "0.1.105",
4
4
  "description": "Assistant-first CLI for running, inspecting, and debugging local testkit suites",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -90,10 +90,10 @@
90
90
  },
91
91
  "dependencies": {
92
92
  "@babel/code-frame": "^7.29.0",
93
- "@elench/next-analysis": "0.1.103",
94
- "@elench/testkit-bridge": "0.1.103",
95
- "@elench/testkit-protocol": "0.1.103",
96
- "@elench/ts-analysis": "0.1.103",
93
+ "@elench/next-analysis": "0.1.105",
94
+ "@elench/testkit-bridge": "0.1.105",
95
+ "@elench/testkit-protocol": "0.1.105",
96
+ "@elench/ts-analysis": "0.1.105",
97
97
  "@oclif/core": "^4.10.6",
98
98
  "esbuild": "^0.25.11",
99
99
  "execa": "^9.5.0",