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
@@ -0,0 +1,292 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ accumulateResidentLine,
5
+ claudeConfigDir,
6
+ createResidentContextStats,
7
+ estimateTokens,
8
+ summarizeResidentContext
9
+ } from "./token-goat-chunk-MEKVJZUN.mjs";
10
+ import {
11
+ commandHash,
12
+ getBashOutput,
13
+ normalizeCommandForCacheKey
14
+ } from "./token-goat-chunk-M43UJVYQ.mjs";
15
+ import {
16
+ init_define_import_meta_env
17
+ } from "./token-goat-chunk-A37V4PBF.mjs";
18
+
19
+ // src/waste.ts
20
+ init_define_import_meta_env();
21
+ import * as fs from "node:fs";
22
+ import * as path from "node:path";
23
+ import { StringDecoder } from "node:string_decoder";
24
+ function projectTranscriptsDir(projectRoot) {
25
+ const rootStr = path.resolve(projectRoot);
26
+ const slug = rootStr.replace(/[^A-Za-z0-9]/g, "-");
27
+ return path.join(claudeConfigDir(), "projects", slug);
28
+ }
29
+ function findLatestTranscript(projectRoot) {
30
+ const dir = projectTranscriptsDir(projectRoot);
31
+ let entries;
32
+ try {
33
+ entries = fs.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
34
+ } catch {
35
+ return null;
36
+ }
37
+ let best = null;
38
+ for (const name of entries) {
39
+ const full = path.join(dir, name);
40
+ let stat;
41
+ try {
42
+ stat = fs.statSync(full);
43
+ } catch {
44
+ continue;
45
+ }
46
+ if (best === null || stat.mtimeMs > best.mtimeMs) best = { file: full, mtimeMs: stat.mtimeMs };
47
+ }
48
+ return best?.file ?? null;
49
+ }
50
+ function safeStringify(value) {
51
+ try {
52
+ return JSON.stringify(value) ?? "";
53
+ } catch {
54
+ return "";
55
+ }
56
+ }
57
+ var FILE_PATH_TOOLS = /* @__PURE__ */ new Set(["Read", "Edit", "Write", "NotebookEdit", "MultiEdit"]);
58
+ function extractFilePath(name, input) {
59
+ if (!FILE_PATH_TOOLS.has(name)) return null;
60
+ if (input === null || typeof input !== "object") return null;
61
+ const o = input;
62
+ const fp = o["file_path"];
63
+ if (typeof fp === "string") return fp;
64
+ const notebookPath = o["notebook_path"];
65
+ return typeof notebookPath === "string" ? notebookPath : null;
66
+ }
67
+ function extractCommand(name, input) {
68
+ if (name !== "Bash") return null;
69
+ if (input === null || typeof input !== "object") return null;
70
+ const cmd = input["command"];
71
+ return typeof cmd === "string" ? cmd : null;
72
+ }
73
+ function summarizeInput(name, input, filePath, command) {
74
+ if (filePath !== null) return filePath;
75
+ if (command !== null) return command.length > 100 ? `${command.slice(0, 100)}\u2026` : command;
76
+ if (input === null || typeof input !== "object") return "";
77
+ const o = input;
78
+ for (const key of ["pattern", "query", "path", "description", "prompt"]) {
79
+ const v = o[key];
80
+ if (typeof v === "string") return v.length > 100 ? `${v.slice(0, 100)}\u2026` : v;
81
+ }
82
+ const s = safeStringify(input);
83
+ return s.length > 100 ? `${s.slice(0, 100)}\u2026` : s;
84
+ }
85
+ function extractResultText(content) {
86
+ if (typeof content === "string") return content;
87
+ if (Array.isArray(content)) {
88
+ return content.map((b) => {
89
+ if (b !== null && typeof b === "object" && b["type"] === "text") {
90
+ const t = b["text"];
91
+ return typeof t === "string" ? t : "";
92
+ }
93
+ return "";
94
+ }).join("\n");
95
+ }
96
+ return "";
97
+ }
98
+ function* readFileLines(filePath) {
99
+ const fd = fs.openSync(filePath, "r");
100
+ try {
101
+ const buf = Buffer.allocUnsafe(1 << 20);
102
+ const decoder = new StringDecoder("utf8");
103
+ let carry = "";
104
+ for (; ; ) {
105
+ const bytes = fs.readSync(fd, buf, 0, buf.length, null);
106
+ if (bytes === 0) break;
107
+ const lines = (carry + decoder.write(buf.subarray(0, bytes))).split("\n");
108
+ carry = lines.pop() ?? "";
109
+ for (const line of lines) yield line;
110
+ }
111
+ carry += decoder.end();
112
+ if (carry !== "") yield carry;
113
+ } finally {
114
+ fs.closeSync(fd);
115
+ }
116
+ }
117
+ function parseTranscript(transcriptPath) {
118
+ const calls = [];
119
+ const resultTextById = /* @__PURE__ */ new Map();
120
+ const assistantTurns = [];
121
+ const resident = createResidentContextStats();
122
+ let seq = 0;
123
+ for (const line of readFileLines(transcriptPath)) {
124
+ const trimmed = line.trim();
125
+ if (trimmed === "") continue;
126
+ let obj;
127
+ try {
128
+ obj = JSON.parse(trimmed);
129
+ } catch {
130
+ continue;
131
+ }
132
+ if (obj === null || typeof obj !== "object") continue;
133
+ const o = obj;
134
+ accumulateResidentLine(resident, o, trimmed.length);
135
+ const message = o["message"];
136
+ if (message === null || typeof message !== "object") continue;
137
+ const messageObj = message;
138
+ const content = messageObj["content"];
139
+ if (!Array.isArray(content)) continue;
140
+ const role = typeof messageObj["role"] === "string" ? messageObj["role"] : null;
141
+ const cwd = typeof o["cwd"] === "string" ? o["cwd"] : null;
142
+ let turnTextTokens = 0;
143
+ let sawTextBlock = false;
144
+ for (const block of content) {
145
+ if (block === null || typeof block !== "object") continue;
146
+ const b = block;
147
+ if (b["type"] === "tool_use" && typeof b["id"] === "string" && typeof b["name"] === "string") {
148
+ const name = b["name"];
149
+ const input = b["input"];
150
+ const filePath = extractFilePath(name, input);
151
+ const command = extractCommand(name, input);
152
+ calls.push({
153
+ seq: seq++,
154
+ id: b["id"],
155
+ name,
156
+ input,
157
+ filePath,
158
+ command,
159
+ summary: summarizeInput(name, input, filePath, command),
160
+ cwd
161
+ });
162
+ } else if (b["type"] === "tool_result" && typeof b["tool_use_id"] === "string") {
163
+ const id = b["tool_use_id"];
164
+ if (!resultTextById.has(id)) {
165
+ resultTextById.set(id, extractResultText(b["content"]));
166
+ }
167
+ } else if (role === "assistant" && b["type"] === "text" && typeof b["text"] === "string") {
168
+ sawTextBlock = true;
169
+ turnTextTokens += estimateTokens(b["text"]);
170
+ }
171
+ }
172
+ if (role === "assistant" && sawTextBlock) assistantTurns.push(turnTextTokens);
173
+ }
174
+ return { calls, resultTextById, assistantTurns, resident };
175
+ }
176
+ function costPerCall(parsed) {
177
+ return parsed.calls.map((c) => ({
178
+ ...c,
179
+ tokens: estimateTokens(parsed.resultTextById.get(c.id) ?? "")
180
+ }));
181
+ }
182
+ function aggregateSortedDesc(pairs) {
183
+ return [...pairs].map(([key, tokens]) => ({ key, tokens })).sort((a, b) => b.tokens - a.tokens);
184
+ }
185
+ function tokensByTool(costs) {
186
+ const m = /* @__PURE__ */ new Map();
187
+ for (const c of costs) m.set(c.name, (m.get(c.name) ?? 0) + c.tokens);
188
+ return aggregateSortedDesc(m);
189
+ }
190
+ function tokensByFile(costs) {
191
+ const m = /* @__PURE__ */ new Map();
192
+ for (const c of costs) {
193
+ if (c.filePath === null) continue;
194
+ m.set(c.filePath, (m.get(c.filePath) ?? 0) + c.tokens);
195
+ }
196
+ return aggregateSortedDesc(m);
197
+ }
198
+ function topExpensiveCalls(costs, n) {
199
+ return [...costs].sort((a, b) => b.tokens - a.tokens).slice(0, n);
200
+ }
201
+ function neverTouchedAgain(costs) {
202
+ const readsByFile = /* @__PURE__ */ new Map();
203
+ for (const c of costs) {
204
+ if (c.name !== "Read" || c.filePath === null) continue;
205
+ const arr = readsByFile.get(c.filePath) ?? [];
206
+ arr.push(c);
207
+ readsByFile.set(c.filePath, arr);
208
+ }
209
+ const out = [];
210
+ for (const [filePath, reads] of readsByFile) {
211
+ const firstSeq = Math.min(...reads.map((r) => r.seq));
212
+ const touchedLater = costs.some((c) => {
213
+ if (c.seq <= firstSeq) return false;
214
+ if (c.filePath === filePath) return true;
215
+ return safeStringify(c.input).includes(filePath);
216
+ });
217
+ if (!touchedLater) {
218
+ out.push({ filePath, tokens: reads.reduce((n, r) => n + r.tokens, 0) });
219
+ }
220
+ }
221
+ return out.sort((a, b) => b.tokens - a.tokens);
222
+ }
223
+ async function repeatedUncompressedBashCommands(costs) {
224
+ const groups = /* @__PURE__ */ new Map();
225
+ for (const c of costs) {
226
+ if (c.name !== "Bash" || c.command === null) continue;
227
+ const norm = normalizeCommandForCacheKey(c.command);
228
+ const arr = groups.get(norm) ?? [];
229
+ arr.push(c);
230
+ groups.set(norm, arr);
231
+ }
232
+ const out = [];
233
+ for (const [norm, calls] of groups) {
234
+ if (calls.length < 2) continue;
235
+ let cached = false;
236
+ for (const c of calls) {
237
+ try {
238
+ const hash = await commandHash(c.command, c.cwd);
239
+ if (getBashOutput(hash) !== null) {
240
+ cached = true;
241
+ break;
242
+ }
243
+ } catch {
244
+ }
245
+ }
246
+ if (cached) continue;
247
+ const totalTokens = calls.reduce((n, c) => n + c.tokens, 0);
248
+ out.push({
249
+ normalized: norm,
250
+ count: calls.length,
251
+ totalTokens,
252
+ avgTokens: Math.round(totalTokens / calls.length)
253
+ });
254
+ }
255
+ return out.sort((a, b) => b.totalTokens - a.totalTokens);
256
+ }
257
+ function assistantOutputCost(assistantTurns) {
258
+ const turnCount = assistantTurns.length;
259
+ let generatedTokens = 0;
260
+ let resendCeilingTokens = 0;
261
+ for (let i = 0; i < turnCount; i++) {
262
+ const tokens = assistantTurns[i] ?? 0;
263
+ generatedTokens += tokens;
264
+ resendCeilingTokens += tokens * (turnCount - 1 - i);
265
+ }
266
+ return { turnCount, generatedTokens, resendCeilingTokens };
267
+ }
268
+ async function buildWasteReport(transcriptPath, opts = {}) {
269
+ const parsed = parseTranscript(transcriptPath);
270
+ const costs = costPerCall(parsed);
271
+ const topN = opts.topN ?? 10;
272
+ return {
273
+ transcriptPath,
274
+ totalTokens: costs.reduce((n, c) => n + c.tokens, 0),
275
+ tokensByTool: tokensByTool(costs),
276
+ tokensByFile: tokensByFile(costs),
277
+ topCalls: topExpensiveCalls(costs, topN).map((c) => ({ seq: c.seq, name: c.name, summary: c.summary, tokens: c.tokens })),
278
+ neverTouchedAgain: neverTouchedAgain(costs),
279
+ repeatedUncompressedBash: await repeatedUncompressedBashCommands(costs),
280
+ assistantOutput: assistantOutputCost(parsed.assistantTurns),
281
+ residentContext: summarizeResidentContext(parsed.resident)
282
+ };
283
+ }
284
+
285
+ export {
286
+ projectTranscriptsDir,
287
+ findLatestTranscript,
288
+ safeStringify,
289
+ extractResultText,
290
+ readFileLines,
291
+ buildWasteReport
292
+ };
@@ -2,10 +2,10 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  loadConfig
5
- } from "./token-goat-chunk-SOKAPOOC.mjs";
5
+ } from "./token-goat-chunk-M6CDVASA.mjs";
6
6
  import {
7
7
  VERSION
8
- } from "./token-goat-chunk-ZGQXRYKE.mjs";
8
+ } from "./token-goat-chunk-TGBSMHPM.mjs";
9
9
  import {
10
10
  displaySafeJson
11
11
  } from "./token-goat-chunk-NMTKNYGF.mjs";
@@ -0,0 +1,198 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ buildCopilotWasteReport,
5
+ findProjectSession,
6
+ isCopilotTranscript
7
+ } from "./token-goat-chunk-PPYZS7TI.mjs";
8
+ import {
9
+ buildWasteReport
10
+ } from "./token-goat-chunk-KQI24O4B.mjs";
11
+ import "./token-goat-chunk-MQACN7VG.mjs";
12
+ import "./token-goat-chunk-MEKVJZUN.mjs";
13
+ import "./token-goat-chunk-M43UJVYQ.mjs";
14
+ import "./token-goat-chunk-PHDNWISV.mjs";
15
+ import "./token-goat-chunk-XQSSPX3X.mjs";
16
+ import "./token-goat-chunk-2ZHD3XCD.mjs";
17
+ import {
18
+ resolveProjectRoot
19
+ } from "./token-goat-chunk-M6CDVASA.mjs";
20
+ import "./token-goat-chunk-EEIDFMEM.mjs";
21
+ import "./token-goat-chunk-OSUFN2FV.mjs";
22
+ import "./token-goat-chunk-TGBSMHPM.mjs";
23
+ import {
24
+ displaySafeJson
25
+ } from "./token-goat-chunk-NMTKNYGF.mjs";
26
+ import "./token-goat-chunk-GMOUBOX4.mjs";
27
+ import {
28
+ init_define_import_meta_env
29
+ } from "./token-goat-chunk-A37V4PBF.mjs";
30
+
31
+ // src/cli_audit.ts
32
+ init_define_import_meta_env();
33
+ import * as fs from "node:fs";
34
+ import * as path from "node:path";
35
+ function buildFeedbackCardFromClaude(report) {
36
+ const preAudit = [];
37
+ const fixes = [];
38
+ if (report.neverTouchedAgain.length > 0) {
39
+ const files = report.neverTouchedAgain.map((f) => path.basename(f.filePath)).join(", ");
40
+ preAudit.push(`Whole-file reads without reuse: ${files} (${report.neverTouchedAgain.length} file(s) read once via Read)`);
41
+ fixes.push('Prefer `token-goat symbol` or `read "file::symbol"` instead of reading whole files.');
42
+ }
43
+ if (report.repeatedUncompressedBash.length > 0) {
44
+ const cmds = report.repeatedUncompressedBash.map((b) => `"${b.normalized.slice(0, 40)}" (${b.count}x)`).join(", ");
45
+ preAudit.push(`Repeated uncompressed terminal commands: ${cmds}`);
46
+ fixes.push("Use token-goat bash-output cache or compress flags for repetitive terminal invocations.");
47
+ }
48
+ if (preAudit.length === 0) {
49
+ preAudit.push("Pre-audit session: No missed surgical reads or friction detected; gate adhered to cleanly.");
50
+ }
51
+ if (fixes.length === 0) {
52
+ fixes.push("Maintain current surgical read and caching discipline.");
53
+ }
54
+ return {
55
+ taskSummary: `Engineering session in project (${report.totalTokens.toLocaleString()} tokens recorded across tool invocations).`,
56
+ preAuditFindings: preAudit,
57
+ auditExecutionFindings: [],
58
+ recommendedFix: fixes
59
+ };
60
+ }
61
+ function buildFeedbackCardFromCopilot(report) {
62
+ const preAudit = [];
63
+ const fixes = [];
64
+ const repeatedBlocks = report.blocks.filter((b) => b.repeatCount > 0);
65
+ if (repeatedBlocks.length > 0) {
66
+ const kinds = repeatedBlocks.map((b) => `${b.kind} (${b.repeatCount}x repeat, ${b.repeatBytes} B)`).join(", ");
67
+ preAudit.push(`Repeated prompt blocks across turns: ${kinds}`);
68
+ fixes.push("Deduplicate repeated prompt instructions or context blocks across turns.");
69
+ }
70
+ if (report.mcpTools && report.mcpTools.servers.length > 0) {
71
+ const totalMcpTokens = report.mcpTools.servers.reduce((sum, s) => sum + s.estimatedTokens, 0);
72
+ const heavy = report.mcpTools.servers.filter((s) => s.estimatedTokens > 1e3);
73
+ if (heavy.length > 0 || totalMcpTokens > 1500) {
74
+ const details = report.mcpTools.servers.map((s) => `${s.serverName} (${s.toolCount} tool(s), ~${s.estimatedTokens.toLocaleString()} tok/turn)`).join(", ");
75
+ const turns = Math.max(1, report.turns);
76
+ const cumulative = totalMcpTokens * turns;
77
+ preAudit.push(
78
+ `High fixed MCP tool definition overhead: ${details}; total ~${totalMcpTokens.toLocaleString()} tok/turn re-sent across ${turns} turn(s) (~${cumulative.toLocaleString()} cumulative tokens).`
79
+ );
80
+ const heavyNames = (heavy.length > 0 ? heavy : report.mcpTools.servers).map((s) => s.serverName).join(", ");
81
+ fixes.push(
82
+ `Disable unused MCP server(s) (${heavyNames}) via 'copilot mcp disable <name>' or exclude individual tools with '--excluded-tools'.`
83
+ );
84
+ }
85
+ }
86
+ if (preAudit.length === 0) {
87
+ preAudit.push("Pre-audit session: No missed surgical reads or friction detected; gate adhered to cleanly.");
88
+ }
89
+ if (fixes.length === 0) {
90
+ fixes.push("Maintain current token-efficient tooling and MCP server configuration.");
91
+ }
92
+ return {
93
+ taskSummary: `Copilot CLI session (${report.turns} turn(s) recorded in ${report.sessionId}).`,
94
+ preAuditFindings: preAudit,
95
+ auditExecutionFindings: [],
96
+ recommendedFix: fixes
97
+ };
98
+ }
99
+ function formatMaintainerCard(card) {
100
+ const lines = [
101
+ "Maintainer Feedback Card",
102
+ "",
103
+ `\u2022 Task Summary: ${card.taskSummary}`,
104
+ "\u2022 Friction & Missed Savings:"
105
+ ];
106
+ for (const f of card.preAuditFindings) {
107
+ lines.push(` - ${f}`);
108
+ }
109
+ if (card.auditExecutionFindings.length > 0) {
110
+ for (const a of card.auditExecutionFindings) {
111
+ lines.push(` - Audit Execution: ${a}`);
112
+ }
113
+ }
114
+ lines.push("\u2022 Recommended Fix:");
115
+ for (const r of card.recommendedFix) {
116
+ lines.push(` - ${r}`);
117
+ }
118
+ return lines.join("\n") + "\n";
119
+ }
120
+ async function runAuditCommand(opts = {}) {
121
+ const projectRoot = resolveProjectRoot(opts.project !== void 0 ? { project: opts.project } : {});
122
+ if (opts.transcript !== void 0) {
123
+ const resolved = path.resolve(opts.transcript);
124
+ if (!fs.existsSync(resolved)) {
125
+ const err = `transcript not found: ${resolved}`;
126
+ if (opts.json === true) {
127
+ process.stdout.write(`${displaySafeJson({ error: err }, 0)}
128
+ `);
129
+ } else {
130
+ process.stderr.write(`token-goat: ${err}
131
+ `);
132
+ }
133
+ process.exitCode = 1;
134
+ return;
135
+ }
136
+ if (isCopilotTranscript(resolved)) {
137
+ const report3 = buildCopilotWasteReport(resolved);
138
+ const card3 = buildFeedbackCardFromCopilot(report3);
139
+ if (opts.json === true) {
140
+ process.stdout.write(`${displaySafeJson(card3, 0)}
141
+ `);
142
+ } else {
143
+ process.stdout.write(formatMaintainerCard(card3));
144
+ }
145
+ return;
146
+ }
147
+ const report2 = await buildWasteReport(resolved);
148
+ const card2 = buildFeedbackCardFromClaude(report2);
149
+ if (opts.json === true) {
150
+ process.stdout.write(`${displaySafeJson(card2, 0)}
151
+ `);
152
+ } else {
153
+ process.stdout.write(formatMaintainerCard(card2));
154
+ }
155
+ return;
156
+ }
157
+ const detected = findProjectSession(projectRoot);
158
+ if (detected === null) {
159
+ const err = "no session transcript found for project";
160
+ if (opts.json === true) {
161
+ process.stdout.write(`${displaySafeJson({ error: err, project: projectRoot }, 0)}
162
+ `);
163
+ } else {
164
+ process.stdout.write(`
165
+ # token-goat audit
166
+ Project: ${projectRoot}
167
+ No session transcript found. Pass --transcript <path> to specify one explicitly.
168
+ `);
169
+ }
170
+ process.exitCode = 1;
171
+ return;
172
+ }
173
+ if (detected.kind === "copilot") {
174
+ const report2 = buildCopilotWasteReport(detected.path);
175
+ const card2 = buildFeedbackCardFromCopilot(report2);
176
+ if (opts.json === true) {
177
+ process.stdout.write(`${displaySafeJson(card2, 0)}
178
+ `);
179
+ } else {
180
+ process.stdout.write(formatMaintainerCard(card2));
181
+ }
182
+ return;
183
+ }
184
+ const report = await buildWasteReport(detected.path);
185
+ const card = buildFeedbackCardFromClaude(report);
186
+ if (opts.json === true) {
187
+ process.stdout.write(`${displaySafeJson(card, 0)}
188
+ `);
189
+ } else {
190
+ process.stdout.write(formatMaintainerCard(card));
191
+ }
192
+ }
193
+ export {
194
+ buildFeedbackCardFromClaude,
195
+ buildFeedbackCardFromCopilot,
196
+ formatMaintainerCard,
197
+ runAuditCommand
198
+ };
@@ -3,7 +3,7 @@ const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  queryCsv,
5
5
  quoteCsvCell
6
- } from "./token-goat-chunk-KWM6KBPA.mjs";
6
+ } from "./token-goat-chunk-SHV6QQAS.mjs";
7
7
  import {
8
8
  NotAnOfficeDocumentError,
9
9
  assertOoxmlWithinDeadline,
@@ -12,7 +12,7 @@ import {
12
12
  ooxmlWorkDeadline,
13
13
  parseOoxmlPart,
14
14
  readOoxmlZip
15
- } from "./token-goat-chunk-BTKDZW7F.mjs";
15
+ } from "./token-goat-chunk-QTIRVK3M.mjs";
16
16
  import {
17
17
  displaySafeText
18
18
  } from "./token-goat-chunk-NMTKNYGF.mjs";