token-goat 2.9.17 → 2.9.19

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.
Files changed (42) hide show
  1. package/README.md +2 -0
  2. package/dist/{token-goat-chunk-HRUGS6CO.mjs → token-goat-chunk-2GTB5RBZ.mjs} +365 -26
  3. package/dist/{token-goat-chunk-W2GUUQAQ.mjs → token-goat-chunk-3W5P673V.mjs} +1 -1
  4. package/dist/{token-goat-chunk-5WB7GDH4.mjs → token-goat-chunk-62VJ47WN.mjs} +6 -6
  5. package/dist/{token-goat-chunk-C26ZBGBC.mjs → token-goat-chunk-7DGTVBWP.mjs} +58 -44
  6. package/dist/{token-goat-chunk-GPCCYQSS.mjs → token-goat-chunk-AVRD7SBO.mjs} +60 -8
  7. package/dist/{token-goat-chunk-66224KCZ.mjs → token-goat-chunk-BTKDZW7F.mjs} +1 -1
  8. package/dist/{token-goat-chunk-LMFO66YD.mjs → token-goat-chunk-DA43OE7L.mjs} +1 -1
  9. package/dist/{token-goat-chunk-YFGEUIEO.mjs → token-goat-chunk-DA6TIDLI.mjs} +21 -16
  10. package/dist/{token-goat-chunk-CPHVXKNU.mjs → token-goat-chunk-DHIXE6GV.mjs} +2 -2
  11. package/dist/{token-goat-chunk-73GXYYZR.mjs → token-goat-chunk-EP7TCWHM.mjs} +304 -46
  12. package/dist/{token-goat-chunk-EKGVCAES.mjs → token-goat-chunk-EXLLLIC3.mjs} +4 -4
  13. package/dist/{token-goat-chunk-CICJFVIH.mjs → token-goat-chunk-F24NE3FO.mjs} +3 -3
  14. package/dist/{token-goat-chunk-THBLW56G.mjs → token-goat-chunk-FJZ5MZK7.mjs} +3 -3
  15. package/dist/{token-goat-chunk-PKNOHVWP.mjs → token-goat-chunk-FQYMZASI.mjs} +632 -350
  16. package/dist/{token-goat-chunk-RFFUCQK7.mjs → token-goat-chunk-FX5DHKGF.mjs} +5 -5
  17. package/dist/{token-goat-chunk-RB24DBBS.mjs → token-goat-chunk-GYC34EGE.mjs} +7 -8
  18. package/dist/{token-goat-chunk-2M33Q3O4.mjs → token-goat-chunk-HPMMLV37.mjs} +369 -120
  19. package/dist/{token-goat-chunk-BK3RPPTK.mjs → token-goat-chunk-IBTFGZBX.mjs} +19 -14
  20. package/dist/{token-goat-chunk-MWSVUKVV.mjs → token-goat-chunk-JNIFSVWM.mjs} +36 -10
  21. package/dist/{token-goat-chunk-KZPO4DWC.mjs → token-goat-chunk-KWM6KBPA.mjs} +2 -2
  22. package/dist/{token-goat-chunk-G75WIVFM.mjs → token-goat-chunk-L2UOJBV4.mjs} +21 -20
  23. package/dist/{token-goat-chunk-2XA5ZW4T.mjs → token-goat-chunk-LACH3WKS.mjs} +4 -4
  24. package/dist/token-goat-chunk-MU6I4IDI.mjs +337 -0
  25. package/dist/{token-goat-chunk-SWRKVA3J.mjs → token-goat-chunk-NJKNAC37.mjs} +1 -1
  26. package/dist/{token-goat-chunk-ERTXEKB6.mjs → token-goat-chunk-NMTKNYGF.mjs} +2 -0
  27. package/dist/{token-goat-chunk-CKIJWTRQ.mjs → token-goat-chunk-NYFYIRG3.mjs} +12 -12
  28. package/dist/{token-goat-chunk-4EXFN2AW.mjs → token-goat-chunk-OZRSREKR.mjs} +2 -2
  29. package/dist/token-goat-chunk-P46RTOAQ.mjs +27 -0
  30. package/dist/{token-goat-chunk-IDHWKNKZ.mjs → token-goat-chunk-PIAESKVU.mjs} +139 -23
  31. package/dist/{token-goat-chunk-26YUO7MP.mjs → token-goat-chunk-S7RFIX4Y.mjs} +180 -50
  32. package/dist/{token-goat-chunk-2VASS5VR.mjs → token-goat-chunk-SOKAPOOC.mjs} +23 -4
  33. package/dist/token-goat-chunk-VJQBYSXG.mjs +20 -0
  34. package/dist/{token-goat-chunk-7VQMOBPQ.mjs → token-goat-chunk-WKO5TVNA.mjs} +948 -618
  35. package/dist/{token-goat-chunk-UBZCON6N.mjs → token-goat-chunk-ZGQXRYKE.mjs} +29 -5
  36. package/dist/token-goat-hook.mjs +12 -12
  37. package/dist/token-goat.core.mjs +21 -20
  38. package/docs/cli.md +4 -3
  39. package/docs/security.md +1 -1
  40. package/package.json +1 -1
  41. package/dist/token-goat-chunk-7EGKXBWT.mjs +0 -173
  42. package/dist/token-goat-chunk-GLOM5W5C.mjs +0 -93
@@ -2,15 +2,15 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  getDb
5
- } from "./token-goat-chunk-MWSVUKVV.mjs";
5
+ } from "./token-goat-chunk-JNIFSVWM.mjs";
6
6
  import {
7
7
  foldPath,
8
8
  globalDbPath,
9
9
  isCaseInsensitiveFs
10
- } from "./token-goat-chunk-UBZCON6N.mjs";
10
+ } from "./token-goat-chunk-ZGQXRYKE.mjs";
11
11
  import {
12
12
  normalizePath
13
- } from "./token-goat-chunk-ERTXEKB6.mjs";
13
+ } from "./token-goat-chunk-NMTKNYGF.mjs";
14
14
  import {
15
15
  init_define_import_meta_env
16
16
  } from "./token-goat-chunk-A37V4PBF.mjs";
@@ -50,6 +50,9 @@ function projectScopeClause(column) {
50
50
  }
51
51
 
52
52
  // src/index_reader.ts
53
+ function indexKey(p) {
54
+ return normalizePath(p);
55
+ }
53
56
  function toSymbolEntry(row) {
54
57
  return {
55
58
  filePath: row.file_path,
@@ -75,7 +78,7 @@ function applyRootDirScope(rootDir, column, where, params) {
75
78
  if (rootDir === void 0) return;
76
79
  const { clause, params: bounds } = projectScopeClause(column);
77
80
  where.push(clause);
78
- params.push(...bounds(rootDir));
81
+ params.push(...bounds(indexKey(rootDir)));
79
82
  }
80
83
  function buildSymbolWhere(opts) {
81
84
  const where = [];
@@ -85,13 +88,14 @@ function buildSymbolWhere(opts) {
85
88
  params.push(opts.name);
86
89
  }
87
90
  if (opts.filePath !== void 0) {
88
- if (opts.filePath.includes("/") || opts.filePath.includes("\\")) {
89
- const alt = opts.filePath.includes("/") ? opts.filePath.replace(/\//g, "\\") : opts.filePath.replace(/\\/g, "/");
91
+ const key = indexKey(opts.filePath);
92
+ if (key.includes("/") || key.includes("\\")) {
93
+ const alt = key.includes("/") ? key.replace(/\//g, "\\") : key.replace(/\\/g, "/");
90
94
  where.push(`(${pathEqClause("file_path")} OR ${pathEqClause("file_path")})`);
91
- params.push(foldPath(opts.filePath), foldPath(alt));
95
+ params.push(foldPath(key), foldPath(alt));
92
96
  } else {
93
97
  where.push(pathEqClause("file_path"));
94
- params.push(foldPath(opts.filePath));
98
+ params.push(foldPath(key));
95
99
  }
96
100
  }
97
101
  if (opts.kind !== void 0) {
@@ -141,7 +145,7 @@ function buildRefsWhere(opts) {
141
145
  const params = [opts.name];
142
146
  if (opts.filePath !== void 0) {
143
147
  where.push(pathEqClause("file_path"));
144
- params.push(foldPath(opts.filePath));
148
+ params.push(foldPath(indexKey(opts.filePath)));
145
149
  }
146
150
  applyRootDirScope(opts.rootDir, "file_path", where, params);
147
151
  return { clause: `WHERE ${where.join(" AND ")}`, params };
@@ -164,7 +168,7 @@ function countRefs(opts, dbPath = globalDbPath()) {
164
168
  function queryRefsByContext(context, filePath, dbPath = globalDbPath()) {
165
169
  const sql = `SELECT file_path, name, line, col, context FROM refs WHERE context = ? AND ${pathEqClause("file_path")} ORDER BY line LIMIT 20`;
166
170
  const db = getDb(dbPath);
167
- const rows = db.prepare(sql).all(context, foldPath(filePath));
171
+ const rows = db.prepare(sql).all(context, foldPath(indexKey(filePath)));
168
172
  return rows.map(toRefEntry);
169
173
  }
170
174
  var REF_COUNT_BATCH = 900;
@@ -192,7 +196,7 @@ function queryRefCounts(names, dbPath = globalDbPath(), rootDir) {
192
196
  function getProjectFileEntries(rootDir, dbPath = globalDbPath()) {
193
197
  const db = getDb(dbPath);
194
198
  const { clause, params } = projectScopeClause("path");
195
- const rows = db.prepare(`SELECT path, sha, mtime, language, indexed_at, embed_sha, parser_sha FROM files WHERE ${clause}`).all(...params(rootDir));
199
+ const rows = db.prepare(`SELECT path, sha, mtime, language, indexed_at, embed_sha, parser_sha FROM files WHERE ${clause}`).all(...params(indexKey(rootDir)));
196
200
  const out = /* @__PURE__ */ new Map();
197
201
  for (const row of rows) {
198
202
  out.set(foldPath(row.path), {
@@ -209,11 +213,12 @@ function getProjectFileEntries(rootDir, dbPath = globalDbPath()) {
209
213
  }
210
214
  function getFileEntry(filePath, dbPath = globalDbPath()) {
211
215
  const db = getDb(dbPath);
216
+ const key = indexKey(filePath);
212
217
  let row = db.prepare(
213
218
  `SELECT path, sha, mtime, language, indexed_at, embed_sha, parser_sha FROM files WHERE ${pathEqClause("path")}`
214
- ).get(foldPath(filePath));
215
- if (row === void 0 && (filePath.includes("/") || filePath.includes("\\"))) {
216
- const altPath = filePath.includes("/") ? filePath.replace(/\//g, "\\") : filePath.replace(/\\/g, "/");
219
+ ).get(foldPath(key));
220
+ if (row === void 0 && (key.includes("/") || key.includes("\\"))) {
221
+ const altPath = key.includes("/") ? key.replace(/\//g, "\\") : key.replace(/\\/g, "/");
217
222
  row = db.prepare(
218
223
  `SELECT path, sha, mtime, language, indexed_at, embed_sha, parser_sha FROM files WHERE ${pathEqClause("path")}`
219
224
  ).get(foldPath(altPath));
@@ -15,10 +15,10 @@ import {
15
15
  foldCase,
16
16
  foldPath,
17
17
  sleepSync
18
- } from "./token-goat-chunk-UBZCON6N.mjs";
18
+ } from "./token-goat-chunk-ZGQXRYKE.mjs";
19
19
  import {
20
20
  safeJoin
21
- } from "./token-goat-chunk-ERTXEKB6.mjs";
21
+ } from "./token-goat-chunk-NMTKNYGF.mjs";
22
22
  import {
23
23
  init_define_import_meta_env
24
24
  } from "./token-goat-chunk-A37V4PBF.mjs";
@@ -2044,12 +2044,29 @@ CREATE INDEX IF NOT EXISTS idx_cache_recall_type ON cache_recall(cache_type);
2044
2044
  -- emission event (a hook returning a 'context' HookOutput classified as a discretionary
2045
2045
  -- efficiency nudge, as opposed to a mandatory informational injection -- see hint_stats.ts's
2046
2046
  -- doc comment for the exact category list and what is deliberately excluded). correlator is a
2047
- -- best-effort file-path/output-id substring extracted from the hint's own text, used to check
2047
+ -- file-path/output-id pointer the hint carries, used to check
2048
2048
  -- whether a later Bash tool call in the same session actually followed the hint's specific
2049
- -- pointer (see resolvePendingHintsForEvent) -- NULL when no such pointer could be extracted,
2050
- -- in which case the row is inserted already resolved with acted_on=0 (counted as emitted, never
2051
- -- eligible for auto-detected credit). calls_remaining is the countdown of subsequent tool-use
2052
- -- events still eligible to resolve this row before it is considered timed out.
2049
+ -- pointer (see resolvePendingHintsForEvent). Supplied by the hint builder itself via
2050
+ -- HookOutput's correlators field wherever one exists, and only fallen back to a regex scrape
2051
+ -- of the rendered hint text (hint_stats.ts's extractPathCorrelator, whose doc comment records
2052
+ -- what that scrape actually measured) where it does not. A row can name SEVERAL files -- the
2053
+ -- sed/awk line-range hint joins one per file into one emission -- stored newline-separated and
2054
+ -- matched on any member; see joinCorrelators. NULL when no pointer could be supplied or extracted,
2055
+ -- in which case the row is inserted already resolved and observable=0: there is nothing a later
2056
+ -- command could do that this row would recognize, so no verdict about it was ever observed.
2057
+ -- observable=1 is the normal case. Efficacy counts only observable rows, numerator and denominator
2058
+ -- both, because an unobservable row's acted_on is a fabrication in whichever direction the code
2059
+ -- picked and averaging fabrications into a measured rate is how 174 of bash_redirect's 698 rows
2060
+ -- came to hold its efficacy down to 0.86% when the observed figure was 1.15%. calls_remaining is
2061
+ -- the countdown of subsequent tool-use events still eligible to resolve this row before it is
2062
+ -- considered timed out. displayed=0 marks a detection that never reached the agent -- auto-
2063
+ -- suppressed, or declined by a hint's own net-benefit gate -- recorded so the ledger can answer
2064
+ -- how often a hint fired at all, which it previously could not: a suppressed detection left no
2065
+ -- trace, so a fully-muted category was indistinguishable from one that had stopped triggering.
2066
+ -- Such a row is always observable=0 and bytes_emitted=0: nothing was shown, so nothing was spent
2067
+ -- and no verdict was available. It shares hint_emissions' 180-day retention (pruneHintEmissions
2068
+ -- in stats.ts deletes on emitted_at alone), deliberately unlike hint_suppression_probes, which is
2069
+ -- one upserted row per key rather than a growing log and is therefore never pruned.
2053
2070
  CREATE TABLE IF NOT EXISTS hint_emissions (
2054
2071
  id INTEGER PRIMARY KEY AUTOINCREMENT,
2055
2072
  category TEXT NOT NULL,
@@ -2060,7 +2077,9 @@ CREATE TABLE IF NOT EXISTS hint_emissions (
2060
2077
  resolved INTEGER NOT NULL DEFAULT 0,
2061
2078
  acted_on INTEGER NOT NULL DEFAULT 0,
2062
2079
  calls_remaining INTEGER NOT NULL DEFAULT 0,
2063
- bytes_emitted INTEGER
2080
+ bytes_emitted INTEGER,
2081
+ observable INTEGER NOT NULL DEFAULT 1,
2082
+ displayed INTEGER NOT NULL DEFAULT 1
2064
2083
  );
2065
2084
  CREATE INDEX IF NOT EXISTS idx_hint_emissions_category ON hint_emissions(category);
2066
2085
  CREATE INDEX IF NOT EXISTS idx_hint_emissions_session_pending ON hint_emissions(session_id, resolved);
@@ -2206,7 +2225,7 @@ CREATE TRIGGER IF NOT EXISTS cache_recall_au AFTER UPDATE ON cache_recall BEGIN
2206
2225
  VALUES (new.row_id, new.label, new.content);
2207
2226
  END;
2208
2227
  `;
2209
- var SCHEMA_VERSION = 14;
2228
+ var SCHEMA_VERSION = 16;
2210
2229
  function alterTableIdempotent(conn, sql) {
2211
2230
  try {
2212
2231
  conn.exec(sql);
@@ -2262,7 +2281,14 @@ var MIGRATIONS = {
2262
2281
  // v12 -> v13: adds files.parser_sha, the digest of the extraction logic that produced this file's rows, tracked separately from files.sha for the same reason embed_sha is -- content freshness and parse freshness are different questions, and the content sha alone could only ever answer the first. A pre-existing v12 database's `files` table predates the column, so it needs an explicit ALTER TABLE here; a brand-new database already has it from SCHEMA_SQL's CREATE TABLE above, so the ALTER TABLE would fail with "duplicate column name" there -- swallow exactly that error and rethrow anything else, same pattern as v1 -> v2 / v2 -> v3 / v8 -> v9 / v9 -> v10 above. Deliberately left NULL for every existing row rather than backfilled with the current fingerprint: NULL is the truthful answer (nobody recorded which parser wrote those rows), and it is also the answer that makes the freshness gates reparse them once, which is exactly what a database indexed by an older parser needs. v13 -> v14: changes both FTS5 tables' tokenizer to `unicode61 remove_diacritics 2`, so a search for `Noi` or `Viet` finds `Hà Nội` and `Việt Nam` -- combining marks that `remove_diacritics 1`, FTS5's default, leaves in place. This is the first schema change that `CREATE VIRTUAL TABLE IF NOT EXISTS` cannot express at all rather than merely cannot express on a populated table: against an existing virtual table that statement is a silent no-op, so without MIGRATIONS[13] the new tokenizer would reach only databases created after this release. The step drops both tables, re-runs FTS_SQL to re-create them at the current declaration, and rebuilds each from its content table.
2263
2282
  12: (conn) => alterTableIdempotent(conn, "ALTER TABLE files ADD COLUMN parser_sha TEXT"),
2264
2283
  // v13 -> v14: re-creates both FTS5 tables at the tokenizer FTS_SQL currently declares (see the SCHEMA_VERSION comment above for why no `IF NOT EXISTS` form can do this).
2265
- 13: rebuildFtsAtCurrentTokenizer
2284
+ 13: rebuildFtsAtCurrentTokenizer,
2285
+ // v14 -> v15: adds hint_emissions.observable and backfills it from the column that always implied it. A pre-existing v14 database's `hint_emissions` table predates the column, so it needs an explicit ALTER TABLE here; a brand-new database already has it from SCHEMA_SQL's CREATE TABLE above, so the ALTER TABLE would fail with "duplicate column name" there -- swallow exactly that error and rethrow anything else, same pattern as v8 -> v9 / v9 -> v10 / v12 -> v13 above. The backfill is exact rather than a guess: until this version a NULL correlator was the only way a row could be born with nothing to wait for, so `correlator IS NULL` identifies precisely the population the new column names. It runs unconditionally after the ALTER because alterTableIdempotent swallows the duplicate-column case, and re-running the UPDATE on an already-migrated database sets the same rows to the same values.
2286
+ 14: (conn) => {
2287
+ alterTableIdempotent(conn, "ALTER TABLE hint_emissions ADD COLUMN observable INTEGER NOT NULL DEFAULT 1");
2288
+ conn.prepare("UPDATE hint_emissions SET observable = 0 WHERE correlator IS NULL").run();
2289
+ },
2290
+ // v15 -> v16: adds hint_emissions.displayed (see the SCHEMA_VERSION comment above for why). A pre-existing v15 database's `hint_emissions` table predates the column, so it needs an explicit ALTER TABLE here; a brand-new database already has it from SCHEMA_SQL's CREATE TABLE above, so the ALTER TABLE would fail with "duplicate column name" there -- swallow exactly that error and rethrow anything else, same pattern as v8 -> v9 / v9 -> v10 / v12 -> v13 / v14 -> v15 above. No backfill, unlike v14 -> v15: until this version a row was only ever written when a hint was actually shown, so the column's own DEFAULT 1 is already the truth for every existing row, and an UPDATE would be a no-op dressed up as a migration step.
2291
+ 15: (conn) => alterTableIdempotent(conn, "ALTER TABLE hint_emissions ADD COLUMN displayed INTEGER NOT NULL DEFAULT 1")
2266
2292
  };
2267
2293
  function runMigrations(conn, fromVersion, toVersion) {
2268
2294
  for (let v = fromVersion; v < toVersion; v++) {
@@ -5,10 +5,10 @@ import {
5
5
  } from "./token-goat-chunk-3BTK54F3.mjs";
6
6
  import {
7
7
  filtersFilteredToEmptyNotice
8
- } from "./token-goat-chunk-UBZCON6N.mjs";
8
+ } from "./token-goat-chunk-ZGQXRYKE.mjs";
9
9
  import {
10
10
  displaySafeText
11
- } from "./token-goat-chunk-ERTXEKB6.mjs";
11
+ } from "./token-goat-chunk-NMTKNYGF.mjs";
12
12
  import {
13
13
  compileGuardedRegexCached
14
14
  } from "./token-goat-chunk-GMOUBOX4.mjs";
@@ -70,35 +70,36 @@ import {
70
70
  requirePositiveInt,
71
71
  run,
72
72
  visualStudioManualSteps
73
- } from "./token-goat-chunk-7VQMOBPQ.mjs";
74
- import "./token-goat-chunk-IDHWKNKZ.mjs";
75
- import "./token-goat-chunk-CICJFVIH.mjs";
76
- import "./token-goat-chunk-LMFO66YD.mjs";
73
+ } from "./token-goat-chunk-WKO5TVNA.mjs";
74
+ import "./token-goat-chunk-GYC34EGE.mjs";
75
+ import "./token-goat-chunk-PIAESKVU.mjs";
76
+ import "./token-goat-chunk-F24NE3FO.mjs";
77
+ import "./token-goat-chunk-DA43OE7L.mjs";
77
78
  import {
78
79
  cmdDescribe,
79
80
  cmdSessionSchema
80
- } from "./token-goat-chunk-HRUGS6CO.mjs";
81
- import "./token-goat-chunk-PKNOHVWP.mjs";
82
- import "./token-goat-chunk-C26ZBGBC.mjs";
83
- import "./token-goat-chunk-26YUO7MP.mjs";
81
+ } from "./token-goat-chunk-2GTB5RBZ.mjs";
82
+ import "./token-goat-chunk-FQYMZASI.mjs";
83
+ import "./token-goat-chunk-7DGTVBWP.mjs";
84
+ import "./token-goat-chunk-S7RFIX4Y.mjs";
84
85
  import "./token-goat-chunk-LKSXAMJB.mjs";
85
- import "./token-goat-chunk-CPHVXKNU.mjs";
86
- import "./token-goat-chunk-W2GUUQAQ.mjs";
87
- import "./token-goat-chunk-THBLW56G.mjs";
88
- import "./token-goat-chunk-KZPO4DWC.mjs";
86
+ import "./token-goat-chunk-DHIXE6GV.mjs";
87
+ import "./token-goat-chunk-3W5P673V.mjs";
88
+ import "./token-goat-chunk-FJZ5MZK7.mjs";
89
+ import "./token-goat-chunk-KWM6KBPA.mjs";
89
90
  import "./token-goat-chunk-3BTK54F3.mjs";
90
- import "./token-goat-chunk-66224KCZ.mjs";
91
+ import "./token-goat-chunk-BTKDZW7F.mjs";
91
92
  import "./token-goat-chunk-XTQAOTSO.mjs";
92
93
  import "./token-goat-chunk-AH6QILZM.mjs";
93
- import "./token-goat-chunk-SWRKVA3J.mjs";
94
- import "./token-goat-chunk-73GXYYZR.mjs";
95
- import "./token-goat-chunk-BK3RPPTK.mjs";
96
- import "./token-goat-chunk-MWSVUKVV.mjs";
97
- import "./token-goat-chunk-2VASS5VR.mjs";
94
+ import "./token-goat-chunk-NJKNAC37.mjs";
95
+ import "./token-goat-chunk-EP7TCWHM.mjs";
96
+ import "./token-goat-chunk-IBTFGZBX.mjs";
97
+ import "./token-goat-chunk-JNIFSVWM.mjs";
98
+ import "./token-goat-chunk-SOKAPOOC.mjs";
98
99
  import "./token-goat-chunk-EEIDFMEM.mjs";
99
100
  import "./token-goat-chunk-OSUFN2FV.mjs";
100
- import "./token-goat-chunk-UBZCON6N.mjs";
101
- import "./token-goat-chunk-ERTXEKB6.mjs";
101
+ import "./token-goat-chunk-ZGQXRYKE.mjs";
102
+ import "./token-goat-chunk-NMTKNYGF.mjs";
102
103
  import "./token-goat-chunk-Y4AFKTHK.mjs";
103
104
  import "./token-goat-chunk-GMOUBOX4.mjs";
104
105
  import "./token-goat-chunk-RRNZMM3A.mjs";
@@ -6,12 +6,12 @@ import {
6
6
  pptxOutline,
7
7
  pptxSlideText,
8
8
  pptxTextGrep
9
- } from "./token-goat-chunk-W2GUUQAQ.mjs";
10
- import "./token-goat-chunk-66224KCZ.mjs";
9
+ } from "./token-goat-chunk-3W5P673V.mjs";
10
+ import "./token-goat-chunk-BTKDZW7F.mjs";
11
11
  import "./token-goat-chunk-XTQAOTSO.mjs";
12
12
  import "./token-goat-chunk-AH6QILZM.mjs";
13
- import "./token-goat-chunk-UBZCON6N.mjs";
14
- import "./token-goat-chunk-ERTXEKB6.mjs";
13
+ import "./token-goat-chunk-ZGQXRYKE.mjs";
14
+ import "./token-goat-chunk-NMTKNYGF.mjs";
15
15
  import "./token-goat-chunk-Y4AFKTHK.mjs";
16
16
  import "./token-goat-chunk-GMOUBOX4.mjs";
17
17
  import "./token-goat-chunk-A37V4PBF.mjs";
@@ -0,0 +1,337 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ ToolFilter,
5
+ capTokens,
6
+ compressedTokensSaved,
7
+ deliverCompressed,
8
+ dispatchArgv,
9
+ filterByName,
10
+ selectFilter,
11
+ shlexSplit,
12
+ storeBashOutputSync
13
+ } from "./token-goat-chunk-7DGTVBWP.mjs";
14
+ import {
15
+ recordStat
16
+ } from "./token-goat-chunk-S7RFIX4Y.mjs";
17
+ import {
18
+ detectHarness,
19
+ loadConfig
20
+ } from "./token-goat-chunk-SOKAPOOC.mjs";
21
+ import {
22
+ init_define_import_meta_env
23
+ } from "./token-goat-chunk-A37V4PBF.mjs";
24
+
25
+ // src/bash_runner.ts
26
+ init_define_import_meta_env();
27
+ import { spawn, spawnSync } from "node:child_process";
28
+ import * as os from "node:os";
29
+
30
+ // src/delivery_cap.ts
31
+ init_define_import_meta_env();
32
+ var CLAUDE_CODE_BASH_OUTPUT_CAP_BYTES = 2e4;
33
+ var CLAUDE_CODE_PERSISTED_PREVIEW_BYTES = 2048;
34
+ function bashOutputCapBytes(harness = detectHarness()) {
35
+ return harness === "claudecode" ? CLAUDE_CODE_BASH_OUTPUT_CAP_BYTES : null;
36
+ }
37
+ function clipToDeliveryCap(text, reserveBytes) {
38
+ const cap = bashOutputCapBytes();
39
+ const room = cap === null ? null : cap - reserveBytes;
40
+ if (room === null || room <= 0 || Buffer.byteLength(text, "utf-8") <= room) return { text, clipped: false };
41
+ const sliced = Buffer.from(text, "utf-8").subarray(0, room).toString("utf-8");
42
+ const lastNewline = sliced.lastIndexOf("\n");
43
+ return { text: lastNewline > 0 ? sliced.slice(0, lastNewline) : sliced, clipped: true };
44
+ }
45
+ function deliveredOutputBytes(originalBytes, harness) {
46
+ const cap = bashOutputCapBytes(harness);
47
+ if (cap === null) return Math.max(0, originalBytes);
48
+ if (originalBytes <= cap) return Math.max(0, originalBytes);
49
+ return CLAUDE_CODE_PERSISTED_PREVIEW_BYTES;
50
+ }
51
+
52
+ // src/shell.ts
53
+ init_define_import_meta_env();
54
+ import * as fs from "node:fs";
55
+ import * as path from "node:path";
56
+ var WSL_LAUNCHER_SEGMENTS = /* @__PURE__ */ new Set(["system32", "syswow64", "windowsapps"]);
57
+ function isExecutable(p) {
58
+ try {
59
+ return fs.statSync(p).isFile();
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+ function isWslLauncherDir(dir) {
65
+ return dir.toLowerCase().split(/[\\/]+/).some((seg) => WSL_LAUNCHER_SEGMENTS.has(seg));
66
+ }
67
+ function locateBashOnPath(pathValue, isExe = isExecutable) {
68
+ for (const dir of (pathValue ?? "").split(path.delimiter)) {
69
+ if (!dir || isWslLauncherDir(dir)) continue;
70
+ const cand = path.join(dir, "bash.exe");
71
+ if (isExe(cand)) return cand;
72
+ }
73
+ return null;
74
+ }
75
+ function knownGitBashPaths() {
76
+ const localPrograms = process.env["LOCALAPPDATA"] ? path.join(process.env["LOCALAPPDATA"], "Programs") : void 0;
77
+ const bases = [
78
+ process.env["ProgramFiles"],
79
+ process.env["ProgramFiles(x86)"],
80
+ process.env["ProgramW6432"],
81
+ localPrograms
82
+ ];
83
+ const out = [];
84
+ for (const base of bases) {
85
+ if (!base) continue;
86
+ out.push(path.join(base, "Git", "bin", "bash.exe"));
87
+ out.push(path.join(base, "Git", "usr", "bin", "bash.exe"));
88
+ }
89
+ return out;
90
+ }
91
+ function resolveWindowsBash() {
92
+ if (process.platform !== "win32") return null;
93
+ const override = process.env["TOKEN_GOAT_BASH"];
94
+ if (override && isExecutable(override)) return override;
95
+ const onPath = locateBashOnPath(process.env["PATH"]);
96
+ if (onPath) return onPath;
97
+ for (const cand of knownGitBashPaths()) {
98
+ if (isExecutable(cand)) return cand;
99
+ }
100
+ return null;
101
+ }
102
+ function wrappedShell() {
103
+ if (process.platform !== "win32") return true;
104
+ return resolveWindowsBash() ?? true;
105
+ }
106
+ function canRunWrappedShell() {
107
+ return process.platform !== "win32" || resolveWindowsBash() !== null;
108
+ }
109
+
110
+ // src/bash_runner.ts
111
+ var IdentityFilter = class extends ToolFilter {
112
+ name = "passthrough";
113
+ };
114
+ var DEFAULT_TIMEOUT_SECONDS = 600;
115
+ var MAX_CAPTURE_BYTES = 32 * 1024 * 1024;
116
+ var MIN_RECORD_STAT_BYTES = 32;
117
+ var DEFAULT_HEARTBEAT_INTERVAL_MS = 15e3;
118
+ var CaptureSink = class {
119
+ constructor(cap) {
120
+ this.cap = cap;
121
+ }
122
+ cap;
123
+ chunks = [];
124
+ bytes = 0;
125
+ overflowed = false;
126
+ push(chunk) {
127
+ if (this.overflowed) return;
128
+ this.bytes += chunk.length;
129
+ if (this.bytes > this.cap) {
130
+ this.overflowed = true;
131
+ return;
132
+ }
133
+ this.chunks.push(chunk);
134
+ }
135
+ text() {
136
+ return decode(Buffer.concat(this.chunks));
137
+ }
138
+ };
139
+ function scheduleHeartbeat(startTime, intervalMs, write) {
140
+ let interval = intervalMs;
141
+ let timer;
142
+ const arm = () => {
143
+ timer = setTimeout(() => {
144
+ write(Math.round((Date.now() - startTime) / 1e3));
145
+ interval *= 2;
146
+ arm();
147
+ }, interval);
148
+ timer.unref();
149
+ };
150
+ arm();
151
+ return {
152
+ onOutput: () => {
153
+ interval = intervalMs;
154
+ if (timer !== void 0) clearTimeout(timer);
155
+ arm();
156
+ },
157
+ stop: () => {
158
+ if (timer !== void 0) clearTimeout(timer);
159
+ }
160
+ };
161
+ }
162
+ function resolveFilter(command, filterName, cwd) {
163
+ let split;
164
+ try {
165
+ split = shlexSplit(command);
166
+ } catch {
167
+ split = null;
168
+ }
169
+ const argv = split === null ? [command] : dispatchArgv(split, cwd).argv;
170
+ if (filterName) {
171
+ const named = filterByName(filterName);
172
+ if (named !== null) return { filter: named, argv };
173
+ }
174
+ if (split === null) return { filter: null, argv };
175
+ return { filter: selectFilter(split, cwd), argv };
176
+ }
177
+ function baseSpawnOptions(timeout, cwd) {
178
+ return { timeout: timeout * 1e3, cwd };
179
+ }
180
+ function spawnTarget(command, nativeShell) {
181
+ const shell = nativeShell ? true : wrappedShell();
182
+ if (typeof shell === "string") return { file: shell, args: ["-c", 'eval "$TG_CMD"'], shell: false, cmdEnv: { TG_CMD: command } };
183
+ return { file: command, args: [], shell };
184
+ }
185
+ function mergeSpawnEnv(env, cmdEnv) {
186
+ return cmdEnv ? { ...env ?? process.env, ...cmdEnv } : env;
187
+ }
188
+ function signalExitCode(signal) {
189
+ const num = os.constants.signals[signal];
190
+ return 128 + (num ?? 0);
191
+ }
192
+ function resolveProfile(explicit) {
193
+ if (explicit) return explicit;
194
+ try {
195
+ const p = loadConfig().compression.profile;
196
+ return p && p !== "auto" ? p : "balanced";
197
+ } catch {
198
+ return "balanced";
199
+ }
200
+ }
201
+ function resolveCompressLimits() {
202
+ try {
203
+ const bc = loadConfig().bash_compress;
204
+ return { maxLines: bc.max_lines, maxBytes: bc.max_bytes };
205
+ } catch {
206
+ return { maxLines: 1e3, maxBytes: 64 * 1024 };
207
+ }
208
+ }
209
+ async function run(command, opts = {}) {
210
+ const timeout = opts.timeout ?? DEFAULT_TIMEOUT_SECONDS;
211
+ const { filter, argv } = resolveFilter(command, opts.filterName, opts.cwd);
212
+ if (filter === null) {
213
+ if ((opts.maxTokens ?? 0) > 0 || opts.quietSuccess) {
214
+ return wrapAndCompress(command, argv, new IdentityFilter(), timeout, resolveProfile(opts.compressionProfile), opts);
215
+ }
216
+ return passthrough(command, timeout, opts.cwd, opts.env, opts.nativeShell);
217
+ }
218
+ return wrapAndCompress(command, argv, filter, timeout, resolveProfile(opts.compressionProfile), opts);
219
+ }
220
+ function runRaw(command, timeout = DEFAULT_TIMEOUT_SECONDS) {
221
+ return passthrough(command, timeout, void 0, void 0);
222
+ }
223
+ function passthrough(command, timeout, cwd, env, nativeShell) {
224
+ const { file, args, shell, cmdEnv } = spawnTarget(command, nativeShell);
225
+ const result = spawnSync(file, args, {
226
+ ...baseSpawnOptions(timeout, cwd),
227
+ shell,
228
+ env: mergeSpawnEnv(env, cmdEnv),
229
+ stdio: "inherit"
230
+ });
231
+ if (isTimeout(result.error)) return 124;
232
+ if (result.status !== null) return result.status;
233
+ if (result.signal) return signalExitCode(result.signal);
234
+ return 0;
235
+ }
236
+ function isTimeout(error) {
237
+ return !!error && error.code === "ETIMEDOUT";
238
+ }
239
+ function decode(buf) {
240
+ if (buf == null) return "";
241
+ return typeof buf === "string" ? buf : buf.toString("utf8");
242
+ }
243
+ async function wrapAndCompress(command, argv, filter, timeout, profile, opts) {
244
+ const writeStdout = opts.writeStdout ?? ((s) => process.stdout.write(s));
245
+ const writeStderr = opts.writeStderr ?? ((s) => process.stderr.write(s));
246
+ const startTime = Date.now();
247
+ const { file, args, shell, cmdEnv } = spawnTarget(command, opts.nativeShell);
248
+ const stdoutSink = new CaptureSink(MAX_CAPTURE_BYTES);
249
+ const stderrSink = new CaptureSink(MAX_CAPTURE_BYTES);
250
+ let timedOut = false;
251
+ let overflowed = false;
252
+ const child = spawn(file, args, { cwd: opts.cwd, shell, env: mergeSpawnEnv(opts.env, cmdEnv), stdio: ["ignore", "pipe", "pipe"] });
253
+ const heartbeat = scheduleHeartbeat(startTime, opts.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS, (elapsed) => {
254
+ writeStderr(`[token-goat compress] still running, ${elapsed}s elapsed
255
+ `);
256
+ });
257
+ const timeoutHandle = setTimeout(() => {
258
+ timedOut = true;
259
+ child.kill();
260
+ }, timeout * 1e3);
261
+ const onData = (sink) => (chunk) => {
262
+ heartbeat.onOutput();
263
+ sink.push(chunk);
264
+ if (sink.overflowed) {
265
+ overflowed = true;
266
+ child.kill();
267
+ }
268
+ };
269
+ child.stdout?.on("data", onData(stdoutSink));
270
+ child.stderr?.on("data", onData(stderrSink));
271
+ const { code, signal } = await new Promise((resolve) => {
272
+ child.on("close", (code2, signal2) => resolve({ code: code2, signal: signal2 }));
273
+ });
274
+ heartbeat.stop();
275
+ clearTimeout(timeoutHandle);
276
+ let stdoutText = stdoutSink.text();
277
+ let stderrText = stderrSink.text();
278
+ if (overflowed) {
279
+ stdoutText += `
280
+ [token-goat: capture capped at ${MAX_CAPTURE_BYTES / (1024 * 1024)} MiB]`;
281
+ }
282
+ let exitCode;
283
+ if (timedOut) {
284
+ exitCode = 124;
285
+ stderrText = (stderrText ? stderrText + "\n" : "") + `[token-goat: command exceeded ${timeout}s timeout and was killed]`;
286
+ } else if (code !== null) {
287
+ exitCode = code;
288
+ } else if (signal) {
289
+ exitCode = signalExitCode(signal);
290
+ } else {
291
+ exitCode = 0;
292
+ }
293
+ if (opts.quietSuccess && exitCode === 0) {
294
+ const duration = ((Date.now() - startTime) / 1e3).toFixed(1) + "s";
295
+ const fullRaw = (stdoutText + (stderrText ? "\n" + stderrText : "")).trim();
296
+ const id = storeBashOutputSync(command, fullRaw, 0, opts.cwd ?? null);
297
+ const summary = `[tg: ok] ${command} (${duration}, recall: token-goat bash-output ${id})
298
+ `;
299
+ writeStdout(summary);
300
+ return 0;
301
+ }
302
+ const limits = resolveCompressLimits();
303
+ const { applied, marker, compressed, text: delivered } = deliverCompressed(filter, stdoutText, stderrText, exitCode, argv, {
304
+ compressionProfile: profile,
305
+ maxLines: limits.maxLines,
306
+ maxBytes: limits.maxBytes
307
+ });
308
+ let text = delivered;
309
+ const maxTokens = opts.maxTokens ?? 0;
310
+ if (maxTokens > 0) text = capTokens(text, maxTokens);
311
+ let body = text + marker;
312
+ if (applied) {
313
+ const fullRaw = (stdoutText + (stderrText ? "\n" + stderrText : "")).trim();
314
+ const recallId = storeBashOutputSync(command, fullRaw, exitCode, opts.cwd ?? null);
315
+ body += "\n[token-goat] full output: bash-output " + recallId + " --full";
316
+ }
317
+ writeStdout(body.endsWith("\n") ? body : body + "\n");
318
+ if (applied) recordSavings(compressed);
319
+ return exitCode;
320
+ }
321
+ function recordSavings(result) {
322
+ const delivered = deliveredOutputBytes(result.originalBytes);
323
+ const bytesSaved = Math.max(0, delivered - result.compressedBytes);
324
+ if (bytesSaved < MIN_RECORD_STAT_BYTES) return;
325
+ recordStat(`bash_compress:${result.filterName}`, bytesSaved, compressedTokensSaved(bytesSaved));
326
+ }
327
+
328
+ export {
329
+ clipToDeliveryCap,
330
+ deliveredOutputBytes,
331
+ canRunWrappedShell,
332
+ DEFAULT_TIMEOUT_SECONDS,
333
+ MAX_CAPTURE_BYTES,
334
+ DEFAULT_HEARTBEAT_INTERVAL_MS,
335
+ run,
336
+ runRaw
337
+ };
@@ -10,7 +10,7 @@ import {
10
10
  stripMultilineStringSpan,
11
11
  stripNestedBlockCommentSpan,
12
12
  stripStringLiterals
13
- } from "./token-goat-chunk-MWSVUKVV.mjs";
13
+ } from "./token-goat-chunk-JNIFSVWM.mjs";
14
14
  import {
15
15
  init_define_import_meta_env
16
16
  } from "./token-goat-chunk-A37V4PBF.mjs";
@@ -133,6 +133,7 @@ function shellMountToWindowsPath(p) {
133
133
  }
134
134
  return s;
135
135
  }
136
+ var TOOL_RESULTS_ID_CHARS = "[a-z0-9_-]+";
136
137
  function normalizePath(p) {
137
138
  let s = p;
138
139
  if (EXTENDED_UNC_PREFIX_RE.test(s)) {
@@ -217,6 +218,7 @@ export {
217
218
  MSYS_PATH_RE,
218
219
  expandShortPath,
219
220
  lowercaseDriveLetter,
221
+ TOOL_RESULTS_ID_CHARS,
220
222
  normalizePath,
221
223
  normalizeDarwinSystemAlias,
222
224
  resolveIndexPath,