token-goat 2.8.0 → 2.8.2

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 { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  ALL_SYMBOLS_IN_FILE_LIMIT,
5
+ AMBIGUOUS_HEADING_LIMIT,
5
6
  CONTENT_MAX_INPUT_CHARS,
6
7
  WHOLE_FILE_NOTE_SYMBOL,
7
8
  compressText,
@@ -60,6 +61,7 @@ import {
60
61
  runOpenApiOutline,
61
62
  runOutline,
62
63
  runPdfExtractText,
64
+ runPdfLocate,
63
65
  runPdfMeta,
64
66
  runPdfOutline,
65
67
  runPrSlice,
@@ -84,7 +86,7 @@ import {
84
86
  runZipRead,
85
87
  symbolNamesInFile,
86
88
  upsertNote
87
- } from "./token-goat-chunk-SRAR6DOK.mjs";
89
+ } from "./token-goat-chunk-LILS6TIU.mjs";
88
90
  import {
89
91
  BASH_OUTPUT_SUBDIR,
90
92
  GEMINI_TOOL_NAME_MAP,
@@ -111,7 +113,7 @@ import {
111
113
  searchRecall,
112
114
  storeWebOutput,
113
115
  summarizeResidentContext
114
- } from "./token-goat-chunk-AO6MFFTW.mjs";
116
+ } from "./token-goat-chunk-5MLXFSRI.mjs";
115
117
  import {
116
118
  AGENT_SALT_MARKER,
117
119
  CONTEXT_AUTOCOMPACT_TOKENS,
@@ -254,7 +256,7 @@ import {
254
256
  vscodeDecoderConfigured,
255
257
  walkProject,
256
258
  writeCompact
257
- } from "./token-goat-chunk-2F6TFBZE.mjs";
259
+ } from "./token-goat-chunk-AM23GDIS.mjs";
258
260
  import {
259
261
  C,
260
262
  CONFIG_KEY_ENV_OVERRIDES,
@@ -307,6 +309,7 @@ import {
307
309
  parse,
308
310
  pushAll,
309
311
  readConfigSource,
312
+ readUnmappedTools,
310
313
  recordStat,
311
314
  redactIfDotenv,
312
315
  redactUrlQuery,
@@ -335,11 +338,14 @@ import {
335
338
  withFileLock,
336
339
  withRetryOnLock,
337
340
  writeJsonSettings
338
- } from "./token-goat-chunk-44Y77VHR.mjs";
341
+ } from "./token-goat-chunk-IVCTQPZD.mjs";
339
342
  import {
340
343
  __export
341
344
  } from "./token-goat-chunk-AEX54RUZ.mjs";
342
345
 
346
+ // src/cli.ts
347
+ import { randomBytes } from "node:crypto";
348
+
343
349
  // node_modules/commander/lib/error.js
344
350
  var CommanderError = class extends Error {
345
351
  /**
@@ -4081,7 +4087,8 @@ var PI_EXTENSION_SCRIPT = `// token-goat bridge extension for pi (pi-coding-agen
4081
4087
  // Bridges pi's extension events to token-goat's subprocess hook protocol.
4082
4088
  // https://github.com/DFKHelper/token-goat
4083
4089
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
4084
- import { spawnSync } from "node:child_process";
4090
+ import { spawnSync } from "node:child_process"
4091
+ import crypto from "node:crypto";
4085
4092
  import * as fs from "node:fs";
4086
4093
  import * as os from "node:os";
4087
4094
  import * as path from "node:path";
@@ -4540,6 +4547,7 @@ var OPENCODE_PLUGIN_SCRIPT = `// token-goat bridge plugin for opencode
4540
4547
  // Bridges opencode's plugin hooks to token-goat's subprocess hook protocol.
4541
4548
  // https://github.com/DFKHelper/token-goat
4542
4549
  import { spawnSync } from "node:child_process"
4550
+ import crypto from "node:crypto"
4543
4551
  import fs from "node:fs"
4544
4552
  import os from "node:os"
4545
4553
  import path from "node:path"
@@ -5012,11 +5020,23 @@ var NO_SERVER_HANDLER_REASON = "token-goat has no registered server-side handler
5012
5020
  var NO_POST_COMPACT_EVENT_REASON = "post_compact is a Claude Code event (hook_event_name PostCompact, carrying compact_summary); no equivalent has been confirmed for this harness, so it is left unwired rather than guessed at";
5013
5021
  var NO_SEPARATE_FAILURE_EVENT_REASON = "post_tool_use_failure exists because Copilot CLI routes a failed tool result to a separate postToolUseFailure event instead of postToolUse. No separate failure event is wired for this harness, so a failed tool call either arrives on post_tool_use like any other result or is not seen; which of the two has not been checked here";
5014
5022
  var COPILOT_NO_POST_COMPACT_REASON = "Copilot CLI has no post-compaction hook: its HookType enum (schemas/api.schema.json, 1.0.79 and 1.0.80) declares preCompact and no postCompact, and both preCompact call sites in app.js await the hook and never assign its result. Whether the summary is reachable another way is open. app.js does emit session.compaction_complete carrying summaryContent, and the session event writer subscribes to '*' -- but emit() vs emitEphemeral() does NOT gate that writer: dispatchEventHandlers runs outside the ephemeral branch in emitInternal, on('*') early-returns past every filter, and the writer's callback applies no filter of its own before handing the JSON to native recordEventJson. The durable-vs-ephemeral decision is in Rust (api_session_event_writer.rs) and was not readable. Nor is the event uniformly summary-bearing: two of the four emit sites are the branches taken when no summary exists, summaryContent is optional in the schema, and the relay session class emits the same event via emitEphemeral. No compaction has ever been observed on the machines checked, so nothing empirical anchors any of it. Context can be written back on the next turn through userPromptSubmitted additionalContext";
5023
+ var VERIFICATION_RANK = {
5024
+ dogfooded: 0,
5025
+ sourced: 1,
5026
+ documented: 2
5027
+ };
5028
+ var VERIFICATION_BLURB = {
5029
+ dogfooded: "driven against the real harness binary",
5030
+ sourced: "wire format read from the harness's own source or declarations, never driven against it",
5031
+ documented: "wired from the harness's documentation only, never driven against it"
5032
+ };
5015
5033
  var BRIDGE_CAPABILITY_MATRIX = [
5016
5034
  {
5017
5035
  harness: "claudecode",
5018
5036
  label: "Claude Code",
5019
5037
  sourceFile: "src/install.ts (HOOK_EVENT_MAP)",
5038
+ verification: "dogfooded",
5039
+ verificationNote: "The harness this repo is developed and released under; CLAUDE.md requires every CLI/hook change be run against the built binary here before it ships.",
5020
5040
  implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "pre_compact", "post_compact", "user_prompt_submit", "subagent_stop", "session_start"]),
5021
5041
  reasons: [
5022
5042
  { events: ["post_tool_use_failure"], reason: NO_SEPARATE_FAILURE_EVENT_REASON },
@@ -5027,6 +5047,8 @@ var BRIDGE_CAPABILITY_MATRIX = [
5027
5047
  harness: "codex",
5028
5048
  label: "Codex CLI",
5029
5049
  sourceFile: "src/bridges/codex_install.ts (CODEX_HOOK_EVENTS, CODEX_GLOBAL_HOOK_EVENTS)",
5050
+ verification: "sourced",
5051
+ verificationNote: "Wire format pinned to Codex 0.137.0+'s declared hookSpecificOutput schema (src/bridges/codex.ts); no run against a Codex binary is recorded here.",
5030
5052
  implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "pre_compact", "user_prompt_submit", "subagent_stop"]),
5031
5053
  reasons: [
5032
5054
  { events: ["post_tool_use_failure"], reason: NO_SEPARATE_FAILURE_EVENT_REASON },
@@ -5042,6 +5064,8 @@ var BRIDGE_CAPABILITY_MATRIX = [
5042
5064
  harness: "grok",
5043
5065
  label: "Grok CLI",
5044
5066
  sourceFile: "src/bridges/grok_install.ts (GROK_HOOK_EVENTS)",
5067
+ verification: "dogfooded",
5068
+ verificationNote: "grok 0.2.93, 2026-07-09: observed executing the global settings.json hooks config unmodified and setting GROK_SESSION_ID on every hook subprocess (src/bridges/registry.ts, src/hooks_cli.ts).",
5045
5069
  implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "pre_compact", "user_prompt_submit", "subagent_stop"]),
5046
5070
  reasons: [
5047
5071
  { events: ["post_tool_use_failure"], reason: NO_SEPARATE_FAILURE_EVENT_REASON },
@@ -5106,6 +5130,8 @@ var BRIDGE_CAPABILITY_MATRIX = [
5106
5130
  harness: "copilot_cli",
5107
5131
  label: "Copilot CLI",
5108
5132
  sourceFile: "src/bridges/copilot_cli_install.ts (COPILOT_CLI_HOOK_EVENTS), src/bridges/copilot_cli.ts (COPILOT_TO_TG_EVENT)",
5133
+ verification: "dogfooded",
5134
+ verificationNote: "doctor's checkCopilotCli drives the installed shim end to end, and every hook payload field is cross-checked against Copilot's own declarations pinned at 1.0.80 (schemas/copilot_cli.hooks.json).",
5109
5135
  implemented: /* @__PURE__ */ new Set([
5110
5136
  "session_start",
5111
5137
  "pre_tool_use",
@@ -5128,6 +5154,8 @@ var BRIDGE_CAPABILITY_MATRIX = [
5128
5154
  harness: "gemini",
5129
5155
  label: "Gemini CLI",
5130
5156
  sourceFile: "src/bridges/gemini_install.ts (GEMINI_HOOK_EVENTS)",
5157
+ verification: "sourced",
5158
+ verificationNote: "Verified against Gemini CLI's published hooks reference (src/bridges/gemini_install.ts); no run against a Gemini binary is recorded here.",
5131
5159
  implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "pre_compact"]),
5132
5160
  reasons: [
5133
5161
  { events: ["post_tool_use_failure"], reason: NO_SEPARATE_FAILURE_EVENT_REASON },
@@ -5142,6 +5170,8 @@ var BRIDGE_CAPABILITY_MATRIX = [
5142
5170
  harness: "qwen",
5143
5171
  label: "Qwen Code",
5144
5172
  sourceFile: "src/bridges/qwen_install.ts (QWEN_HOOK_EVENTS)",
5173
+ verification: "documented",
5174
+ verificationNote: "Wired from QwenLM/qwen-code's hooks.md, which src/bridges/qwen_install.ts states was not live-tested against a running install.",
5145
5175
  implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "pre_compact", "user_prompt_submit", "subagent_stop"]),
5146
5176
  reasons: [
5147
5177
  { events: ["post_tool_use_failure"], reason: NO_SEPARATE_FAILURE_EVENT_REASON },
@@ -5161,6 +5191,8 @@ var BRIDGE_CAPABILITY_MATRIX = [
5161
5191
  harness: "kimi",
5162
5192
  label: "Kimi Code CLI",
5163
5193
  sourceFile: "src/bridges/kimi_install.ts (KIMI_EVENT_ARG), src/bridges/kimi.ts (KIMI_HOOK_SCRIPT)",
5194
+ verification: "sourced",
5195
+ verificationNote: "Payload keys and response contract read from MoonshotAI/kimi-code's own source (externalHooksRunner/runner.ts, externalHooksService.ts); no run against a Kimi binary is recorded here.",
5164
5196
  implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "pre_compact", "user_prompt_submit", "subagent_stop", "session_start"]),
5165
5197
  reasons: [
5166
5198
  { events: ["post_tool_use_failure"], reason: NO_SEPARATE_FAILURE_EVENT_REASON },
@@ -5172,6 +5204,8 @@ var BRIDGE_CAPABILITY_MATRIX = [
5172
5204
  harness: "opencode",
5173
5205
  label: "opencode",
5174
5206
  sourceFile: "src/bridges/opencode.ts",
5207
+ verification: "sourced",
5208
+ verificationNote: "Tool ids, parameter keys and handler behavior read directly from opencode's own source (src/bridges/opencode.ts); no run against a running gateway is recorded here.",
5175
5209
  implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "pre_compact"]),
5176
5210
  reasons: [
5177
5211
  { events: ["post_tool_use_failure"], reason: NO_SEPARATE_FAILURE_EVENT_REASON },
@@ -5186,6 +5220,8 @@ var BRIDGE_CAPABILITY_MATRIX = [
5186
5220
  harness: "openclaw",
5187
5221
  label: "OpenClaw",
5188
5222
  sourceFile: "src/bridges/openclaw.ts",
5223
+ verification: "sourced",
5224
+ verificationNote: "Verified against the live plugin docs and OpenClaw's aristotle-agent/types.ts; the bridge's own note lists what stays unverified without a live instance (built-in tool names, session id).",
5189
5225
  implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "pre_compact"]),
5190
5226
  reasons: [
5191
5227
  { events: ["post_tool_use_failure"], reason: NO_SEPARATE_FAILURE_EVENT_REASON },
@@ -5200,6 +5236,8 @@ var BRIDGE_CAPABILITY_MATRIX = [
5200
5236
  harness: "pi",
5201
5237
  label: "pi (pi-coding-agent)",
5202
5238
  sourceFile: "src/bridges/pi.ts",
5239
+ verification: "sourced",
5240
+ verificationNote: "Response shape verified against pi's own TextContent/ImageContent types (src/bridges/pi.ts); no run against a pi binary is recorded here.",
5203
5241
  implemented: /* @__PURE__ */ new Set(["pre_tool_use", "post_tool_use", "pre_compact"]),
5204
5242
  reasons: [
5205
5243
  { events: ["post_tool_use_failure"], reason: NO_SEPARATE_FAILURE_EVENT_REASON },
@@ -5223,7 +5261,15 @@ function bridgesStatusToJson(matrix = BRIDGE_CAPABILITY_MATRIX) {
5223
5261
  reasons[event] = reason;
5224
5262
  }
5225
5263
  }
5226
- return { harness: row.harness, label: row.label, sourceFile: row.sourceFile, events, reasons };
5264
+ return {
5265
+ harness: row.harness,
5266
+ label: row.label,
5267
+ sourceFile: row.sourceFile,
5268
+ verification: row.verification,
5269
+ verificationNote: row.verificationNote,
5270
+ events,
5271
+ reasons
5272
+ };
5227
5273
  });
5228
5274
  }
5229
5275
  function formatBridgesStatus(matrix = BRIDGE_CAPABILITY_MATRIX) {
@@ -5234,7 +5280,8 @@ function formatBridgesStatus(matrix = BRIDGE_CAPABILITY_MATRIX) {
5234
5280
  lines.push("Read-only static analysis of src/bridges/*_install.ts and *.ts -- never invokes a real harness binary.");
5235
5281
  lines.push("");
5236
5282
  const harnessWidth = Math.max(...matrix.map((r) => r.harness.length), "harness".length);
5237
- const header = ["harness".padEnd(harnessWidth), ...HOOK_EVENTS.map((e) => e), "score"].join(" ");
5283
+ const verifyWidth = Math.max(...matrix.map((r) => r.verification.length), "verified".length);
5284
+ const header = ["harness".padEnd(harnessWidth), ...HOOK_EVENTS.map((e) => e), "score", "verified".padEnd(verifyWidth)].join(" ");
5238
5285
  lines.push(header);
5239
5286
  for (const row of matrix) {
5240
5287
  const cells = HOOK_EVENTS.map((event) => {
@@ -5242,7 +5289,16 @@ function formatBridgesStatus(matrix = BRIDGE_CAPABILITY_MATRIX) {
5242
5289
  return cell.padEnd(event.length);
5243
5290
  });
5244
5291
  const score = `${row.implemented.size}/${HOOK_EVENTS.length}`;
5245
- lines.push([row.harness.padEnd(harnessWidth), ...cells, score].join(" "));
5292
+ lines.push([row.harness.padEnd(harnessWidth), ...cells, score, row.verification.padEnd(verifyWidth)].join(" "));
5293
+ }
5294
+ lines.push("");
5295
+ lines.push("## How each row was established");
5296
+ for (const level of Object.keys(VERIFICATION_RANK)) {
5297
+ lines.push(`- ${level}: ${VERIFICATION_BLURB[level]}`);
5298
+ }
5299
+ lines.push("");
5300
+ for (const row of [...matrix].sort((a, b) => VERIFICATION_RANK[a.verification] - VERIFICATION_RANK[b.verification])) {
5301
+ lines.push(`- ${row.harness} (${row.verification}): ${row.verificationNote}`);
5246
5302
  }
5247
5303
  const gapRows = matrix.filter((r) => r.reasons.length > 0);
5248
5304
  if (gapRows.length > 0) {
@@ -5256,6 +5312,11 @@ function formatBridgesStatus(matrix = BRIDGE_CAPABILITY_MATRIX) {
5256
5312
  }
5257
5313
  return lines.join("\n");
5258
5314
  }
5315
+ function installVerificationNotice(harness) {
5316
+ const row = BRIDGE_CAPABILITY_MATRIX.find((r) => r.harness === harness);
5317
+ if (row === void 0 || row.verification === "dogfooded") return null;
5318
+ return `NOTE: the ${row.label} bridge is ${row.verification} -- ${VERIFICATION_BLURB[row.verification]}. If a token-goat feature seems to do nothing here, run \`token-goat doctor\` (its "Tool names" check reports what ${row.label} actually sent) and please open an issue. Details: token-goat bridges-status.`;
5319
+ }
5259
5320
 
5260
5321
  // src/sharepoint_resolve.ts
5261
5322
  import * as fs8 from "node:fs";
@@ -5673,6 +5734,7 @@ function runStats(opts = {}) {
5673
5734
  by_project: summary.by_project,
5674
5735
  by_command: summary.by_command,
5675
5736
  by_source: summary.by_source,
5737
+ by_harness: summary.by_harness,
5676
5738
  window_days: summary.window_days
5677
5739
  };
5678
5740
  process.stdout.write(JSON.stringify(out2) + "\n");
@@ -5767,7 +5829,9 @@ function checkMcpProcessHealth(processes) {
5767
5829
  const nodeProcesses = processes.filter((process3) => process3.name.toLowerCase() === "node.exe");
5768
5830
  const chromeLaunchers = nodeProcesses.filter((process3) => /npx-cli\.js.*chrome-devtools-mcp/i.test(process3.commandLine));
5769
5831
  const playwrightLaunchers = nodeProcesses.filter((process3) => /npx-cli\.js.*@playwright[\\/]mcp/i.test(process3.commandLine));
5770
- const orphanedNodeProcesses = nodeProcesses.filter((process3) => !byPid.has(process3.parentProcessId));
5832
+ const orphanedNodeProcesses = nodeProcesses.filter(
5833
+ (process3) => !byPid.has(process3.parentProcessId) && !/--worker-daemon\b/.test(process3.commandLine)
5834
+ );
5771
5835
  const launchers = chromeLaunchers.length + playwrightLaunchers.length;
5772
5836
  if (launchers > 2 || orphanedNodeProcesses.length > 0) {
5773
5837
  const details = [];
@@ -6211,6 +6275,38 @@ function checkCompactionChannel(dbPath) {
6211
6275
  return { name, status: "warn", message: `could not read compaction stats: ${extractErrorMessage(e)}` };
6212
6276
  }
6213
6277
  }
6278
+ var UNMAPPED_TOOL_SAMPLE = 5;
6279
+ function checkUnmappedTools(dbPath) {
6280
+ const name = "Tool names";
6281
+ if (!fs12.existsSync(dbPath)) {
6282
+ return { name, status: "ok", message: "no database yet" };
6283
+ }
6284
+ try {
6285
+ const rows = readUnmappedTools(dbPath);
6286
+ if (rows.length === 0) {
6287
+ return { name, status: "ok", message: "every tool name seen so far reached a handler that wanted it" };
6288
+ }
6289
+ const nearMisses = rows.filter((r) => r.near_miss !== null && r.near_miss !== void 0);
6290
+ if (nearMisses.length > 0) {
6291
+ const shown2 = nearMisses.slice(0, UNMAPPED_TOOL_SAMPLE).map((r) => `${r.harness} sent "${r.tool_name}" where "${r.near_miss}" is handled (${r.event_name}, ${r.hits}x)`);
6292
+ const more2 = nearMisses.length > UNMAPPED_TOOL_SAMPLE ? ` (+${nearMisses.length - UNMAPPED_TOOL_SAMPLE} more)` : "";
6293
+ return {
6294
+ name,
6295
+ status: "warn",
6296
+ message: `${shown2.join("; ")}${more2} -- these differ only by case or separators, so that bridge's tool-rename step is very likely not being applied and every handler behind those names is inert`
6297
+ };
6298
+ }
6299
+ const shown = rows.slice(0, UNMAPPED_TOOL_SAMPLE).map((r) => `${r.tool_name} (${r.hits}x)`);
6300
+ const more = rows.length > UNMAPPED_TOOL_SAMPLE ? `, +${rows.length - UNMAPPED_TOOL_SAMPLE} more` : "";
6301
+ return {
6302
+ name,
6303
+ status: "ok",
6304
+ message: `${rows.length} tool name(s) seen with no handler, none resembling one token-goat handles: ${shown.join(", ")}${more}`
6305
+ };
6306
+ } catch (e) {
6307
+ return { name, status: "warn", message: `could not read the tool-name histogram: ${extractErrorMessage(e)}` };
6308
+ }
6309
+ }
6214
6310
  function runDoctor(dataDir2, configPath2, rootDir, processes) {
6215
6311
  const results = [];
6216
6312
  const actualDataDir = dataDir2 || dataDir();
@@ -6223,6 +6319,7 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
6223
6319
  results.push(checkSymbolCount(path11.join(actualDataDir, "global.db"), rootDir));
6224
6320
  results.push(checkDirtyQueueHealth(actualDataDir));
6225
6321
  results.push(checkCompactionChannel(path11.join(actualDataDir, "global.db")));
6322
+ results.push(checkUnmappedTools(path11.join(actualDataDir, "global.db")));
6226
6323
  const actualConfigPath = configPath2 || configPath();
6227
6324
  results.push(checkConfigValid(actualConfigPath));
6228
6325
  results.push(checkEmbeddings(loadConfig(rootDir)));
@@ -9118,6 +9215,10 @@ var GO_FAIL = /^--- FAIL:\s+(\S+)/;
9118
9215
  var CARGO_FAIL = /^test (\S+) \.\.\. FAILED/;
9119
9216
  var CARGO_SECTION = /^---- (\S+) (?:stdout|stderr) ----$/;
9120
9217
  var CARGO_RESULT = /^test result:/;
9218
+ var VITEST_FAIL_HEADER = /^\s+FAIL\s+(.+?)\s*$/;
9219
+ var VITEST_BANNER = /⎯+\s*Failed Tests\s+\d+\s*⎯+/;
9220
+ var VITEST_SEPARATOR = /^⎯+.*⎯+$/;
9221
+ var VITEST_SUMMARY = /^\s*(Test Files|Tests|Start at|Duration)\s/;
9121
9222
  function detectRunner(text) {
9122
9223
  if (text.includes("=== FAILURES ===") || text.includes("=== ERRORS ===")) {
9123
9224
  return "pytest";
@@ -9125,6 +9226,9 @@ function detectRunner(text) {
9125
9226
  if (/\bFAILED\b.+::test_/.test(text) || /short test summary/i.test(text)) {
9126
9227
  return "pytest";
9127
9228
  }
9229
+ if (VITEST_BANNER.test(text) || /^\s*Test Files\s+/m.test(text) && /^\s*Tests\s+\d+\s+(failed|passed)/m.test(text)) {
9230
+ return "vitest";
9231
+ }
9128
9232
  if (/^\s+●\s/m.test(text) || /^FAIL\s+\S+\.test\b/m.test(text)) {
9129
9233
  return "jest";
9130
9234
  }
@@ -9249,6 +9353,44 @@ function extractJest(lines) {
9249
9353
  }
9250
9354
  return result;
9251
9355
  }
9356
+ function extractVitest(lines) {
9357
+ const result = { runner: "vitest", blocks: [], summaryLines: [], statsLine: "" };
9358
+ let inBlock = false;
9359
+ let currentName = "";
9360
+ let currentBody = [];
9361
+ const flush = () => {
9362
+ if (inBlock && currentName) result.blocks.push({ name: currentName, body: currentBody.join("\n").trimEnd() });
9363
+ inBlock = false;
9364
+ currentName = "";
9365
+ currentBody = [];
9366
+ };
9367
+ for (const line of lines) {
9368
+ const s = line.trimEnd();
9369
+ const header = VITEST_FAIL_HEADER.exec(s);
9370
+ if (header) {
9371
+ flush();
9372
+ currentName = header[1] ?? "";
9373
+ currentBody = [];
9374
+ inBlock = true;
9375
+ result.summaryLines.push(currentName);
9376
+ continue;
9377
+ }
9378
+ if (VITEST_SUMMARY.test(s)) {
9379
+ if (/^\s*Tests\s/.test(s)) result.statsLine = s.trim();
9380
+ flush();
9381
+ continue;
9382
+ }
9383
+ if (inBlock) {
9384
+ if (VITEST_SEPARATOR.test(s)) {
9385
+ flush();
9386
+ continue;
9387
+ }
9388
+ currentBody.push(s);
9389
+ }
9390
+ }
9391
+ flush();
9392
+ return result;
9393
+ }
9252
9394
  function extractGo(lines) {
9253
9395
  const result = {
9254
9396
  runner: "go",
@@ -9371,6 +9513,9 @@ function extractFailures(text, options) {
9371
9513
  if (detected === "pytest") {
9372
9514
  return extractPytest(lines);
9373
9515
  }
9516
+ if (detected === "vitest") {
9517
+ return extractVitest(lines);
9518
+ }
9374
9519
  if (detected === "jest") {
9375
9520
  return extractJest(lines);
9376
9521
  }
@@ -9836,7 +9981,7 @@ function parseNodeBlock(lines, start) {
9836
9981
  }
9837
9982
  return { block: { frames, exception: header }, nextIndex: i };
9838
9983
  }
9839
- var RUST_PANIC_HEADER_RE = /^thread '[^']*' panicked at (.+):(\d+):(\d+):\s*$/;
9984
+ var RUST_PANIC_HEADER_RE = /^thread '[^']*' panicked at ([^:\r\n]+):(\d+):(\d+):\s*$/;
9840
9985
  var RUST_BACKTRACE_NOTE_RE = /^note: run with `RUST_BACKTRACE=1`/;
9841
9986
  var RUST_BACKTRACE_NUM_RE = /^\s*\d+:\s+(.+)$/;
9842
9987
  var RUST_BACKTRACE_AT_RE = /^\s+at\s+(.+):(\d+):(\d+)\s*$/;
@@ -9882,7 +10027,7 @@ function parseRustBlock(lines, start) {
9882
10027
  }
9883
10028
  return { block: { frames: [panicFrame], exception }, nextIndex: i };
9884
10029
  }
9885
- var JVM_HEADER_RE = /^(?:Exception in thread "[^"]*" )?(?:Caused by: )?((?:[A-Za-z_$][\w$]*\.)+[A-Za-z_$][\w$]*): (.*)$/;
10030
+ var JVM_HEADER_RE = /^(?:Exception in thread "[^"\r\n]*" )?(?:Caused by: )?((?:[A-Za-z_$][\w$]*\.)+[A-Za-z_$][\w$]*): (.*)$/;
9886
10031
  var JVM_FRAME_RE = /^\s+at\s+(\S+)\(([^)]*)\)\s*$/;
9887
10032
  var JVM_MORE_RE = /^\s*\.\.\.\s+\d+\s+more\s*$/;
9888
10033
  function parseJvmFrameLine(line) {
@@ -13898,7 +14043,7 @@ async function cmdMcpServe() {
13898
14043
  let StdioServerTransport;
13899
14044
  try {
13900
14045
  ;
13901
- ({ createMcpServer } = await import("./token-goat-chunk-FBGBTICM.mjs"));
14046
+ ({ createMcpServer } = await import("./token-goat-chunk-AL644JUV.mjs"));
13902
14047
  ({ StdioServerTransport } = await import("./token-goat-chunk-324QOJYZ.mjs"));
13903
14048
  } catch (err2) {
13904
14049
  process.stderr.write(
@@ -13915,11 +14060,15 @@ async function cmdMcpServe() {
13915
14060
  server.onclose = resolve12;
13916
14061
  });
13917
14062
  }
14063
+ function printBridgeVerificationNotice(harness) {
14064
+ const notice = installVerificationNotice(harness);
14065
+ if (notice !== null) out(notice);
14066
+ }
13918
14067
  async function cmdHook(event, opts) {
13919
14068
  if (typeof opts.harness === "string" && opts.harness.length > 0) {
13920
14069
  process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
13921
14070
  }
13922
- const { relay } = await import("./token-goat-chunk-A62K4XW2.mjs");
14071
+ const { relay } = await import("./token-goat-chunk-JU7QESB3.mjs");
13923
14072
  await relay(event);
13924
14073
  }
13925
14074
  async function cmdInstall(opts) {
@@ -13946,6 +14095,7 @@ async function cmdInstall(opts) {
13946
14095
  } else {
13947
14096
  out(`Installed token-goat Codex CLI integration \u2192 ${codexResult.configPath}, ${codexResult.agentsPath}`);
13948
14097
  }
14098
+ printBridgeVerificationNotice("codex");
13949
14099
  }
13950
14100
  if (opts.gemini === true) {
13951
14101
  const geminiResult = installGemini();
@@ -13954,6 +14104,7 @@ async function cmdInstall(opts) {
13954
14104
  } else {
13955
14105
  out(`Installed token-goat Gemini CLI integration \u2192 ${geminiResult.settingsPath}`);
13956
14106
  }
14107
+ printBridgeVerificationNotice("gemini");
13957
14108
  }
13958
14109
  if (opts.qwen === true) {
13959
14110
  const qwenResult = installQwen();
@@ -13962,6 +14113,7 @@ async function cmdInstall(opts) {
13962
14113
  } else {
13963
14114
  out(`Installed token-goat Qwen Code integration \u2192 ${qwenResult.settingsPath}`);
13964
14115
  }
14116
+ printBridgeVerificationNotice("qwen");
13965
14117
  }
13966
14118
  if (opts.kimi === true) {
13967
14119
  const kimiResult = installKimi();
@@ -13970,6 +14122,7 @@ async function cmdInstall(opts) {
13970
14122
  } else {
13971
14123
  out(`Installed token-goat Kimi Code integration \u2192 ${kimiResult.configPath}, ${kimiResult.hookScriptPath}, ${kimiResult.agentsPath}, ${kimiResult.skillPath}`);
13972
14124
  }
14125
+ printBridgeVerificationNotice("kimi");
13973
14126
  }
13974
14127
  if (opts.pi === true) {
13975
14128
  const piResult = installPi({ local: opts.local === true });
@@ -13978,6 +14131,7 @@ async function cmdInstall(opts) {
13978
14131
  } else {
13979
14132
  out(`Installed token-goat pi extension \u2192 ${piResult.extensionPath}`);
13980
14133
  }
14134
+ printBridgeVerificationNotice("pi");
13981
14135
  }
13982
14136
  if (opts.openclaw === true) {
13983
14137
  const openclawResult = installOpenclaw();
@@ -13986,6 +14140,7 @@ async function cmdInstall(opts) {
13986
14140
  } else {
13987
14141
  out(`Installed token-goat OpenClaw integration \u2192 ${openclawResult.configPath}, ${openclawResult.pluginPath}`);
13988
14142
  }
14143
+ printBridgeVerificationNotice("openclaw");
13989
14144
  }
13990
14145
  if (opts.copilot === true) {
13991
14146
  const copilotResult = installCopilotCli({ local: opts.local === true });
@@ -13994,6 +14149,7 @@ async function cmdInstall(opts) {
13994
14149
  } else {
13995
14150
  out(`Installed token-goat Copilot CLI integration \u2192 ${copilotResult.configPath}, ${copilotResult.scriptPath}, ${copilotResult.instructionsPath}`);
13996
14151
  }
14152
+ printBridgeVerificationNotice("copilot_cli");
13997
14153
  }
13998
14154
  if (opts.opencode === true) {
13999
14155
  const opencodeResult = installOpencode();
@@ -14002,6 +14158,7 @@ async function cmdInstall(opts) {
14002
14158
  } else {
14003
14159
  out(`Installed token-goat opencode plugin \u2192 ${opencodeResult.pluginPath}`);
14004
14160
  }
14161
+ printBridgeVerificationNotice("opencode");
14005
14162
  }
14006
14163
  if (opts.grok === true) {
14007
14164
  const grokResult = installGrok();
@@ -14010,6 +14167,7 @@ async function cmdInstall(opts) {
14010
14167
  } else {
14011
14168
  out(`Installed token-goat Grok CLI integration \u2192 ${grokResult.configPath}, ${grokResult.hookScriptPath}`);
14012
14169
  }
14170
+ printBridgeVerificationNotice("grok");
14013
14171
  }
14014
14172
  if (opts.vscode === true) {
14015
14173
  const vscodeResult = installVscode({ project: opts.project === true });
@@ -14344,10 +14502,11 @@ function _applyFiltersAndPrint(content, opts, fenceUntrusted = false, fenceTag =
14344
14502
  content = [...matched.slice(0, cap), "[token-goat: showing first " + cap + " of " + matched.length + " matching lines; raise --max-matches for more]"].join("\n");
14345
14503
  }
14346
14504
  }
14347
- const lines = content.split(/\r?\n/);
14505
+ const rawLines = content.split(/\r?\n/);
14348
14506
  if (opts.full === true) {
14349
- return emit2(lines.join("\n"));
14507
+ return emit2(rawLines.join("\n"));
14350
14508
  }
14509
+ const lines = rawLines.length > 1 && rawLines[rawLines.length - 1] === "" ? rawLines.slice(0, -1) : rawLines;
14351
14510
  const headN = opts.head !== void 0 ? requireNonNegativeInt("--head", opts.head) : 30;
14352
14511
  const tailN = opts.tail !== void 0 ? requireNonNegativeInt("--tail", opts.tail) : 80;
14353
14512
  const applyElision = (lines2, headN2, tailN2) => lines2.length > headN2 + tailN2 + 1 ? [...lines2.slice(0, headN2), "...(elided)...", ...lines2.slice(lines2.length - tailN2)] : lines2;
@@ -14431,6 +14590,33 @@ async function cmdPdfExtract(file, opts) {
14431
14590
  const bytesSaved = Math.max(1, fullSourceBytes - Buffer.byteLength(printed, "utf8"));
14432
14591
  recordStat("pdf_extract", bytesSaved, Math.round(bytesSaved / 4));
14433
14592
  }
14593
+ async function cmdPdfLocate(file, pattern, opts) {
14594
+ const locateOpts = {
14595
+ ignoreCase: opts.ignoreCase === true
14596
+ };
14597
+ if (opts.maxMatches !== void 0) locateOpts.maxMatches = requirePositiveInt("--max-matches", opts.maxMatches);
14598
+ if (opts.context !== void 0) locateOpts.context = requirePositiveInt("--context", opts.context);
14599
+ if (opts.pages !== void 0) locateOpts.pages = opts.pages;
14600
+ const matches2 = await runPdfLocate(file, pattern, locateOpts);
14601
+ const pages = matches2.map((m) => m.page);
14602
+ let printed;
14603
+ if (opts.json === true) {
14604
+ printed = JSON.stringify({ file, pattern, matchCount: matches2.length, pages, matches: matches2 }, null, 2);
14605
+ out(printed);
14606
+ } else if (matches2.length === 0) {
14607
+ printed = "(no matches)";
14608
+ out(printed);
14609
+ } else {
14610
+ const lines = matches2.map((m) => `p${m.page}: ${m.snippet}`);
14611
+ printed = `${lines.join("\n")}
14612
+
14613
+ ${countNoun(matches2.length, "match", "matches")} across ${countNoun(pages.length, "page")}`;
14614
+ out(printed);
14615
+ }
14616
+ const fullSourceBytes = fileSizeOrZero(file);
14617
+ const bytesSaved = Math.max(1, fullSourceBytes - Buffer.byteLength(printed, "utf8"));
14618
+ recordStat("pdf_locate", bytesSaved, Math.round(bytesSaved / 4));
14619
+ }
14434
14620
  async function cmdPdfOutline(file, opts) {
14435
14621
  const entries = await runPdfOutline(file);
14436
14622
  if (entries.length === 0) {
@@ -14777,7 +14963,7 @@ function emitExtraFileArgsNote(command, first, extras, opts = {}) {
14777
14963
  }
14778
14964
  async function cmdCompress(opts) {
14779
14965
  try {
14780
- const bashRunner = await import("./token-goat-chunk-4KZILRZN.mjs");
14966
+ const bashRunner = await import("./token-goat-chunk-BTZSUIYA.mjs");
14781
14967
  if (opts.compress === false) {
14782
14968
  process.exitCode = bashRunner.runRaw(opts.cmd, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
14783
14969
  return;
@@ -15027,8 +15213,13 @@ async function cmdSkillSection(nameHeading, headingArg) {
15027
15213
  const body = decodeSource(fs27.readFileSync(filePath));
15028
15214
  const extracted = extractNamedSection(body, heading);
15029
15215
  if (!extracted) {
15030
- process.exitCode = 1;
15031
- return;
15216
+ const messages = [`Section '${heading}' not found in skill '${skillName}'`];
15217
+ const allHeadings = listSections(filePath);
15218
+ const available = filterSimilarHeadings(allHeadings, heading);
15219
+ if (available.length > 0) messages.push(didYouMean(available));
15220
+ else if (allHeadings.length === 0) messages.push(`skill '${skillName}' has no headings`);
15221
+ else messages.push(`Try: token-goat outline ${filePath}`);
15222
+ throw new CliError(messages.join("\n"));
15032
15223
  }
15033
15224
  out(extracted);
15034
15225
  }
@@ -15041,7 +15232,7 @@ function atomicWriteBuffer(dest, data) {
15041
15232
  } catch (e) {
15042
15233
  if (e.code !== "ENOENT") throw e;
15043
15234
  }
15044
- const rnd = Math.random().toString(36).slice(2, 8);
15235
+ const rnd = randomBytes(4).toString("hex");
15045
15236
  const tmp = path27.join(path27.dirname(path27.resolve(dest)), `.tmp.${process.pid}.${rnd}`);
15046
15237
  try {
15047
15238
  fs27.writeFileSync(tmp, data, { mode: 384 });
@@ -15081,7 +15272,7 @@ function mapFsError(e, src, dest, srcLabel = "source") {
15081
15272
  if (fe.code === "ENOENT") {
15082
15273
  const errPath = fe.path ?? "";
15083
15274
  const isSource = src !== void 0 && path27.resolve(errPath) === path27.resolve(src);
15084
- if (isSource) throw new CliError(`${srcLabel} file not found: ${src}`);
15275
+ if (isSource) throw new CliError(`${/\bfile$/i.test(srcLabel) ? srcLabel : `${srcLabel} file`} not found: ${src}`);
15085
15276
  const destDir = dest ? path27.dirname(path27.resolve(dest)) : path27.dirname(path27.resolve(errPath || "."));
15086
15277
  throw new CliError(`destination directory does not exist: ${destDir}`);
15087
15278
  }
@@ -15612,6 +15803,18 @@ function cmdInsertSection(file, opts) {
15612
15803
  else if (allHeadings.length > 0) messages.push(`Try: token-goat outline ${file}`);
15613
15804
  throw new CliError(messages.join("\n"));
15614
15805
  }
15806
+ if (result.occurrences !== void 0) {
15807
+ const lines = [
15808
+ `Ambiguous heading '${opts.after}' in '${file}': ${countNoun(result.occurrences.length, "heading")} match. Retry with one of the qualified forms below to pick one:`
15809
+ ];
15810
+ for (const [i, line] of result.occurrences.slice(0, AMBIGUOUS_HEADING_LIMIT).entries()) {
15811
+ lines.push(` - line ${line} -> --after "${opts.after}#${i + 1}"`);
15812
+ }
15813
+ if (result.occurrences.length > AMBIGUOUS_HEADING_LIMIT) {
15814
+ lines.push(` (${result.occurrences.length - AMBIGUOUS_HEADING_LIMIT} more not shown)`);
15815
+ }
15816
+ throw new CliError(lines.join("\n"));
15817
+ }
15615
15818
  let rawBytes;
15616
15819
  try {
15617
15820
  rawBytes = fs27.readFileSync(file);
@@ -16266,6 +16469,9 @@ function buildProgram() {
16266
16469
  );
16267
16470
  program2.command("config-get <file> <key>").description("read one value from a config file (TOML/JSON/YAML/INI)").action((file, key) => runExit(() => runConfigGet({ file, key })));
16268
16471
  program2.command("pdf-extract <file>").description("extract plain text from a PDF (optionally --pages N or N-M) instead of a raw Read").option("--pages <spec>", "page range to extract, e.g. 1-5 or 3 (default: all pages)").option("--layout", "heuristic column-aware reading-order reconstruction from text-item coordinates (imperfect on rotated/overlapping text)").option("--head <n>", "show only the first N lines").option("--tail <n>", "show only the last N lines").option("--grep <pattern>", "filter to lines matching this regex").option("--section <heading>", "extract one markdown section by heading").option("--max-matches <n>", "cap the number of --grep matches shown").action(guard(cmdPdfExtract));
16472
+ program2.command("pdf-locate <file> <pattern>").description(
16473
+ "find which pages of a PDF match a regex, with a snippet per match, so you can pdf-extract only those pages instead of the whole document"
16474
+ ).option("-i, --ignore-case", "case-insensitive matching").option("--max-matches <n>", "stop after this many page matches (default: 50)").option("--context <n>", "snippet length in characters around each match (default: 80)").option("--pages <spec>", "page range to scan, e.g. 1-5 or 3 (default: all pages)").option("-j, --json", "output as JSON").action(guard(cmdPdfLocate));
16269
16475
  program2.command("pdf-outline <file>").description("list a PDF's bookmark/outline tree with page numbers instead of a raw Read").option("-j, --json", "output as JSON").action(guard(cmdPdfOutline));
16270
16476
  program2.command("pdf-meta <file>").description("page count, title/author, and whether a PDF has an extractable text layer").option("-j, --json", "output as JSON").action(guard(cmdPdfMeta));
16271
16477
  program2.command("image-meta <file>").description("dimensions, byte size, format, and what a shrink would cost -- sharp metadata only, never runs OCR").option("-j, --json", "output as JSON").action(guard(cmdImageMeta));
@@ -8,7 +8,7 @@ import {
8
8
  loadBlob,
9
9
  sanitizeFtsQuery,
10
10
  storeBlob
11
- } from "./token-goat-chunk-2F6TFBZE.mjs";
11
+ } from "./token-goat-chunk-AM23GDIS.mjs";
12
12
  import {
13
13
  SYMBOL_BODY_CHAR_CAP,
14
14
  extractErrorMessage,
@@ -18,7 +18,7 @@ import {
18
18
  redactSecrets,
19
19
  runGit,
20
20
  shortFingerprint
21
- } from "./token-goat-chunk-44Y77VHR.mjs";
21
+ } from "./token-goat-chunk-IVCTQPZD.mjs";
22
22
  import {
23
23
  registerReset
24
24
  } from "./token-goat-chunk-AO2QD2AG.mjs";
@@ -404,10 +404,10 @@ var COMMAND_PATTERNS = {
404
404
  gitImmutable: /^\s*git\s+show\s+[0-9a-f]{40}\b/i,
405
405
  gitDiffScoped: /\s--\s+\S/,
406
406
  dirListing: /^\s*(?:ls|eza|exa|dir|Get-ChildItem|gci)\b/i,
407
- depList: /^\s*(?:npm\s+(?:-\S+\s+)*(?:ls|list)\b|pip\s+(?:-\S+\s+)*(?:list|freeze)\b|uv\s+pip\s+(?:-\S+\s+)*(?:list|freeze)\b|pnpm\s+(?:-\S+\s+)*(?:list|ls)\b|yarn\s+(?:-\S+\s+)*(?:list)\b|cargo\s+(?:-\S+\s+)*tree\b|bundle\s+(?:-\S+\s+)*(?:list|show)\b|composer\s+(?:-\S+\s+)*show\b)/i,
408
- npmInstall: /^\s*npm\s+(?:-\S+\s+)*(?:install|ci)\b/i,
409
- npmAudit: /^\s*npm\s+(?:-\S+\s+)*audit\b(?!.*(?:--fix|fix)\b)/i,
410
- npmOutdated: /^\s*npm\s+(?:-\S+\s+)*outdated\b/i,
407
+ depList: /^\s*(?:npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:ls|list)\b|pip\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:list|freeze)\b|uv\s+pip\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:list|freeze)\b|pnpm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:list|ls)\b|yarn\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:list)\b|cargo\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*tree\b|bundle\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:list|show)\b|composer\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*show\b)/i,
408
+ npmInstall: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:install|ci)\b/i,
409
+ npmAudit: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*audit\b(?!.*(?:--fix|fix)\b)/i,
410
+ npmOutdated: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*outdated\b/i,
411
411
  envProbe: /^\s*(?:node\s+(?:-v|--version)|npm\s+(?:-v|--version)|python3?\s+(?:(?:-V)\b|--?version)|git\s+--version|uv\s+--version|go\s+version|rustc\s+--version|cargo\s+--version|java\s+--version|ruby\s+--version|gem\s+--version|php\s+--version|which\b|where\b)/i,
412
412
  npx: /^\s*npx\s+(?:--?yes\s+)?(?!.*\b(?:install|add|remove|uninstall|i|rm|update|upgrade|set|get|publish|link|ci|audit|shrinkwrap|dedupe|prune|rebuild)\b)/i,
413
413
  gitPush: /^\s*git\s+push\b/i,