token-goat 2.9.19 → 2.9.21

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 (50) hide show
  1. package/README.md +3 -1
  2. package/dist/{token-goat-chunk-JNIFSVWM.mjs → token-goat-chunk-2ZHD3XCD.mjs} +5 -1
  3. package/dist/{token-goat-chunk-P46RTOAQ.mjs → token-goat-chunk-4IIU3GNS.mjs} +10 -7
  4. package/dist/{token-goat-chunk-2GTB5RBZ.mjs → token-goat-chunk-4V4OZHHV.mjs} +11 -271
  5. package/dist/token-goat-chunk-64OEF3UN.mjs +128 -0
  6. package/dist/{token-goat-chunk-AVRD7SBO.mjs → token-goat-chunk-6AQLZMCO.mjs} +270 -29
  7. package/dist/{token-goat-chunk-LACH3WKS.mjs → token-goat-chunk-BIEDTF6V.mjs} +3 -3
  8. package/dist/{token-goat-chunk-PIAESKVU.mjs → token-goat-chunk-BKHJRFDE.mjs} +568 -429
  9. package/dist/token-goat-chunk-CAHN4QU7.mjs +26 -0
  10. package/dist/{token-goat-chunk-MU6I4IDI.mjs → token-goat-chunk-CMAJGP3E.mjs} +11 -7
  11. package/dist/token-goat-chunk-CMHBTXMY.mjs +41 -0
  12. package/dist/{token-goat-chunk-HPMMLV37.mjs → token-goat-chunk-CZ5ZFJSN.mjs} +256 -341
  13. package/dist/{token-goat-chunk-7DGTVBWP.mjs → token-goat-chunk-DDP64XR7.mjs} +159 -1863
  14. package/dist/{token-goat-chunk-FX5DHKGF.mjs → token-goat-chunk-DISVHOW6.mjs} +4 -4
  15. package/dist/{token-goat-chunk-L2UOJBV4.mjs → token-goat-chunk-FZN77SZD.mjs} +27 -19
  16. package/dist/{token-goat-chunk-DA6TIDLI.mjs → token-goat-chunk-GO7OWONJ.mjs} +18 -13
  17. package/dist/{token-goat-chunk-EP7TCWHM.mjs → token-goat-chunk-IAMIQLCV.mjs} +83 -66
  18. package/dist/{token-goat-chunk-NJKNAC37.mjs → token-goat-chunk-IJUHQX2M.mjs} +1 -1
  19. package/dist/token-goat-chunk-KQI24O4B.mjs +292 -0
  20. package/dist/{token-goat-chunk-GYC34EGE.mjs → token-goat-chunk-KRPJIXCT.mjs} +2 -2
  21. package/dist/token-goat-chunk-KVAXTA2W.mjs +198 -0
  22. package/dist/{token-goat-chunk-FJZ5MZK7.mjs → token-goat-chunk-LK3YOKDF.mjs} +2 -2
  23. package/dist/token-goat-chunk-M43UJVYQ.mjs +1816 -0
  24. package/dist/{token-goat-chunk-SOKAPOOC.mjs → token-goat-chunk-M6CDVASA.mjs} +46 -47
  25. package/dist/token-goat-chunk-MECDAPXB.mjs +1989 -0
  26. package/dist/token-goat-chunk-MEKVJZUN.mjs +324 -0
  27. package/dist/token-goat-chunk-MQACN7VG.mjs +1379 -0
  28. package/dist/{token-goat-chunk-3W5P673V.mjs → token-goat-chunk-MZQORUX5.mjs} +1 -1
  29. package/dist/{token-goat-chunk-DHIXE6GV.mjs → token-goat-chunk-ORRDWKLA.mjs} +1 -1
  30. package/dist/{token-goat-chunk-F24NE3FO.mjs → token-goat-chunk-PCR43MCM.mjs} +2 -2
  31. package/dist/token-goat-chunk-PDYQA3AP.mjs +248 -0
  32. package/dist/{token-goat-chunk-S7RFIX4Y.mjs → token-goat-chunk-PHDNWISV.mjs} +45 -14
  33. package/dist/token-goat-chunk-PPYZS7TI.mjs +304 -0
  34. package/dist/{token-goat-chunk-BTKDZW7F.mjs → token-goat-chunk-QTIRVK3M.mjs} +1 -1
  35. package/dist/{token-goat-chunk-62VJ47WN.mjs → token-goat-chunk-QUM3UNWS.mjs} +5 -5
  36. package/dist/{token-goat-chunk-KWM6KBPA.mjs → token-goat-chunk-SHV6QQAS.mjs} +1 -1
  37. package/dist/{token-goat-chunk-FQYMZASI.mjs → token-goat-chunk-SMUYPBF5.mjs} +796 -4464
  38. package/dist/{token-goat-chunk-ZGQXRYKE.mjs → token-goat-chunk-TGBSMHPM.mjs} +1 -1
  39. package/dist/{token-goat-chunk-EXLLLIC3.mjs → token-goat-chunk-VXKT3W4C.mjs} +3 -3
  40. package/dist/{token-goat-chunk-WKO5TVNA.mjs → token-goat-chunk-XJUPKJF5.mjs} +544 -576
  41. package/dist/{token-goat-chunk-IBTFGZBX.mjs → token-goat-chunk-XQSSPX3X.mjs} +2 -2
  42. package/dist/{token-goat-chunk-VJQBYSXG.mjs → token-goat-chunk-ZJWPJL7O.mjs} +3 -3
  43. package/dist/token-goat-hook.mjs +17 -11
  44. package/dist/token-goat.core.mjs +27 -19
  45. package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
  46. package/docs/cli.md +13 -12
  47. package/docs/install.md +1 -1
  48. package/docs/security.md +2 -2
  49. package/package.json +3 -2
  50. package/dist/token-goat-chunk-NYFYIRG3.mjs +0 -35
package/README.md CHANGED
@@ -21,6 +21,8 @@ permalink: /
21
21
 
22
22
  Token-Goat sits silently between your AI and your tools. Re-read a file? It gets a one-line hint and a narrow-slice suggestion instead of the full file again. Grab a screenshot? A 100 KB copy reaches the model instead of 10 MB. Run `pytest`, `npm install`, `docker build`, or `cargo`? The thousands of progress bars and passing-test names are stripped to the failures before the output even reaches the context window. Open a PDF, a large Markdown doc, or a CSV? The hook intercepts it — heading tree, page count, or column preview — so the model never pays for the full file. Run `gh run watch` or `next dev` a second time? Prior output is recalled rather than re-run. Compact a long session? It gets a clean structured manifest of edited files and key symbols so nothing important is forgotten. Sessions drop 40–90%+ in cost. You change nothing about how you work.
23
23
 
24
+ The symbol index also understands native Nginx, Caddyfile, and Apache HTTP Server configuration blocks, so `token-goat read "file::symbol"` can jump directly to them.
25
+
24
26
  Works with **Claude Code**, **Gemini CLI**, **Qwen Code**, **Codex CLI**, **Aider**, **Cursor**, **Cline**, **Windsurf**, **Copilot CLI**, **VS Code** (Copilot agent), **Visual Studio** (Copilot agent, MCP tools and instructions only), **Zed** (MCP tools only), **Kimi Code**, **Grok CLI** (xAI Grok Build), and OpenCode, plus **pi** ([pi-coding-agent](https://github.com/earendil-works/pi-mono)).
25
27
 
26
28
  **Ask your AI to install it fully (give it this GitHub link), or install in one command:**
@@ -213,7 +215,7 @@ Numbers below come from synthetic-fixture benchmarks in the test suite, except t
213
215
  | Symbol start_line | TypeScript decorators captured in symbol span | One `token-goat read` returns the decorator + signature + body; no re-read | `src/parser.ts` (TypeScript adapter) |
214
216
  | Section extraction | Setext headings, h5/h6, anchor IDs, shell comment banners (`##`, `# ---`, `# ===`, `# [...]`, `# REGION:`), and `__frontmatter__` | `token-goat section` resolves more headings and procedural shell checks without falling back to a full file read | `src/parser.ts` (Markdown & Shell adapters) |
215
217
  | Image cache | Repeat Read of an unchanged image serves the stored re-encode, keyed on path + size + mtime, instead of re-encoding it again | Skips the re-encode entirely on a hit; the same bytes reach the model, so the reported saving is identical either way | `src/image_shrink.ts` (`findCachedShrink`) |
216
- | Monorepo defaults | Reindex batch 500 → 2000; compact `min_events` 5 → 3 | Fewer worker wakeups; compact manifests fire on shorter sessions | `src/config.ts` defaults |
218
+ | Monorepo defaults | Reindex batch 500 → 2000 | Fewer worker wakeups on a large tree | `src/config.ts` defaults |
217
219
  | Miss suggestions | `read` / `section` print "Did you mean…?" on a miss; `section` also auto-redirects on an unambiguous heading-prefix match | Keeps agents on the surgical-read path instead of falling back to full-file `Read` | `src/read_commands.ts` |
218
220
  | Atlassian MCP compression | Strips UI chrome, avatars, operations, and schema links from Jira and Confluence tool responses | 60–80% smaller before generic table compression | `src/mcp_atlassian_compress.ts` |
219
221
  | MCP tool spill slicing | Narrow-slice queries on cached MCP tool outputs or dumped JSON files with `mcp-output --json-query` | 90–98% smaller than whole-file JSON re-reads | `src/mcp_json_query.ts` |
@@ -15,7 +15,7 @@ import {
15
15
  foldCase,
16
16
  foldPath,
17
17
  sleepSync
18
- } from "./token-goat-chunk-ZGQXRYKE.mjs";
18
+ } from "./token-goat-chunk-TGBSMHPM.mjs";
19
19
  import {
20
20
  safeJoin
21
21
  } from "./token-goat-chunk-NMTKNYGF.mjs";
@@ -1666,6 +1666,10 @@ var LANGUAGE_SPECS = [
1666
1666
  // Oracle PL/SQL sources: package spec and body, standalone procedure/function, trigger, and object type spec and body.
1667
1667
  { id: "sql", extraction: "regex", extensions: [".sql", ".pks", ".pkb", ".pls", ".plsql", ".pck", ".prc", ".fnc", ".trg", ".tps", ".tpb"], label: "SQL", ...DATA, symbolBearing: true, diffable: true, fence: "sql" },
1668
1668
  { id: "ini", extraction: "regex", extensions: [".ini", ".cfg", ".conf"], label: "INI", ...DATA, fence: "ini" },
1669
+ { id: "nginx", extraction: "regex", extensions: [".nginx"], basenames: ["nginx.conf"], label: "Nginx", ...DATA, symbolBearing: true, diffable: true, fence: "nginx" },
1670
+ // `basenames` is matched against a lower-cased basename, so `Caddyfile` is spelled in lower case here; the display spelling is the label's job.
1671
+ { id: "caddy", extraction: "regex", extensions: [".caddy"], basenames: ["caddyfile", "caddy.conf"], label: "Caddyfile", ...DATA, symbolBearing: true, diffable: true, fence: "caddy" },
1672
+ { id: "apache", extraction: "regex", extensions: [".apache", ".apache2"], basenames: ["httpd.conf", "apache2.conf", ".htaccess"], label: "Apache Config", ...DATA, symbolBearing: true, diffable: true, fence: "apache" },
1669
1673
  // `.mk` fragments (config.mk, rules.mk) share a bare Makefile's syntax.
1670
1674
  { id: "makefile", extraction: "regex", extensions: [".mk"], basenames: ["makefile", "gnumakefile", "bsdmakefile"], ...DATA, fence: "makefile", basenameImportsExtension: ".mk" },
1671
1675
  { id: "proto", extraction: "regex", extensions: [".proto"], label: "Protocol Buffers", ...DATA, symbolBearing: true, fence: "protobuf" },
@@ -4,17 +4,19 @@ import {
4
4
  DEFAULT_HEARTBEAT_INTERVAL_MS,
5
5
  DEFAULT_TIMEOUT_SECONDS,
6
6
  MAX_CAPTURE_BYTES,
7
+ resolveFilter,
7
8
  run,
8
9
  runRaw
9
- } from "./token-goat-chunk-MU6I4IDI.mjs";
10
- import "./token-goat-chunk-7DGTVBWP.mjs";
11
- import "./token-goat-chunk-S7RFIX4Y.mjs";
12
- import "./token-goat-chunk-IBTFGZBX.mjs";
13
- import "./token-goat-chunk-JNIFSVWM.mjs";
14
- import "./token-goat-chunk-SOKAPOOC.mjs";
10
+ } from "./token-goat-chunk-CMAJGP3E.mjs";
11
+ import "./token-goat-chunk-DDP64XR7.mjs";
12
+ import "./token-goat-chunk-M43UJVYQ.mjs";
13
+ import "./token-goat-chunk-PHDNWISV.mjs";
14
+ import "./token-goat-chunk-XQSSPX3X.mjs";
15
+ import "./token-goat-chunk-2ZHD3XCD.mjs";
16
+ import "./token-goat-chunk-M6CDVASA.mjs";
15
17
  import "./token-goat-chunk-EEIDFMEM.mjs";
16
18
  import "./token-goat-chunk-OSUFN2FV.mjs";
17
- import "./token-goat-chunk-ZGQXRYKE.mjs";
19
+ import "./token-goat-chunk-TGBSMHPM.mjs";
18
20
  import "./token-goat-chunk-NMTKNYGF.mjs";
19
21
  import "./token-goat-chunk-GMOUBOX4.mjs";
20
22
  import "./token-goat-chunk-A37V4PBF.mjs";
@@ -22,6 +24,7 @@ export {
22
24
  DEFAULT_HEARTBEAT_INTERVAL_MS,
23
25
  DEFAULT_TIMEOUT_SECONDS,
24
26
  MAX_CAPTURE_BYTES,
27
+ resolveFilter,
25
28
  run,
26
29
  runRaw
27
30
  };
@@ -1,47 +1,39 @@
1
1
  import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
- accumulateResidentLine,
5
- claudeConfigDir,
6
- createResidentContextStats,
7
4
  enqueueDirtyPathsSafe,
8
- estimateTokens,
9
5
  fenceUntrusted,
10
6
  getToolName,
11
7
  getTrackedFiles,
12
- scanAndRecord,
13
- summarizeResidentContext
14
- } from "./token-goat-chunk-FQYMZASI.mjs";
8
+ scanAndRecord
9
+ } from "./token-goat-chunk-SMUYPBF5.mjs";
15
10
  import {
16
11
  RECALL_DEFAULT_LIMIT,
17
- commandHash,
18
- getBashOutput,
19
12
  indexRecallEntry,
20
13
  listRecentRecall,
21
- normalizeCommandForCacheKey,
22
14
  searchRecall
23
- } from "./token-goat-chunk-7DGTVBWP.mjs";
15
+ } from "./token-goat-chunk-M43UJVYQ.mjs";
24
16
  import {
25
17
  isBlobStale,
26
18
  loadBlob,
27
19
  redactSecrets,
28
20
  storeBlob
29
- } from "./token-goat-chunk-S7RFIX4Y.mjs";
21
+ } from "./token-goat-chunk-PHDNWISV.mjs";
30
22
  import {
31
23
  parserFingerprintForLanguage
32
- } from "./token-goat-chunk-EP7TCWHM.mjs";
24
+ } from "./token-goat-chunk-IAMIQLCV.mjs";
33
25
  import {
34
26
  getProjectFileEntries,
35
27
  projectScopeClause
36
- } from "./token-goat-chunk-IBTFGZBX.mjs";
28
+ } from "./token-goat-chunk-XQSSPX3X.mjs";
37
29
  import {
38
30
  getDb
39
- } from "./token-goat-chunk-JNIFSVWM.mjs";
31
+ } from "./token-goat-chunk-2ZHD3XCD.mjs";
40
32
  import {
41
33
  fingerprintFile,
42
34
  getDisplayRoot,
43
35
  shortFingerprint
44
- } from "./token-goat-chunk-SOKAPOOC.mjs";
36
+ } from "./token-goat-chunk-M6CDVASA.mjs";
45
37
  import {
46
38
  registerReset
47
39
  } from "./token-goat-chunk-EEIDFMEM.mjs";
@@ -52,7 +44,7 @@ import {
52
44
  foldPath,
53
45
  globalDbPath,
54
46
  pad
55
- } from "./token-goat-chunk-ZGQXRYKE.mjs";
47
+ } from "./token-goat-chunk-TGBSMHPM.mjs";
56
48
  import {
57
49
  UNTRUSTED_TOOL_TAG,
58
50
  UNTRUSTED_WEB_TAG,
@@ -132,7 +124,7 @@ var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
132
124
  var MAX_STDIN_WALL_MS = 6e4;
133
125
  var MAX_STDIN_BYTES = 64 * 1024 * 1024;
134
126
  function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDIN_BYTES) {
135
- return new Promise((resolve3, reject) => {
127
+ return new Promise((resolve2, reject) => {
136
128
  const chunks = [];
137
129
  let totalBytes = 0;
138
130
  let settled = false;
@@ -178,7 +170,7 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
178
170
  return;
179
171
  }
180
172
  try {
181
- resolve3(JSON.parse(text));
173
+ resolve2(JSON.parse(text));
182
174
  } catch (err) {
183
175
  reject(err instanceof Error ? err : new Error(String(err)));
184
176
  }
@@ -972,253 +964,6 @@ function reconcileProject(opts = {}) {
972
964
  };
973
965
  }
974
966
 
975
- // src/waste.ts
976
- init_define_import_meta_env();
977
- import * as fs3 from "node:fs";
978
- import * as path from "node:path";
979
- function projectTranscriptsDir(projectRoot) {
980
- const rootStr = path.resolve(projectRoot);
981
- const slug = rootStr.replace(/[^A-Za-z0-9]/g, "-");
982
- return path.join(claudeConfigDir(), "projects", slug);
983
- }
984
- function findLatestTranscript(projectRoot) {
985
- const dir = projectTranscriptsDir(projectRoot);
986
- let entries;
987
- try {
988
- entries = fs3.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
989
- } catch {
990
- return null;
991
- }
992
- let best = null;
993
- for (const name of entries) {
994
- const full = path.join(dir, name);
995
- let stat;
996
- try {
997
- stat = fs3.statSync(full);
998
- } catch {
999
- continue;
1000
- }
1001
- if (best === null || stat.mtimeMs > best.mtimeMs) best = { file: full, mtimeMs: stat.mtimeMs };
1002
- }
1003
- return best?.file ?? null;
1004
- }
1005
- function safeStringify(value) {
1006
- try {
1007
- return JSON.stringify(value) ?? "";
1008
- } catch {
1009
- return "";
1010
- }
1011
- }
1012
- var FILE_PATH_TOOLS = /* @__PURE__ */ new Set(["Read", "Edit", "Write", "NotebookEdit", "MultiEdit"]);
1013
- function extractFilePath(name, input) {
1014
- if (!FILE_PATH_TOOLS.has(name)) return null;
1015
- if (input === null || typeof input !== "object") return null;
1016
- const o = input;
1017
- const fp = o["file_path"];
1018
- if (typeof fp === "string") return fp;
1019
- const notebookPath = o["notebook_path"];
1020
- return typeof notebookPath === "string" ? notebookPath : null;
1021
- }
1022
- function extractCommand(name, input) {
1023
- if (name !== "Bash") return null;
1024
- if (input === null || typeof input !== "object") return null;
1025
- const cmd = input["command"];
1026
- return typeof cmd === "string" ? cmd : null;
1027
- }
1028
- function summarizeInput(name, input, filePath, command) {
1029
- if (filePath !== null) return filePath;
1030
- if (command !== null) return command.length > 100 ? `${command.slice(0, 100)}\u2026` : command;
1031
- if (input === null || typeof input !== "object") return "";
1032
- const o = input;
1033
- for (const key of ["pattern", "query", "path", "description", "prompt"]) {
1034
- const v = o[key];
1035
- if (typeof v === "string") return v.length > 100 ? `${v.slice(0, 100)}\u2026` : v;
1036
- }
1037
- const s = safeStringify(input);
1038
- return s.length > 100 ? `${s.slice(0, 100)}\u2026` : s;
1039
- }
1040
- function extractResultText(content) {
1041
- if (typeof content === "string") return content;
1042
- if (Array.isArray(content)) {
1043
- return content.map((b) => {
1044
- if (b !== null && typeof b === "object" && b["type"] === "text") {
1045
- const t = b["text"];
1046
- return typeof t === "string" ? t : "";
1047
- }
1048
- return "";
1049
- }).join("\n");
1050
- }
1051
- return "";
1052
- }
1053
- function parseTranscript(transcriptPath) {
1054
- const raw = fs3.readFileSync(transcriptPath, "utf-8");
1055
- const calls = [];
1056
- const resultTextById = /* @__PURE__ */ new Map();
1057
- const assistantTurns = [];
1058
- const resident = createResidentContextStats();
1059
- let seq = 0;
1060
- for (const line of raw.split("\n")) {
1061
- const trimmed = line.trim();
1062
- if (trimmed === "") continue;
1063
- let obj;
1064
- try {
1065
- obj = JSON.parse(trimmed);
1066
- } catch {
1067
- continue;
1068
- }
1069
- if (obj === null || typeof obj !== "object") continue;
1070
- const o = obj;
1071
- accumulateResidentLine(resident, o, trimmed.length);
1072
- const message = o["message"];
1073
- if (message === null || typeof message !== "object") continue;
1074
- const messageObj = message;
1075
- const content = messageObj["content"];
1076
- if (!Array.isArray(content)) continue;
1077
- const role = typeof messageObj["role"] === "string" ? messageObj["role"] : null;
1078
- const cwd = typeof o["cwd"] === "string" ? o["cwd"] : null;
1079
- let turnTextTokens = 0;
1080
- let sawTextBlock = false;
1081
- for (const block of content) {
1082
- if (block === null || typeof block !== "object") continue;
1083
- const b = block;
1084
- if (b["type"] === "tool_use" && typeof b["id"] === "string" && typeof b["name"] === "string") {
1085
- const name = b["name"];
1086
- const input = b["input"];
1087
- const filePath = extractFilePath(name, input);
1088
- const command = extractCommand(name, input);
1089
- calls.push({
1090
- seq: seq++,
1091
- id: b["id"],
1092
- name,
1093
- input,
1094
- filePath,
1095
- command,
1096
- summary: summarizeInput(name, input, filePath, command),
1097
- cwd
1098
- });
1099
- } else if (b["type"] === "tool_result" && typeof b["tool_use_id"] === "string") {
1100
- const id = b["tool_use_id"];
1101
- if (!resultTextById.has(id)) {
1102
- resultTextById.set(id, extractResultText(b["content"]));
1103
- }
1104
- } else if (role === "assistant" && b["type"] === "text" && typeof b["text"] === "string") {
1105
- sawTextBlock = true;
1106
- turnTextTokens += estimateTokens(b["text"]);
1107
- }
1108
- }
1109
- if (role === "assistant" && sawTextBlock) assistantTurns.push(turnTextTokens);
1110
- }
1111
- return { calls, resultTextById, assistantTurns, resident };
1112
- }
1113
- function costPerCall(parsed) {
1114
- return parsed.calls.map((c) => ({
1115
- ...c,
1116
- tokens: estimateTokens(parsed.resultTextById.get(c.id) ?? "")
1117
- }));
1118
- }
1119
- function aggregateSortedDesc(pairs) {
1120
- return [...pairs].map(([key, tokens]) => ({ key, tokens })).sort((a, b) => b.tokens - a.tokens);
1121
- }
1122
- function tokensByTool(costs) {
1123
- const m = /* @__PURE__ */ new Map();
1124
- for (const c of costs) m.set(c.name, (m.get(c.name) ?? 0) + c.tokens);
1125
- return aggregateSortedDesc(m);
1126
- }
1127
- function tokensByFile(costs) {
1128
- const m = /* @__PURE__ */ new Map();
1129
- for (const c of costs) {
1130
- if (c.filePath === null) continue;
1131
- m.set(c.filePath, (m.get(c.filePath) ?? 0) + c.tokens);
1132
- }
1133
- return aggregateSortedDesc(m);
1134
- }
1135
- function topExpensiveCalls(costs, n) {
1136
- return [...costs].sort((a, b) => b.tokens - a.tokens).slice(0, n);
1137
- }
1138
- function neverTouchedAgain(costs) {
1139
- const readsByFile = /* @__PURE__ */ new Map();
1140
- for (const c of costs) {
1141
- if (c.name !== "Read" || c.filePath === null) continue;
1142
- const arr = readsByFile.get(c.filePath) ?? [];
1143
- arr.push(c);
1144
- readsByFile.set(c.filePath, arr);
1145
- }
1146
- const out = [];
1147
- for (const [filePath, reads] of readsByFile) {
1148
- const firstSeq = Math.min(...reads.map((r) => r.seq));
1149
- const touchedLater = costs.some((c) => {
1150
- if (c.seq <= firstSeq) return false;
1151
- if (c.filePath === filePath) return true;
1152
- return safeStringify(c.input).includes(filePath);
1153
- });
1154
- if (!touchedLater) {
1155
- out.push({ filePath, tokens: reads.reduce((n, r) => n + r.tokens, 0) });
1156
- }
1157
- }
1158
- return out.sort((a, b) => b.tokens - a.tokens);
1159
- }
1160
- async function repeatedUncompressedBashCommands(costs) {
1161
- const groups = /* @__PURE__ */ new Map();
1162
- for (const c of costs) {
1163
- if (c.name !== "Bash" || c.command === null) continue;
1164
- const norm = normalizeCommandForCacheKey(c.command);
1165
- const arr = groups.get(norm) ?? [];
1166
- arr.push(c);
1167
- groups.set(norm, arr);
1168
- }
1169
- const out = [];
1170
- for (const [norm, calls] of groups) {
1171
- if (calls.length < 2) continue;
1172
- let cached = false;
1173
- for (const c of calls) {
1174
- try {
1175
- const hash = await commandHash(c.command, c.cwd);
1176
- if (getBashOutput(hash) !== null) {
1177
- cached = true;
1178
- break;
1179
- }
1180
- } catch {
1181
- }
1182
- }
1183
- if (cached) continue;
1184
- const totalTokens = calls.reduce((n, c) => n + c.tokens, 0);
1185
- out.push({
1186
- normalized: norm,
1187
- count: calls.length,
1188
- totalTokens,
1189
- avgTokens: Math.round(totalTokens / calls.length)
1190
- });
1191
- }
1192
- return out.sort((a, b) => b.totalTokens - a.totalTokens);
1193
- }
1194
- function assistantOutputCost(assistantTurns) {
1195
- const turnCount = assistantTurns.length;
1196
- let generatedTokens = 0;
1197
- let resendCeilingTokens = 0;
1198
- for (let i = 0; i < turnCount; i++) {
1199
- const tokens = assistantTurns[i] ?? 0;
1200
- generatedTokens += tokens;
1201
- resendCeilingTokens += tokens * (turnCount - 1 - i);
1202
- }
1203
- return { turnCount, generatedTokens, resendCeilingTokens };
1204
- }
1205
- async function buildWasteReport(transcriptPath, opts = {}) {
1206
- const parsed = parseTranscript(transcriptPath);
1207
- const costs = costPerCall(parsed);
1208
- const topN = opts.topN ?? 10;
1209
- return {
1210
- transcriptPath,
1211
- totalTokens: costs.reduce((n, c) => n + c.tokens, 0),
1212
- tokensByTool: tokensByTool(costs),
1213
- tokensByFile: tokensByFile(costs),
1214
- topCalls: topExpensiveCalls(costs, topN).map((c) => ({ seq: c.seq, name: c.name, summary: c.summary, tokens: c.tokens })),
1215
- neverTouchedAgain: neverTouchedAgain(costs),
1216
- repeatedUncompressedBash: await repeatedUncompressedBashCommands(costs),
1217
- assistantOutput: assistantOutputCost(parsed.assistantTurns),
1218
- residentContext: summarizeResidentContext(parsed.resident)
1219
- };
1220
- }
1221
-
1222
967
  // src/cli_recall.ts
1223
968
  init_define_import_meta_env();
1224
969
  var RECALL_COMMAND = {
@@ -1294,11 +1039,6 @@ export {
1294
1039
  runReconcile,
1295
1040
  isReconcileClean,
1296
1041
  reconcileProject,
1297
- projectTranscriptsDir,
1298
- findLatestTranscript,
1299
- safeStringify,
1300
- extractResultText,
1301
- buildWasteReport,
1302
1042
  RECALL_COMMAND,
1303
1043
  runRecallCommand,
1304
1044
  MAX_STDIN_BYTES,
@@ -0,0 +1,128 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ renderWebFetchRow
5
+ } from "./token-goat-chunk-PDYQA3AP.mjs";
6
+ import {
7
+ SESSIONS_SUBDIR,
8
+ extractChecklistSection,
9
+ getSkillFilePath,
10
+ listSkills
11
+ } from "./token-goat-chunk-MECDAPXB.mjs";
12
+ import {
13
+ getBashOutput
14
+ } from "./token-goat-chunk-M43UJVYQ.mjs";
15
+ import {
16
+ loadBlob
17
+ } from "./token-goat-chunk-PHDNWISV.mjs";
18
+ import {
19
+ resolveProjectRoot
20
+ } from "./token-goat-chunk-M6CDVASA.mjs";
21
+ import {
22
+ runGit,
23
+ safeSlice
24
+ } from "./token-goat-chunk-TGBSMHPM.mjs";
25
+ import {
26
+ displaySafePath,
27
+ displaySafeText,
28
+ neutralizeSpokenMarkers
29
+ } from "./token-goat-chunk-NMTKNYGF.mjs";
30
+ import {
31
+ init_define_import_meta_env
32
+ } from "./token-goat-chunk-A37V4PBF.mjs";
33
+
34
+ // src/resume.ts
35
+ init_define_import_meta_env();
36
+ import { readFile } from "node:fs/promises";
37
+ var MAX_RESUME_TOKENS = 2e3;
38
+ var MAX_RESUME_CHARS = MAX_RESUME_TOKENS * 4;
39
+ var MAX_RESUME_WEB_FETCHES = 8;
40
+ var SKILL_MAX_COUNT = 3;
41
+ var SKILL_MAX_CHARS_EACH = 400;
42
+ async function buildSkillsSection(sessionId) {
43
+ const skills = await listSkills(sessionId);
44
+ if (skills.length === 0) return [];
45
+ const skillLines = ["## Skills"];
46
+ for (const skill of skills.slice(0, SKILL_MAX_COUNT)) {
47
+ let checklist = null;
48
+ try {
49
+ const filePath = await getSkillFilePath(skill.name);
50
+ if (filePath !== null) {
51
+ const body = await readFile(filePath, "utf-8");
52
+ checklist = extractChecklistSection(body);
53
+ }
54
+ } catch {
55
+ checklist = null;
56
+ }
57
+ if (checklist !== null) {
58
+ const trimmed = checklist.length > SKILL_MAX_CHARS_EACH ? checklist.slice(0, SKILL_MAX_CHARS_EACH).trimEnd() + "\u2026" : checklist;
59
+ skillLines.push(`**${displaySafeText(skill.name)}**:`);
60
+ skillLines.push(neutralizeSpokenMarkers(trimmed));
61
+ } else {
62
+ skillLines.push(`**${displaySafeText(skill.name)}** \u2014 \`token-goat skill-body ${displaySafeText(skill.name)} --section DoD\``);
63
+ }
64
+ }
65
+ skillLines.push("");
66
+ return skillLines;
67
+ }
68
+ async function buildResumePacket(sessionId) {
69
+ const blob = loadBlob(SESSIONS_SUBDIR, sessionId);
70
+ if (blob === null || typeof blob !== "object") return null;
71
+ const raw = blob;
72
+ const filesArr = Array.isArray(raw["files"]) ? raw["files"] : [];
73
+ const lines = [`# Resume packet \u2014 session ${displaySafeText(sessionId)}`, ""];
74
+ try {
75
+ lines.push(...await buildSkillsSection(sessionId));
76
+ } catch {
77
+ }
78
+ const editedPaths = filesArr.filter((f) => f["wasEdited"] === true).map((f) => typeof f["path"] === "string" ? f["path"] : "").filter(Boolean).slice(0, 10);
79
+ if (editedPaths.length > 0) {
80
+ lines.push("## Edited files");
81
+ for (const p of editedPaths) lines.push(`- ${displaySafePath(p)}`);
82
+ lines.push("");
83
+ }
84
+ const topRead = [...filesArr].filter((f) => f["wasEdited"] !== true).sort((a, b) => (typeof b["readCount"] === "number" ? b["readCount"] : 0) - (typeof a["readCount"] === "number" ? a["readCount"] : 0)).slice(0, 8).map((f) => typeof f["path"] === "string" ? f["path"] : "").filter(Boolean);
85
+ if (topRead.length > 0) {
86
+ lines.push("## Top files read");
87
+ for (const p of topRead) lines.push(`- ${displaySafePath(p)}`);
88
+ lines.push("");
89
+ }
90
+ const bashOutputs = Array.isArray(raw["bashOutputs"]) ? raw["bashOutputs"] : [];
91
+ const recentBash = bashOutputs.slice(-2);
92
+ if (recentBash.length > 0) {
93
+ lines.push("## Recent bash commands");
94
+ for (const entry of recentBash) {
95
+ if (Array.isArray(entry) && typeof entry[1] === "string") {
96
+ const bashEntry = getBashOutput(entry[1]);
97
+ if (bashEntry !== null) lines.push(`- ${displaySafeText(bashEntry.command)}`);
98
+ }
99
+ }
100
+ lines.push("");
101
+ }
102
+ const webFetches = Array.isArray(raw["webFetches"]) ? raw["webFetches"] : [];
103
+ const webRows = webFetches.slice(-MAX_RESUME_WEB_FETCHES).filter((e) => Array.isArray(e) && typeof e[0] === "string" && typeof e[1] === "string").map(([key, cacheId]) => renderWebFetchRow(key, cacheId));
104
+ if (webRows.length > 0) {
105
+ lines.push("## Web pages fetched");
106
+ lines.push(...webRows);
107
+ lines.push("");
108
+ }
109
+ try {
110
+ const projectRoot = resolveProjectRoot();
111
+ const result = runGit(["diff", "--stat"], { cwd: projectRoot });
112
+ if (result.exitCode === 0 && result.stdout.trim().length > 0) {
113
+ lines.push("## Uncommitted changes (git diff --stat)");
114
+ lines.push(result.stdout.trim().split("\n").map(displaySafeText).join("\n"));
115
+ lines.push("");
116
+ }
117
+ } catch {
118
+ }
119
+ const text = lines.join("\n");
120
+ if (text.length <= MAX_RESUME_CHARS) return text;
121
+ return safeSlice(text, MAX_RESUME_CHARS) + "\n... (truncated to cap)\n";
122
+ }
123
+
124
+ export {
125
+ MAX_RESUME_TOKENS,
126
+ MAX_RESUME_CHARS,
127
+ buildResumePacket
128
+ };