token-goat 2.9.5 → 2.9.8

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
@@ -129,7 +129,7 @@ The fastest way to reduce AI token costs is fixing these five, not writing short
129
129
  | `claude-in-chrome`'s `computer`/`browser_batch` return a raw, full-resolution base64 screenshot in-band, with no destination-file option to redirect through image-shrink | Inline screenshot blocks are shrunk via the same image-shrink pipeline in place, and a repeated `Tab Context:` listing (appended to nearly every call, often unchanged) collapses to a placeholder once seen unchanged this session |
130
130
  | Agent tool spawns a subagent with no orientation and no reuse hints | A `PreToolUse` handler appends a compact briefing pack to the prompt: a one-line project-map summary, 2-3 recent cached-output IDs, and a surgical-read reminder (~300 tokens) |
131
131
  | Subagent's own final report runs long and gets discarded once the parent moves on | Agent tool results ≥8000 characters get a recall pointer appended (`token-goat recall`); the original report always reaches the parent untouched |
132
- | Agent tool spawns with no `subagent_type` (or an explicit `general-purpose`), so the lane starts by paying for every tool and MCP schema on the machine | Once per session, a `PostToolUse` advisory names up to three `tools:`-restricted agent definitions found in `~/.claude/agents` and suggests passing one as `subagent_type` on a future spawn that fits. It fires only when at least one restricted definition exists, states outright that the observed spawn already ran and nothing was saved, and records a zero-credit stat |
132
+ | Agent tool spawns with no `subagent_type` (or an explicit `general-purpose`), so the lane starts by paying for every tool and MCP schema on the machine | Once per session, a `PostToolUse` advisory names up to three `tools:`-restricted agent definitions found in `~/.claude/agents` or the current project's `.claude/agents` and suggests passing one as `subagent_type` on a future spawn that fits. It fires only when at least one restricted definition exists, states outright that the observed spawn already ran and nothing was saved, and records a zero-credit stat |
133
133
  | Large MCP tool result (≥2 KB) is a homogeneous array of objects, e.g. a list/search result | Deterministic structural compression: table-ified into one header row + tab-delimited rows, with columns constant across every row hoisted into a single `constant:` line instead of repeated per row; only applied when it saves ≥15%. Full original always recoverable via `token-goat bash-output <id>` (labeled `[token-goat: compressed, full via mcp-output <id>]`). Disable with `TOKEN_GOAT_MCP_COMPRESS=0` |
134
134
  | Large MCP tool result (≥2 KB) doesn't table-ify (an object-rooted config dump, a single-resource response, a nested settings tree) but most of its bytes are `null`/`""`/`[]`/`{}` | Deterministic empty-value pruning: recursively drops those four empty shapes to a fixed point (a container left empty by its own dropped children is dropped too) and renders the remainder as compact JSON with a trailing `dropped N empty value(s) (null, "", [], {})` summary line; `0` and `false` are never dropped; only applied when it saves ≥15%. Same full-recovery-by-id guarantee and `TOKEN_GOAT_MCP_COMPRESS=0` opt-out as the table pass |
135
135
  | GitHub MCP tool result (`list_pull_requests`, `list_issues`, `search_code`, `get_file_contents`, `pull_request_read`, …) carries dozens of boilerplate fields per object | GitHub compression pack strips `_links`, `node_id`, `gravatar_id`, `site_admin`, and every `*_url` field (`avatar_url`, `html_url`, `events_url`, `gists_url`, `followers_url`, …) except `download_url`/`git_url`/`clone_url`/`ssh_url`, before handing the shrunk JSON to the same table-ifying pass — same `TOKEN_GOAT_MCP_COMPRESS=0` opt-out and full-recovery-by-id guarantee |
@@ -169,6 +169,9 @@ The fastest way to reduce AI token costs is fixing these five, not writing short
169
169
  | Fetched web content lands raw in model context | Always wrapped in an untrusted-content fence; scanned for attack patterns, and a matched pattern name is written to the log and into the fence's notice |
170
170
  | Chatty log repeats the same error or event thousands of times | `token-goat logfold` collapses consecutive duplicates to `[Nx]` counts; same event logged with different timestamps or request IDs folds correctly — ~90–95% smaller on repetitive logs |
171
171
  | Reading poetry.lock or package-lock.json to find a pinned version | `token-goat lockdeps` returns a name/version table of direct dependencies; optional packages and transitive entries excluded |
172
+ | Large SVG / diagram file (≥8 KB) read in full | Coordinate path flooding blocked; extracted layer/group IDs and title shown; redirects to `token-goat xml-outline` and `xml-query` |
173
+ | Broad recursive Glob sweep (`*`, `**/*`) on root directory | Pre-Glob hook warns against tree-dumping and points at `token-goat map --compact` for fast, lightweight structure inspection |
174
+ | Compound test/build pipeline (`npm run build && npm run typecheck && npm test`) | Post-Bash hook routes chained build/test/lint commands to `generic-ci` compression, dropping verbose passing steps and compiler noise |
172
175
 
173
176
  On a per-token API plan, 100K wasted tokens per session runs about $0.30. Five sessions a week is ~$450/year. AI coding cost reduction at that scale comes from fixing the waste, not from using the product less. Token-goat is free. And on subscription plans, it can result in limits feeling 10x higher.
174
177
 
@@ -422,7 +425,7 @@ never drift on where `mcp.json` lives or what key name it looks for.
422
425
 
423
426
  **What the index actually holds, in plain terms.** The point of a surgical read is returning a function body without the file around it, which means the database stores those bodies. `symbols.body` holds the source text of every indexed symbol, `symbols.docstring` its doc comment, `refs.context` the line around each reference, and `chunks.text` the passages that semantic search embeds. There is also a full-text index over the bodies and docstrings. So the database is not a list of names and line numbers: it is a substantial copy of your source, sitting in a plain unencrypted SQLite file outside the repository.
424
427
 
425
- The file-by-file table for each harness, and the path that file sits at: **[What gets installed](docs/install.md#what-gets-installed)**.
428
+ The file-by-file table for each harness, and the path that file sits at: **[What gets installed](docs/install.md#what-gets-installed)** (see also **[Permissions & auto-approval](docs/install.md#command-auto-approval-and-permissions)**).
426
429
 
427
430
  ## Zero maintenance
428
431
 
@@ -86,7 +86,7 @@ import {
86
86
  runZipRead,
87
87
  symbolNamesInFile,
88
88
  upsertNote
89
- } from "./token-goat-chunk-4P6GTCMM.mjs";
89
+ } from "./token-goat-chunk-6RAUVAKW.mjs";
90
90
  import {
91
91
  DEFAULT_RECONCILE_BUDGET_MS,
92
92
  GEMINI_TOOL_NAME_MAP,
@@ -108,7 +108,7 @@ import {
108
108
  runReconcile,
109
109
  storeWebOutput,
110
110
  summarizeResidentContext
111
- } from "./token-goat-chunk-JO5JX72D.mjs";
111
+ } from "./token-goat-chunk-VEEHPNBV.mjs";
112
112
  import {
113
113
  AGENT_SALT_MARKER,
114
114
  BASH_OUTPUT_SUBDIR,
@@ -121,9 +121,6 @@ import {
121
121
  SESSIONS_SUBDIR,
122
122
  SKILLS_OUTPUT_SUBDIR,
123
123
  SKIP_DIRS,
124
- UNTRUSTED_FILE_TAG,
125
- UNTRUSTED_TOOL_TAG,
126
- UNTRUSTED_WEB_TAG,
127
124
  WORKER_HEARTBEAT_STALE_MS,
128
125
  WorkerAlreadyRunningError,
129
126
  applyIndexingPriority,
@@ -154,8 +151,6 @@ import {
154
151
  extractCompactFromMarker,
155
152
  extractNamedSection,
156
153
  fenceUntrusted,
157
- fenceUntrustedContent,
158
- fenceUntrustedOcrText,
159
154
  findClaudeMdFiles,
160
155
  findContentDuplicates,
161
156
  findLatestSessionId,
@@ -195,6 +190,7 @@ import {
195
190
  isParseSkipEligible,
196
191
  isRecallCacheType,
197
192
  isSuppressionCategory,
193
+ isTreeSitterAvailable,
198
194
  isWorkerRunning,
199
195
  listBlobs,
200
196
  listOutputs,
@@ -235,12 +231,14 @@ import {
235
231
  storeCompact,
236
232
  storeOutput,
237
233
  tokenGoatHome,
234
+ treeSitterCoreAvailable,
235
+ treeSitterCoreLoadError,
238
236
  trimToBudget,
239
237
  urlPolicyDenialReason,
240
238
  visionTokensSavedByText,
241
239
  walkProject,
242
240
  writeCompact
243
- } from "./token-goat-chunk-U4FTM2SB.mjs";
241
+ } from "./token-goat-chunk-TXZY7C24.mjs";
244
242
  import {
245
243
  C,
246
244
  CONFIG_KEY_ENV_OVERRIDES,
@@ -249,11 +247,15 @@ import {
249
247
  FILTERS,
250
248
  LOCK_WAIT_MS_HARDENED,
251
249
  MATERIALIZE_SHRUNK_IMAGE_JS,
250
+ PACKAGE_NAME,
252
251
  PROJECT_LOCKED_KEYS,
253
252
  PROJECT_LOCKED_SECTIONS,
254
253
  RESET,
255
254
  TOOL_FILTERS,
256
255
  ToolFilter,
256
+ UNTRUSTED_FILE_TAG,
257
+ UNTRUSTED_TOOL_TAG,
258
+ UNTRUSTED_WEB_TAG,
257
259
  VERSION,
258
260
  _useRichStats,
259
261
  anchoredMarkerPattern,
@@ -283,6 +285,8 @@ import {
283
285
  envBool,
284
286
  escapeRegExp,
285
287
  extractErrorMessage,
288
+ fenceUntrustedContent,
289
+ fenceUntrustedOcrText,
286
290
  fg,
287
291
  fileIsAbsent,
288
292
  findProject,
@@ -337,6 +341,7 @@ import {
337
341
  requirePositiveStrictInt,
338
342
  resolveConfigKeyLayer,
339
343
  resolveIndexPath,
344
+ resolveOnPath,
340
345
  resolveProjectRoot,
341
346
  runGit,
342
347
  safeSlice,
@@ -344,6 +349,7 @@ import {
344
349
  savedTokensFromBytes,
345
350
  sleepSync,
346
351
  stripAnsi,
352
+ stripLower,
347
353
  stripOwnHooksFromMap,
348
354
  stripStaleGroupHooks,
349
355
  suggestPackageNames,
@@ -366,7 +372,7 @@ import {
366
372
  withFileLock,
367
373
  withRetryOnLock,
368
374
  writeJsonSettings
369
- } from "./token-goat-chunk-ZOKNDG6V.mjs";
375
+ } from "./token-goat-chunk-JZYJH76S.mjs";
370
376
  import {
371
377
  __export,
372
378
  init_define_import_meta_env
@@ -5990,7 +5996,7 @@ init_define_import_meta_env();
5990
5996
  import * as fs12 from "fs";
5991
5997
  import * as os8 from "os";
5992
5998
  import * as path12 from "path";
5993
- import { execSync, spawnSync as spawnSync2 } from "child_process";
5999
+ import { spawnSync as spawnSync2 } from "child_process";
5994
6000
  function globalMcpConfigPath() {
5995
6001
  const copilotHome = process.env["COPILOT_HOME"];
5996
6002
  const root = copilotHome !== void 0 && copilotHome.trim() !== "" ? path12.resolve(copilotHome) : path12.join(os8.homedir(), ".copilot");
@@ -6077,12 +6083,16 @@ function checkMcpProcessHealth(processes) {
6077
6083
  }
6078
6084
  function runProcessListCommand() {
6079
6085
  const command = "Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,CommandLine | ConvertTo-Json -Compress";
6080
- return execSync(`powershell.exe -NoProfile -NonInteractive -Command "${command}"`, {
6086
+ const systemRoot = process.env["SystemRoot"] ?? process.env["windir"] ?? "C:\\Windows";
6087
+ const shell = path12.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
6088
+ const result = spawnSync2(fs12.existsSync(shell) ? shell : "powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command], {
6081
6089
  encoding: "utf8",
6082
6090
  timeout: 2e4,
6083
6091
  maxBuffer: 10 * 1024 * 1024,
6084
6092
  windowsHide: true
6085
6093
  });
6094
+ if (result.error !== void 0) throw result.error;
6095
+ return result.stdout ?? "";
6086
6096
  }
6087
6097
  function readWindowsProcesses(runCommand = runProcessListCommand) {
6088
6098
  if (process.platform !== "win32") return [];
@@ -6287,20 +6297,14 @@ function checkDirtyQueueHealth(dataDir2) {
6287
6297
  return { name: "Dirty queue", status: "ok", message: `${pendingCount} file(s) pending, worker actively draining` };
6288
6298
  }
6289
6299
  function checkInstall() {
6290
- try {
6291
- const output = execSync("token-goat --version", { encoding: "utf-8" });
6292
- return {
6293
- name: "Installation",
6294
- status: "ok",
6295
- message: output.trim()
6296
- };
6297
- } catch {
6298
- return {
6299
- name: "Installation",
6300
- status: "fail",
6301
- message: "token-goat command not found; run: npm install -g token-goat-ts"
6302
- };
6300
+ const resolved = resolveOnPath("token-goat");
6301
+ if (resolved !== null) {
6302
+ const isBatch = /\.(?:cmd|bat)$/i.test(resolved);
6303
+ const comspec = path12.join(process.env["SystemRoot"] ?? process.env["windir"] ?? "C:\\Windows", "System32", "cmd.exe");
6304
+ const result = isBatch ? spawnSync2(fs12.existsSync(comspec) ? comspec : "cmd.exe", ["/d", "/s", "/c", resolved, "--version"], { encoding: "utf-8", timeout: 15e3, windowsHide: true }) : spawnSync2(resolved, ["--version"], { encoding: "utf-8", timeout: 15e3, windowsHide: true });
6305
+ if (result.status === 0) return { name: "Installation", status: "ok", message: (result.stdout ?? "").trim() };
6303
6306
  }
6307
+ return { name: "Installation", status: "fail", message: `token-goat command not found; run: npm install -g ${PACKAGE_NAME}` };
6304
6308
  }
6305
6309
  function checkTsCompiler() {
6306
6310
  if (isAvailable2()) {
@@ -6313,6 +6317,29 @@ function checkTsCompiler() {
6313
6317
  message: err2 !== null ? `unavailable: ${extractErrorMessage(err2)}` : "unavailable (not attempted)"
6314
6318
  };
6315
6319
  }
6320
+ var TREE_SITTER_LANGUAGES = ["typescript", "javascript", "python", "go", "rust", "ruby", "java", "c", "cpp"];
6321
+ function checkTreeSitter() {
6322
+ const name = "Tree-sitter";
6323
+ if (!treeSitterCoreAvailable()) {
6324
+ const err2 = treeSitterCoreLoadError();
6325
+ const cause = err2 !== null ? `: ${extractErrorMessage(err2)}` : " (not attempted)";
6326
+ return {
6327
+ name,
6328
+ status: "warn",
6329
+ message: "unavailable -- the source skeleton fold, the code body fold's disk-parse fallback, and tree-sitter indexing are all disabled; likely cause: the optional native dependency `tree-sitter` is not installed, or is not resolvable from the bundle's location" + cause
6330
+ };
6331
+ }
6332
+ const available = TREE_SITTER_LANGUAGES.filter((lang) => isTreeSitterAvailable(lang));
6333
+ if (available.length === TREE_SITTER_LANGUAGES.length) {
6334
+ return { name, status: "ok", message: `available (${available.length}/${TREE_SITTER_LANGUAGES.length} grammars)` };
6335
+ }
6336
+ const missing = TREE_SITTER_LANGUAGES.filter((lang) => !available.includes(lang));
6337
+ return {
6338
+ name,
6339
+ status: "warn",
6340
+ message: `partially available (${available.length}/${TREE_SITTER_LANGUAGES.length} grammars); missing: ${missing.join(", ")}`
6341
+ };
6342
+ }
6316
6343
  function checkEmbeddings(config) {
6317
6344
  const name = "Embeddings";
6318
6345
  if ((config.indexing?.embeddings_enabled ?? true) === false) {
@@ -6688,6 +6715,7 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
6688
6715
  const actualDataDir = dataDir2 || dataDir();
6689
6716
  results.push(checkInstall());
6690
6717
  results.push(checkTsCompiler());
6718
+ results.push(checkTreeSitter());
6691
6719
  results.push(checkStrayClaudeMdBlocks());
6692
6720
  results.push(checkWorkerRunning(actualDataDir) ? { name: "Worker", status: "ok", message: "running" } : { name: "Worker", status: "warn", message: "not running" });
6693
6721
  results.push(checkDbExists(actualDataDir));
@@ -14937,11 +14965,14 @@ function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
14937
14965
  function discoverMcpConfig(projectRoot, home) {
14938
14966
  const mcpJsonPath = path28.join(projectRoot, ".mcp.json");
14939
14967
  const claudeJsonPath = path28.join(home, ".claude.json");
14940
- const sourcesChecked = [mcpJsonPath, claudeJsonPath];
14968
+ const copilotJsonPath = path28.join(home, ".copilot", "mcp-config.json");
14969
+ const sourcesChecked = [mcpJsonPath, claudeJsonPath, copilotJsonPath];
14941
14970
  const fromMcpJson = readMcpJsonFile(mcpJsonPath);
14942
14971
  if (fromMcpJson !== null) return { servers: fromMcpJson, sourcePath: mcpJsonPath, sourcesChecked };
14943
14972
  const fromClaudeJson = readClaudeJsonConfig(claudeJsonPath, projectRoot);
14944
14973
  if (fromClaudeJson !== null) return { servers: fromClaudeJson, sourcePath: claudeJsonPath, sourcesChecked };
14974
+ const fromCopilotJson = readMcpJsonFile(copilotJsonPath);
14975
+ if (fromCopilotJson !== null) return { servers: fromCopilotJson, sourcePath: copilotJsonPath, sourcesChecked };
14945
14976
  return { servers: null, sourcePath: null, sourcesChecked };
14946
14977
  }
14947
14978
  function analyzeMcpCache() {
@@ -15730,7 +15761,7 @@ async function cmdMcpServe() {
15730
15761
  let StdioServerTransport;
15731
15762
  try {
15732
15763
  ;
15733
- ({ createMcpServer } = await import("./token-goat-chunk-DRL5USXI.mjs"));
15764
+ ({ createMcpServer } = await import("./token-goat-chunk-EZZ7IJYM.mjs"));
15734
15765
  ({ StdioServerTransport } = await import("./token-goat-chunk-EVC4TOLE.mjs"));
15735
15766
  } catch (err2) {
15736
15767
  process.stderr.write(
@@ -15755,7 +15786,7 @@ async function cmdHook(event, opts) {
15755
15786
  if (typeof opts.harness === "string" && opts.harness.length > 0) {
15756
15787
  process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
15757
15788
  }
15758
- const { relay } = await import("./token-goat-chunk-GFBXHGFY.mjs");
15789
+ const { relay } = await import("./token-goat-chunk-Q4VCAIBR.mjs");
15759
15790
  await relay(event);
15760
15791
  }
15761
15792
  async function cmdInstall(opts) {
@@ -16688,7 +16719,7 @@ function emitExtraFileArgsNote(command, first, extras, opts = {}) {
16688
16719
  }
16689
16720
  async function cmdCompress(opts) {
16690
16721
  try {
16691
- const bashRunner = await import("./token-goat-chunk-KFFAZ5DJ.mjs");
16722
+ const bashRunner = await import("./token-goat-chunk-WT5OLTT6.mjs");
16692
16723
  if (opts.compress === false) {
16693
16724
  process.exitCode = bashRunner.runRaw(opts.cmd, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
16694
16725
  return;
@@ -16961,8 +16992,12 @@ async function cmdSkillSection(nameHeading, headingArg) {
16961
16992
  const body = decodeSource(fs29.readFileSync(filePath));
16962
16993
  const extracted = extractNamedSection(body, heading);
16963
16994
  if (!extracted) {
16964
- const messages = [`Section '${heading}' not found in skill '${skillName}'`];
16965
16995
  const allHeadings = listSections(filePath);
16996
+ const wanted = stripLower(heading);
16997
+ if (allHeadings.some((h) => stripLower(h) === wanted)) {
16998
+ throw new CliError(`Section '${heading}' in skill '${skillName}' is present but empty`);
16999
+ }
17000
+ const messages = [`Section '${heading}' not found in skill '${skillName}'`];
16966
17001
  const available = filterSimilarHeadings(allHeadings, heading);
16967
17002
  if (available.length > 0) messages.push(didYouMean(available));
16968
17003
  else if (allHeadings.length === 0) messages.push(`skill '${skillName}' has no headings`);
@@ -7,11 +7,11 @@ import {
7
7
  expandGlobs,
8
8
  leftoverIntegrations,
9
9
  run
10
- } from "./token-goat-chunk-5T2K7DEE.mjs";
11
- import "./token-goat-chunk-4P6GTCMM.mjs";
12
- import "./token-goat-chunk-JO5JX72D.mjs";
13
- import "./token-goat-chunk-U4FTM2SB.mjs";
14
- import "./token-goat-chunk-ZOKNDG6V.mjs";
10
+ } from "./token-goat-chunk-2SBHUPBF.mjs";
11
+ import "./token-goat-chunk-6RAUVAKW.mjs";
12
+ import "./token-goat-chunk-VEEHPNBV.mjs";
13
+ import "./token-goat-chunk-TXZY7C24.mjs";
14
+ import "./token-goat-chunk-JZYJH76S.mjs";
15
15
  import "./token-goat-chunk-EEIDFMEM.mjs";
16
16
  import "./token-goat-chunk-A37V4PBF.mjs";
17
17
  export {
@@ -9,7 +9,6 @@ import {
9
9
  MAX_ZIP_OUTPUT_BYTES,
10
10
  OVER_FETCH_FACTOR,
11
11
  SKIP_DIRS,
12
- UNTRUSTED_GITHUB_TAG,
13
12
  ZipInputTooLargeError,
14
13
  ZipOutputTooLargeError,
15
14
  capJsonRows,
@@ -25,8 +24,6 @@ import {
25
24
  extractPdfOutline,
26
25
  extractPdfText,
27
26
  fenceUntrusted,
28
- fenceUntrustedContent,
29
- fenceUntrustedFileContent,
30
27
  formatCsvProfile,
31
28
  formatCsvTable,
32
29
  getEmbeddingCoverage,
@@ -70,10 +67,11 @@ import {
70
67
  walkProject,
71
68
  yamlLineClosesQuote,
72
69
  yamlOpenQuoteAfter
73
- } from "./token-goat-chunk-U4FTM2SB.mjs";
70
+ } from "./token-goat-chunk-TXZY7C24.mjs";
74
71
  import {
75
72
  Database,
76
73
  PER_FILE_COUNTERFACTUAL_CEILING,
74
+ UNTRUSTED_GITHUB_TAG,
77
75
  _detectOpenQuote,
78
76
  _lineClosesQuote,
79
77
  atomicWriteBytes,
@@ -89,6 +87,8 @@ import {
89
87
  escapeRegExp,
90
88
  excludeTestsHiddenNote,
91
89
  extractErrorMessage,
90
+ fenceUntrustedContent,
91
+ fenceUntrustedFileContent,
92
92
  fileIsAbsent,
93
93
  filtersFilteredToEmptyNotice,
94
94
  findHtmlHeadingMatches,
@@ -114,6 +114,7 @@ import {
114
114
  requireNonNegativeStrictInt,
115
115
  requirePositiveStrictInt,
116
116
  resolveIndexPath,
117
+ resolveOnPath,
117
118
  resolveProjectRoot,
118
119
  runGit,
119
120
  savedTokensFromBytes,
@@ -124,7 +125,7 @@ import {
124
125
  unsupportedLanguageName,
125
126
  windowsCmdQuoteArg,
126
127
  withExtension
127
- } from "./token-goat-chunk-ZOKNDG6V.mjs";
128
+ } from "./token-goat-chunk-JZYJH76S.mjs";
128
129
  import {
129
130
  registerReset
130
131
  } from "./token-goat-chunk-EEIDFMEM.mjs";
@@ -457,7 +458,7 @@ init_define_import_meta_env();
457
458
  import * as fs6 from "node:fs";
458
459
  import * as os from "node:os";
459
460
  import * as path6 from "node:path";
460
- import { execFileSync, spawnSync as spawnSync2 } from "node:child_process";
461
+ import { spawnSync as spawnSync2 } from "node:child_process";
461
462
  import { randomUUID } from "node:crypto";
462
463
 
463
464
  // src/ref_blindness.ts
@@ -3827,6 +3828,14 @@ function parseGithubRepoFromRemoteUrl(url) {
3827
3828
  if (https?.[1] !== void 0) return https[1];
3828
3829
  return null;
3829
3830
  }
3831
+ function isSafeRepoSlug(repo) {
3832
+ const parts = repo.split("/");
3833
+ if (parts.length !== 2) return false;
3834
+ return parts.every((p) => /^[A-Za-z0-9._-]+$/.test(p) && p !== "." && p !== ".." && !p.startsWith("-"));
3835
+ }
3836
+ function isSafePrNumber(pr) {
3837
+ return /^[0-9]+$/.test(pr);
3838
+ }
3830
3839
  function parsePrSliceArg(raw) {
3831
3840
  if (raw === "files") return { kind: "files" };
3832
3841
  if (raw === "comments") return { kind: "comments" };
@@ -7371,6 +7380,14 @@ function runPrSlice(opts) {
7371
7380
  }
7372
7381
  repo = resolved;
7373
7382
  }
7383
+ if (!isSafeRepoSlug(repo)) {
7384
+ emitErr(`"${repo}" is not a plain owner/name repository slug -- pass --repo owner/repo`);
7385
+ return 1;
7386
+ }
7387
+ if (!isSafePrNumber(opts.pr)) {
7388
+ emitErr(`"${opts.pr}" is not a pull request number`);
7389
+ return 1;
7390
+ }
7374
7391
  if (!isGhAuthenticated()) {
7375
7392
  emitErr("gh is not authenticated -- run `gh auth login`");
7376
7393
  return 1;
@@ -10916,13 +10933,7 @@ function runAsk(opts) {
10916
10933
  return degrade(`${BACKEND_ENV}=${backendLabel} is set, but no indexed symbol matched this question, so there is no context to answer from -- try different wording or token-goat semantic`);
10917
10934
  }
10918
10935
  const isWin = process.platform === "win32";
10919
- let backendPath = null;
10920
- try {
10921
- const whichOut = execFileSync(isWin ? "where.exe" : "which", [backendLabel], { encoding: "utf8" });
10922
- const found = (whichOut ?? "").trim().split("\n")[0]?.trim() ?? "";
10923
- if (found) backendPath = found;
10924
- } catch {
10925
- }
10936
+ const backendPath = resolveOnPath(backendLabel);
10926
10937
  if (!backendPath) return degrade(`${BACKEND_ENV}=${backendLabel} is set, but '${backendLabel}' was not found on PATH`);
10927
10938
  const rawContext = hits.map((h, i) => `[${i + 1}] ${h.filePath}
10928
10939
  ${h.body ?? ""}`).join("\n\n");
@@ -25,7 +25,7 @@ import {
25
25
  runSkeleton,
26
26
  runSymbol,
27
27
  withPinnedReads
28
- } from "./token-goat-chunk-4P6GTCMM.mjs";
28
+ } from "./token-goat-chunk-6RAUVAKW.mjs";
29
29
  import {
30
30
  buildProjectMap,
31
31
  embeddingsDepsAvailable,
@@ -34,7 +34,7 @@ import {
34
34
  getProjectIndexCounts,
35
35
  isWorkerRunning,
36
36
  mapLookupBytesSaved
37
- } from "./token-goat-chunk-U4FTM2SB.mjs";
37
+ } from "./token-goat-chunk-TXZY7C24.mjs";
38
38
  import {
39
39
  ENV_KEYS,
40
40
  VERSION,
@@ -48,7 +48,7 @@ import {
48
48
  recordStat,
49
49
  resolveProjectRoot,
50
50
  savedTokensFromBytes
51
- } from "./token-goat-chunk-ZOKNDG6V.mjs";
51
+ } from "./token-goat-chunk-JZYJH76S.mjs";
52
52
  import "./token-goat-chunk-EEIDFMEM.mjs";
53
53
  import {
54
54
  init_define_import_meta_env