tracegist-mcp-bridge 0.2.11 → 0.2.14

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 CHANGED
@@ -37,16 +37,19 @@ Set `TRACEGIST_DIR` to change where the bridge looks for packages (defaults to `
37
37
 
38
38
  ## MCP tools
39
39
 
40
- - `list_tracegist_packages` — list TraceGist package zips in `TRACEGIST_DIR` (or `~/Downloads`)
41
- - `get_tracegist_package_overview` manifest + truncated handoff preview + file list (preview capped at 12K chars)
42
- - `get_tracegist_handoff_markdown` full handoff markdown (use this for complete content)
43
- - `read_tracegist_package_file` read any text file from inside the ZIP without writing to disk (e.g. `network/api-requests.jsonl`, the Playwright repro script)
44
- - `extract_tracegist_package_file` write a binary or text file from the ZIP to a local directory
45
- - `transcribe_tracegist_package_voice_notes` transcribe voice notes using local Python Whisper
46
- - `watch_live_session` poll real-time session events while recording is active
47
- - `get_live_screenshot` request and retrieve a screenshot of the current tab
48
- - `ask_tester_question` send a question to the tester (shown as a browser toast, max 200 chars)
49
- - `get_tester_response` retrieve the tester's voice + screenshot answer
40
+ | Tool | Description |
41
+ | ------------------------------------------ | --------------------------------------------------------------- |
42
+ | `list_tracegist_packages` | List package ZIPs in Downloads |
43
+ | `get_tracegist_package_overview` | Manifest + truncated handoff preview |
44
+ | `get_tracegist_handoff_markdown` | Full handoff markdown |
45
+ | `read_tracegist_package_file` | Read any file from the ZIP (e.g. `network/api-requests.jsonl`) |
46
+ | `extract_tracegist_package_file` | Extract a file to disk |
47
+ | `transcribe_tracegist_package_voice_notes` | Local Whisper transcription |
48
+ | `watch_live_session` | Poll real-time session events while recording is active |
49
+ | `get_live_screenshot` | Request and retrieve a screenshot of the current tab |
50
+ | `ask_tester_question` | Send a question to the tester (shown as a browser toast) |
51
+ | `get_tester_response` | Retrieve the tester's voice + screenshot answer |
52
+ | `get_live_session_summary` | Complete interaction log and voice transcriptions after session |
50
53
 
51
54
  ## Package contents
52
55
 
package/bin/lib.mjs CHANGED
@@ -11,14 +11,24 @@ export function normalizeZipBaseName(entryName) {
11
11
  return path.posix.basename(entryName).toLowerCase();
12
12
  }
13
13
 
14
- export function renderPackagesText(packages, directory) {
14
+ export function renderPackagesText(
15
+ packages,
16
+ directory,
17
+ totalCount = null,
18
+ hasMore = false,
19
+ offset = 0,
20
+ ) {
15
21
  if (packages.length === 0) {
16
22
  return [
17
23
  `No TraceGist package zips found in ${directory}.`,
18
24
  "Expected naming pattern: tracegist-...-package.zip",
19
25
  ].join("\n");
20
26
  }
21
- const lines = [`Found ${packages.length} TraceGist package(s) in ${directory}:`, ""];
27
+ const countLabel =
28
+ totalCount != null
29
+ ? `Showing ${packages.length} of ${totalCount} TraceGist package(s) in ${directory}:`
30
+ : `Found ${packages.length} TraceGist package(s) in ${directory}:`;
31
+ const lines = [countLabel, ""];
22
32
  for (const pkg of packages) {
23
33
  lines.push(
24
34
  `- ${pkg.name}`,
@@ -28,6 +38,16 @@ export function renderPackagesText(packages, directory) {
28
38
  "",
29
39
  );
30
40
  }
41
+ if (hasMore) {
42
+ lines.push(
43
+ `More packages available. Use offset: ${offset + packages.length} to see the next page.`,
44
+ "",
45
+ );
46
+ }
47
+ lines.push(
48
+ "Each package includes a ready-to-run Playwright repro script.",
49
+ "Call get_tracegist_handoff_markdown (no section param) on a package to see the TOC with repro script paths.",
50
+ );
31
51
  return lines.join("\n");
32
52
  }
33
53
 
@@ -74,7 +94,8 @@ const SECTION_HINTS = {
74
94
  "user interaction timeline": "Full click/fill/navigation sequence — key for reproduction",
75
95
  "session context": "Page URL, title, session metadata",
76
96
  "session environment": "Browser, viewport, OS — match for reproduction",
77
- "package files": "Paths to Playwright script, manifest, network bodies",
97
+ "package files":
98
+ "ACTION: Read the Python repro script path here — use read_tracegist_package_file before writing any automation",
78
99
  "marker-to-file mapping": "Which screenshots/voice files belong to each marker",
79
100
  "backend log correlation": "Absolute timestamps for server-side log alignment",
80
101
  "full session console timeline": "All console output (deep exports only)",
@@ -90,7 +111,7 @@ const SECTION_HINTS = {
90
111
  "environment at marker time": "Environment snapshot at a specific marker",
91
112
  "marker timeline logs (context window)": "Logs within the ±5 s marker window",
92
113
  "webapp testing reproduction":
93
- "Server setup, Python repro script, verification points, key selectors",
114
+ "ACTION: Ready-to-run Python repro script path + key selectors — read before writing any automation",
94
115
  "tester intent summary": "Classified intent: specifications, issues found, observations",
95
116
  "iterative context": "Previous session reference, verification checklist for build-test cycle",
96
117
  "tier 0: quick summary": "Session overview, intent, reproduction command (~200 tokens)",
@@ -103,14 +124,40 @@ function getSectionHint(sectionName) {
103
124
  return SECTION_HINTS[sectionName.toLowerCase()] || "Additional section";
104
125
  }
105
126
 
106
- export function renderSectionToc(sections, zipPath) {
127
+ export function renderSectionToc(sections, zipPath, manifest = null) {
107
128
  const totalChars = sections.reduce((sum, s) => sum + s.content.length, 0);
108
129
  const lines = [
109
130
  `Handoff document for ${path.basename(zipPath)} — ${sections.length} sections, ${totalChars.toLocaleString()} chars total.`,
110
131
  "",
132
+ ];
133
+
134
+ // Prominent callout BEFORE the table — shown before any section is read
135
+ const pythonScript = manifest?.pythonPlaywrightScriptPath;
136
+ const tsScript = manifest?.playwrightScriptPath;
137
+ if (pythonScript || tsScript) {
138
+ lines.push(
139
+ "**Playwright repro script is ready — read it before writing any automation code:**",
140
+ );
141
+ if (pythonScript) {
142
+ lines.push(
143
+ `- Python (webapp-testing skill): \`read_tracegist_package_file(zipPath, "${pythonScript}")\``,
144
+ );
145
+ }
146
+ if (tsScript) {
147
+ lines.push(
148
+ `- TypeScript (Playwright Test): \`read_tracegist_package_file(zipPath, "${tsScript}")\``,
149
+ );
150
+ }
151
+ lines.push("");
152
+ }
153
+
154
+ lines.push(
155
+ "**Reading guide:** For analysis, start with Session Triage → Marker Timeline → Notable Anomalies.",
156
+ "For reproduction, start with the Playwright repro script above → Session Environment → User Interaction Timeline.",
157
+ "",
111
158
  "| # | Section | Size | Purpose |",
112
159
  "|---|---------|------|---------|",
113
- ];
160
+ );
114
161
  for (let i = 0; i < sections.length; i++) {
115
162
  const hint = getSectionHint(sections[i].name);
116
163
  lines.push(
@@ -118,9 +165,6 @@ export function renderSectionToc(sections, zipPath) {
118
165
  );
119
166
  }
120
167
  lines.push(
121
- "",
122
- "**Reading guide:** For analysis, start with Session Triage → Marker Timeline → Notable Anomalies.",
123
- "For reproduction, start with Session Environment → User Interaction Timeline → Package Files (Playwright script path).",
124
168
  "",
125
169
  'Pass `section` with a section name (e.g. "Marker Timeline") to retrieve its full content.',
126
170
  );
@@ -85,7 +85,7 @@ async function checkWhisperDependencies() {
85
85
  return warnings;
86
86
  }
87
87
 
88
- async function listTraceGistPackages(directory, limit) {
88
+ async function listTraceGistPackages(directory, limit, offset = 0) {
89
89
  const entries = await fs.readdir(directory, { withFileTypes: true });
90
90
  const zipNames = entries
91
91
  .filter((entry) => entry.isFile() && isTraceGistPackageFile(entry.name))
@@ -105,7 +105,9 @@ async function listTraceGistPackages(directory, limit) {
105
105
  );
106
106
 
107
107
  packages.sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt));
108
- return packages.slice(0, limit);
108
+ const totalCount = packages.length;
109
+ const sliced = packages.slice(offset, offset + limit);
110
+ return { packages: sliced, totalCount, hasMore: offset + limit < totalCount };
109
111
  }
110
112
 
111
113
  async function readZipEntries(zipPath) {
@@ -414,6 +416,7 @@ server.registerPrompt(
414
416
  server.registerTool(
415
417
  "transcribe_tracegist_package_voice_notes",
416
418
  {
419
+ title: "Transcribe Voice Notes",
417
420
  description:
418
421
  "Transcribe voice-note files inside a TraceGist package zip using local Python Whisper (no external API).",
419
422
  annotations: {
@@ -435,6 +438,16 @@ server.registerTool(
435
438
  .optional()
436
439
  .describe("ISO 639-1 language code (e.g. en, de, ja). Omit for auto-detection."),
437
440
  }),
441
+ outputSchema: {
442
+ zipPath: z.string(),
443
+ model: z.string(),
444
+ language: z.string().nullable(),
445
+ voiceFileCount: z.number().optional(),
446
+ transcribedCount: z.number().optional(),
447
+ transcriptions: z.array(z.object({ entryName: z.string(), transcript: z.string() })),
448
+ failures: z.array(z.object({ entryName: z.string(), error: z.string() })).optional(),
449
+ warning: z.string().optional(),
450
+ },
438
451
  },
439
452
  async ({ zipPath, model = "base", language }) => {
440
453
  try {
@@ -536,9 +549,10 @@ server.registerTool(
536
549
  failures,
537
550
  };
538
551
 
552
+ const hasError = transcriptions.length === 0 && failures.length > 0;
539
553
  return {
540
554
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
541
- ...(transcriptions.length === 0 && failures.length > 0 ? { isError: true } : {}),
555
+ ...(hasError ? { isError: true } : { structuredContent: result }),
542
556
  };
543
557
  } catch (err) {
544
558
  return toolError(err);
@@ -549,6 +563,7 @@ server.registerTool(
549
563
  server.registerTool(
550
564
  "list_tracegist_packages",
551
565
  {
566
+ title: "List Packages",
552
567
  description:
553
568
  "List TraceGist marker package zip files. Searches TRACEGIST_DIR (env var), falls back to ~/Downloads, or uses the given directory.",
554
569
  annotations: {
@@ -569,14 +584,51 @@ server.registerTool(
569
584
  .max(200)
570
585
  .optional()
571
586
  .describe("Maximum number of packages to return (default: 20)."),
587
+ offset: z
588
+ .number()
589
+ .int()
590
+ .min(0)
591
+ .optional()
592
+ .describe("Number of packages to skip for pagination (default: 0)."),
572
593
  }),
594
+ outputSchema: {
595
+ totalCount: z.number(),
596
+ count: z.number(),
597
+ offset: z.number(),
598
+ hasMore: z.boolean(),
599
+ packages: z.array(
600
+ z.object({
601
+ name: z.string(),
602
+ path: z.string(),
603
+ sizeBytes: z.number(),
604
+ modifiedAt: z.string(),
605
+ }),
606
+ ),
607
+ },
573
608
  },
574
- async ({ directory, limit = 20 }) => {
609
+ async ({ directory, limit = 20, offset = 0 }) => {
575
610
  try {
576
611
  const searchDir = directory || DEFAULT_DOWNLOADS_DIR;
577
- const packages = await listTraceGistPackages(searchDir, limit);
612
+ const { packages, totalCount, hasMore } = await listTraceGistPackages(
613
+ searchDir,
614
+ limit,
615
+ offset,
616
+ );
617
+ const structured = {
618
+ totalCount,
619
+ count: packages.length,
620
+ offset,
621
+ hasMore,
622
+ packages,
623
+ };
578
624
  return {
579
- content: [{ type: "text", text: renderPackagesText(packages, searchDir) }],
625
+ content: [
626
+ {
627
+ type: "text",
628
+ text: renderPackagesText(packages, searchDir, totalCount, hasMore, offset),
629
+ },
630
+ ],
631
+ structuredContent: structured,
580
632
  };
581
633
  } catch (err) {
582
634
  return toolError(err);
@@ -587,6 +639,7 @@ server.registerTool(
587
639
  server.registerTool(
588
640
  "get_tracegist_package_overview",
589
641
  {
642
+ title: "Package Overview",
590
643
  description:
591
644
  "Quick metadata overview of a TraceGist package: manifest and file list. " +
592
645
  "Does NOT include handoff content — call get_tracegist_handoff_markdown (no section param → table of contents, then load sections by name) for the actual handoff. " +
@@ -600,6 +653,15 @@ server.registerTool(
600
653
  inputSchema: z.object({
601
654
  zipPath: z.string().describe("Absolute path to the TraceGist package zip file."),
602
655
  }),
656
+ outputSchema: {
657
+ zipPath: z.string(),
658
+ entryCount: z.number(),
659
+ manifestEntryName: z.string().nullable(),
660
+ handoffEntryName: z.string().nullable(),
661
+ manifest: z.any().nullable(),
662
+ entries: z.array(z.string()),
663
+ hint: z.string(),
664
+ },
603
665
  },
604
666
  async ({ zipPath }) => {
605
667
  try {
@@ -619,6 +681,7 @@ server.registerTool(
619
681
 
620
682
  return {
621
683
  content: [{ type: "text", text: JSON.stringify(overview, null, 2) }],
684
+ structuredContent: overview,
622
685
  };
623
686
  } catch (err) {
624
687
  return toolError(err);
@@ -629,6 +692,7 @@ server.registerTool(
629
692
  server.registerTool(
630
693
  "get_tracegist_handoff_markdown",
631
694
  {
695
+ title: "Handoff Markdown",
632
696
  description:
633
697
  "Extract coding agent handoff markdown from a TraceGist package zip. " +
634
698
  "Voice note transcripts are automatically generated (via local Whisper) and injected inline " +
@@ -715,7 +779,7 @@ server.registerTool(
715
779
  content: [
716
780
  {
717
781
  type: "text",
718
- text: `# Source: ${handoffEntryName}\n\n${renderSectionToc(sections, zipPath)}`,
782
+ text: `# Source: ${handoffEntryName}\n\n${renderSectionToc(sections, zipPath, manifest)}`,
719
783
  },
720
784
  ],
721
785
  };
@@ -753,6 +817,7 @@ server.registerTool(
753
817
  server.registerTool(
754
818
  "read_tracegist_package_file",
755
819
  {
820
+ title: "Read Package File",
756
821
  description:
757
822
  "Read a text file directly from inside a TraceGist package zip and return its content without writing to disk. " +
758
823
  "Common uses: `network/api-requests.jsonl` for request/response bodies, or the Playwright repro script " +
@@ -793,6 +858,7 @@ server.registerTool(
793
858
  server.registerTool(
794
859
  "extract_tracegist_package_file",
795
860
  {
861
+ title: "Extract Package File",
796
862
  description: "Extract one file from a TraceGist package zip to a local directory.",
797
863
  annotations: {
798
864
  readOnlyHint: false,
@@ -1237,24 +1303,34 @@ server.resource(
1237
1303
  );
1238
1304
 
1239
1305
  // Tool: watch_live_session
1240
- server.tool(
1306
+ server.registerTool(
1241
1307
  "watch_live_session",
1242
- "Watch a live TraceGist shadowing session in real-time. Returns buffered events since the given sequence number.\n\n" +
1243
- "IMPORTANT POLLING BEHAVIOR:\n" +
1244
- "- You MUST keep calling this tool every 2-4 seconds in a loop until sessionActive becomes false.\n" +
1245
- "- Do NOT stop polling just because there are no new events — the tester is still active.\n" +
1246
- "- When you see 'voice-transcription' events, the tester recorded a voice marker — read the transcription and react to it.\n" +
1247
- "- When you see 'question-response-received' events, use get_tester_response to retrieve the full answer with images.\n" +
1248
- "- When you see 'question-response-transcribed' events, the tester's voice response has been transcribed — read it for context.\n" +
1249
- "- Marker events with hasPendingVoice=true will have their voiceTranscription field populated once transcription completes.\n" +
1250
- "- The tester may set voice markers at any time to communicate with you — keep watching for them.\n" +
1251
- "- If you need clarification, use ask_tester_question to show a toast notification to the tester.\n" +
1252
- "- Only stop polling when sessionActive is false (the recording ended).",
1253
1308
  {
1254
- since_seq: z
1255
- .number()
1256
- .optional()
1257
- .describe("Return events after this sequence number. Omit for all buffered events."),
1309
+ title: "Watch Live Session",
1310
+ description:
1311
+ "Watch a live TraceGist shadowing session in real-time. Returns buffered events since the given sequence number.\n\n" +
1312
+ "IMPORTANT POLLING BEHAVIOR:\n" +
1313
+ "- You MUST keep calling this tool every 2-4 seconds in a loop until sessionActive becomes false.\n" +
1314
+ "- Do NOT stop polling just because there are no new events — the tester is still active.\n" +
1315
+ "- When you see 'voice-transcription' events, the tester recorded a voice marker — read the transcription and react to it.\n" +
1316
+ "- When you see 'question-response-received' events, use get_tester_response to retrieve the full answer with images.\n" +
1317
+ "- When you see 'question-response-transcribed' events, the tester's voice response has been transcribed — read it for context.\n" +
1318
+ "- Marker events with hasPendingVoice=true will have their voiceTranscription field populated once transcription completes.\n" +
1319
+ "- The tester may set voice markers at any time to communicate with you — keep watching for them.\n" +
1320
+ "- If you need clarification, use ask_tester_question to show a toast notification to the tester.\n" +
1321
+ "- Only stop polling when sessionActive is false (the recording ended).",
1322
+ annotations: {
1323
+ readOnlyHint: true,
1324
+ destructiveHint: false,
1325
+ idempotentHint: true,
1326
+ openWorldHint: false,
1327
+ },
1328
+ inputSchema: z.object({
1329
+ since_seq: z
1330
+ .number()
1331
+ .optional()
1332
+ .describe("Return events after this sequence number. Omit for all buffered events."),
1333
+ }),
1258
1334
  },
1259
1335
  async ({ since_seq }) => {
1260
1336
  if (!liveSessionActive && liveEvents.length === 0) {
@@ -1363,20 +1439,30 @@ server.tool(
1363
1439
  );
1364
1440
 
1365
1441
  // Tool: ask_tester_question
1366
- server.tool(
1442
+ server.registerTool(
1367
1443
  "ask_tester_question",
1368
- "Ask the tester a short, concise question during a live shadowing session. " +
1369
- "The question appears as a toast notification in their browser. " +
1370
- "The tester responds with a voice marker (Alt+Shift+M) and optional highlight captures (Alt+Shift+S). " +
1371
- "Use get_tester_response to check for their answer.",
1372
1444
  {
1373
- question: z
1374
- .string()
1375
- .max(200)
1376
- .describe(
1377
- "Short, concise question for the tester (max 200 chars). " +
1378
- "Example: 'Can you click the Save button again?' or 'Does the error appear with a different email?'",
1379
- ),
1445
+ title: "Ask Tester Question",
1446
+ description:
1447
+ "Ask the tester a short, concise question during a live shadowing session. " +
1448
+ "The question appears as a toast notification in their browser. " +
1449
+ "The tester responds with a voice marker (Alt+Shift+M) and optional highlight captures (Alt+Shift+S). " +
1450
+ "Use get_tester_response to check for their answer.",
1451
+ annotations: {
1452
+ readOnlyHint: false,
1453
+ destructiveHint: false,
1454
+ idempotentHint: false,
1455
+ openWorldHint: true,
1456
+ },
1457
+ inputSchema: z.object({
1458
+ question: z
1459
+ .string()
1460
+ .max(200)
1461
+ .describe(
1462
+ "Short, concise question for the tester (max 200 chars). " +
1463
+ "Example: 'Can you click the Save button again?' or 'Does the error appear with a different email?'",
1464
+ ),
1465
+ }),
1380
1466
  },
1381
1467
  async ({ question }) => {
1382
1468
  if (!liveSessionActive || !activeConnection) {
@@ -1435,16 +1521,26 @@ server.tool(
1435
1521
  );
1436
1522
 
1437
1523
  // Tool: get_tester_response
1438
- server.tool(
1524
+ server.registerTool(
1439
1525
  "get_tester_response",
1440
- "Check for the tester's response to a question asked during live shadowing. " +
1441
- "Returns the tester's screenshot, highlight captures, and transcribed voice note.\n\n" +
1442
- "IMPORTANT: The tester needs time to record their answer (10-60 seconds). " +
1443
- "Instead of calling this tool repeatedly, prefer polling watch_live_session — " +
1444
- "you will see a 'question-response-received' event when the response arrives. " +
1445
- "Then call this tool once to get the full response with transcription and images.",
1446
1526
  {
1447
- question_id: z.string().describe("The question ID returned by ask_tester_question."),
1527
+ title: "Get Tester Response",
1528
+ description:
1529
+ "Check for the tester's response to a question asked during live shadowing. " +
1530
+ "Returns the tester's screenshot, highlight captures, and transcribed voice note.\n\n" +
1531
+ "IMPORTANT: The tester needs time to record their answer (10-60 seconds). " +
1532
+ "Instead of calling this tool repeatedly, prefer polling watch_live_session — " +
1533
+ "you will see a 'question-response-received' event when the response arrives. " +
1534
+ "Then call this tool once to get the full response with transcription and images.",
1535
+ annotations: {
1536
+ readOnlyHint: true,
1537
+ destructiveHint: false,
1538
+ idempotentHint: true,
1539
+ openWorldHint: false,
1540
+ },
1541
+ inputSchema: z.object({
1542
+ question_id: z.string().describe("The question ID returned by ask_tester_question."),
1543
+ }),
1448
1544
  },
1449
1545
  async ({ question_id }) => {
1450
1546
  const response = questionResponses.get(question_id);
@@ -1561,10 +1657,20 @@ server.tool(
1561
1657
  );
1562
1658
 
1563
1659
  // Tool: get_live_screenshot
1564
- server.tool(
1660
+ server.registerTool(
1565
1661
  "get_live_screenshot",
1566
- "Capture a screenshot of the tester's current browser tab during a live shadowing session.",
1567
- {},
1662
+ {
1663
+ title: "Live Screenshot",
1664
+ description:
1665
+ "Capture a screenshot of the tester's current browser tab during a live shadowing session.",
1666
+ annotations: {
1667
+ readOnlyHint: true,
1668
+ destructiveHint: false,
1669
+ idempotentHint: true,
1670
+ openWorldHint: false,
1671
+ },
1672
+ inputSchema: z.object({}),
1673
+ },
1568
1674
  async () => {
1569
1675
  if (!liveSessionActive || !activeConnection) {
1570
1676
  return toolError("No active live shadowing session or extension not connected.");
@@ -1615,13 +1721,23 @@ server.tool(
1615
1721
  );
1616
1722
 
1617
1723
  // Tool: get_live_session_summary
1618
- server.tool(
1724
+ server.registerTool(
1619
1725
  "get_live_session_summary",
1620
- "Get a summary of the live shadowing session including all agent-tester interactions. " +
1621
- "Call this after the session ends (sessionActive becomes false) to get the complete interaction log " +
1622
- "for incorporating into your analysis. This includes all questions asked, tester responses, " +
1623
- "voice transcriptions, and voice marker transcriptions from the session.",
1624
- {},
1726
+ {
1727
+ title: "Live Session Summary",
1728
+ description:
1729
+ "Get a summary of the live shadowing session including all agent-tester interactions. " +
1730
+ "Call this after the session ends (sessionActive becomes false) to get the complete interaction log " +
1731
+ "for incorporating into your analysis. This includes all questions asked, tester responses, " +
1732
+ "voice transcriptions, and voice marker transcriptions from the session.",
1733
+ annotations: {
1734
+ readOnlyHint: true,
1735
+ destructiveHint: false,
1736
+ idempotentHint: true,
1737
+ openWorldHint: false,
1738
+ },
1739
+ inputSchema: z.object({}),
1740
+ },
1625
1741
  async () => {
1626
1742
  if (!liveSessionMeta && liveInteractions.length === 0) {
1627
1743
  return toolError("No live session data available. Start a live shadowing session first.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracegist-mcp-bridge",
3
- "version": "0.2.11",
3
+ "version": "0.2.14",
4
4
  "description": "Local-first MCP bridge for reading and transcribing TraceGist package zips.",
5
5
  "type": "module",
6
6
  "bin": {