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.
- package/README.md +3 -1
- package/dist/{token-goat-chunk-JNIFSVWM.mjs → token-goat-chunk-2ZHD3XCD.mjs} +5 -1
- package/dist/{token-goat-chunk-P46RTOAQ.mjs → token-goat-chunk-4IIU3GNS.mjs} +10 -7
- package/dist/{token-goat-chunk-2GTB5RBZ.mjs → token-goat-chunk-4V4OZHHV.mjs} +11 -271
- package/dist/token-goat-chunk-64OEF3UN.mjs +128 -0
- package/dist/{token-goat-chunk-AVRD7SBO.mjs → token-goat-chunk-6AQLZMCO.mjs} +270 -29
- package/dist/{token-goat-chunk-LACH3WKS.mjs → token-goat-chunk-BIEDTF6V.mjs} +3 -3
- package/dist/{token-goat-chunk-PIAESKVU.mjs → token-goat-chunk-BKHJRFDE.mjs} +568 -429
- package/dist/token-goat-chunk-CAHN4QU7.mjs +26 -0
- package/dist/{token-goat-chunk-MU6I4IDI.mjs → token-goat-chunk-CMAJGP3E.mjs} +11 -7
- package/dist/token-goat-chunk-CMHBTXMY.mjs +41 -0
- package/dist/{token-goat-chunk-HPMMLV37.mjs → token-goat-chunk-CZ5ZFJSN.mjs} +256 -341
- package/dist/{token-goat-chunk-7DGTVBWP.mjs → token-goat-chunk-DDP64XR7.mjs} +159 -1863
- package/dist/{token-goat-chunk-FX5DHKGF.mjs → token-goat-chunk-DISVHOW6.mjs} +4 -4
- package/dist/{token-goat-chunk-L2UOJBV4.mjs → token-goat-chunk-FZN77SZD.mjs} +27 -19
- package/dist/{token-goat-chunk-DA6TIDLI.mjs → token-goat-chunk-GO7OWONJ.mjs} +18 -13
- package/dist/{token-goat-chunk-EP7TCWHM.mjs → token-goat-chunk-IAMIQLCV.mjs} +83 -66
- package/dist/{token-goat-chunk-NJKNAC37.mjs → token-goat-chunk-IJUHQX2M.mjs} +1 -1
- package/dist/token-goat-chunk-KQI24O4B.mjs +292 -0
- package/dist/{token-goat-chunk-GYC34EGE.mjs → token-goat-chunk-KRPJIXCT.mjs} +2 -2
- package/dist/token-goat-chunk-KVAXTA2W.mjs +198 -0
- package/dist/{token-goat-chunk-FJZ5MZK7.mjs → token-goat-chunk-LK3YOKDF.mjs} +2 -2
- package/dist/token-goat-chunk-M43UJVYQ.mjs +1816 -0
- package/dist/{token-goat-chunk-SOKAPOOC.mjs → token-goat-chunk-M6CDVASA.mjs} +46 -47
- package/dist/token-goat-chunk-MECDAPXB.mjs +1989 -0
- package/dist/token-goat-chunk-MEKVJZUN.mjs +324 -0
- package/dist/token-goat-chunk-MQACN7VG.mjs +1379 -0
- package/dist/{token-goat-chunk-3W5P673V.mjs → token-goat-chunk-MZQORUX5.mjs} +1 -1
- package/dist/{token-goat-chunk-DHIXE6GV.mjs → token-goat-chunk-ORRDWKLA.mjs} +1 -1
- package/dist/{token-goat-chunk-F24NE3FO.mjs → token-goat-chunk-PCR43MCM.mjs} +2 -2
- package/dist/token-goat-chunk-PDYQA3AP.mjs +248 -0
- package/dist/{token-goat-chunk-S7RFIX4Y.mjs → token-goat-chunk-PHDNWISV.mjs} +45 -14
- package/dist/token-goat-chunk-PPYZS7TI.mjs +304 -0
- package/dist/{token-goat-chunk-BTKDZW7F.mjs → token-goat-chunk-QTIRVK3M.mjs} +1 -1
- package/dist/{token-goat-chunk-62VJ47WN.mjs → token-goat-chunk-QUM3UNWS.mjs} +5 -5
- package/dist/{token-goat-chunk-KWM6KBPA.mjs → token-goat-chunk-SHV6QQAS.mjs} +1 -1
- package/dist/{token-goat-chunk-FQYMZASI.mjs → token-goat-chunk-SMUYPBF5.mjs} +796 -4464
- package/dist/{token-goat-chunk-ZGQXRYKE.mjs → token-goat-chunk-TGBSMHPM.mjs} +1 -1
- package/dist/{token-goat-chunk-EXLLLIC3.mjs → token-goat-chunk-VXKT3W4C.mjs} +3 -3
- package/dist/{token-goat-chunk-WKO5TVNA.mjs → token-goat-chunk-XJUPKJF5.mjs} +544 -576
- package/dist/{token-goat-chunk-IBTFGZBX.mjs → token-goat-chunk-XQSSPX3X.mjs} +2 -2
- package/dist/{token-goat-chunk-VJQBYSXG.mjs → token-goat-chunk-ZJWPJL7O.mjs} +3 -3
- package/dist/token-goat-hook.mjs +17 -11
- package/dist/token-goat.core.mjs +27 -19
- package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
- package/docs/cli.md +13 -12
- package/docs/install.md +1 -1
- package/docs/security.md +2 -2
- package/package.json +3 -2
- package/dist/token-goat-chunk-NYFYIRG3.mjs +0 -35
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
|
+
const require = __cjsRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
classifyContent,
|
|
5
|
+
guardDivisor,
|
|
6
|
+
stripAnsiEscapes
|
|
7
|
+
} from "./token-goat-chunk-PHDNWISV.mjs";
|
|
8
|
+
import {
|
|
9
|
+
safeSlice
|
|
10
|
+
} from "./token-goat-chunk-TGBSMHPM.mjs";
|
|
11
|
+
import {
|
|
12
|
+
displaySafeText
|
|
13
|
+
} from "./token-goat-chunk-NMTKNYGF.mjs";
|
|
14
|
+
import {
|
|
15
|
+
init_define_import_meta_env
|
|
16
|
+
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
17
|
+
|
|
18
|
+
// src/claude_config_dir.ts
|
|
19
|
+
init_define_import_meta_env();
|
|
20
|
+
import * as os from "node:os";
|
|
21
|
+
import * as path from "node:path";
|
|
22
|
+
function claudeConfigDir(homeDir = os.homedir()) {
|
|
23
|
+
const override = process.env["CLAUDE_CONFIG_DIR"];
|
|
24
|
+
if (override !== void 0 && override !== "") return override;
|
|
25
|
+
return path.join(homeDir, ".claude");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/overflow_guard.ts
|
|
29
|
+
init_define_import_meta_env();
|
|
30
|
+
function estimateTokensFromLength(length, cls = "text") {
|
|
31
|
+
return Math.max(1, Math.floor(Math.max(0, length) / guardDivisor(cls)) + 1);
|
|
32
|
+
}
|
|
33
|
+
function estimateTokens(text) {
|
|
34
|
+
const stripped = stripAnsiEscapes(text);
|
|
35
|
+
return estimateTokensFromLength(stripped.length, classifyContent(stripped));
|
|
36
|
+
}
|
|
37
|
+
function trimToBudget(text, budgetTokens, command) {
|
|
38
|
+
const markerMarginTokens = 64;
|
|
39
|
+
const strippedAll = stripAnsiEscapes(text);
|
|
40
|
+
const contentClass = classifyContent(strippedAll);
|
|
41
|
+
const totalTokens = estimateTokensFromLength(strippedAll.length, contentClass);
|
|
42
|
+
if (totalTokens <= budgetTokens) {
|
|
43
|
+
return text;
|
|
44
|
+
}
|
|
45
|
+
const lines = text.split("\n");
|
|
46
|
+
if (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
|
|
47
|
+
const totalLines = lines.length;
|
|
48
|
+
const bodyBudget = Math.max(1, budgetTokens - markerMarginTokens);
|
|
49
|
+
const charBudget = bodyBudget * guardDivisor(contentClass);
|
|
50
|
+
const kept = [];
|
|
51
|
+
let used = 0;
|
|
52
|
+
for (const ln of lines) {
|
|
53
|
+
const stripped = stripAnsiEscapes(ln);
|
|
54
|
+
const cost = ln.length + 1;
|
|
55
|
+
if (kept.length === 0 && cost > charBudget) {
|
|
56
|
+
const truncated = safeSlice(stripped, charBudget);
|
|
57
|
+
kept.push(truncated);
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
if (kept.length > 0 && used + cost > charBudget) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
kept.push(ln);
|
|
64
|
+
used += cost;
|
|
65
|
+
}
|
|
66
|
+
const shown = kept.length;
|
|
67
|
+
const hint = getHintFor(command);
|
|
68
|
+
const marker = `[token-goat: output capped at ~${budgetTokens} tokens to protect context \u2014 showing ${shown} of ${totalLines} lines. ${hint}]`;
|
|
69
|
+
return kept.join("\n") + "\n" + marker;
|
|
70
|
+
}
|
|
71
|
+
function capJsonRows(items, budgetTokens) {
|
|
72
|
+
const totalCount = items.length;
|
|
73
|
+
const charBudget = Math.max(1, budgetTokens * guardDivisor());
|
|
74
|
+
const kept = [];
|
|
75
|
+
let used = 0;
|
|
76
|
+
for (const item of items) {
|
|
77
|
+
const cost = JSON.stringify(item).length + 2;
|
|
78
|
+
if (kept.length > 0 && used + cost > charBudget) break;
|
|
79
|
+
kept.push(item);
|
|
80
|
+
used += cost;
|
|
81
|
+
}
|
|
82
|
+
return { items: kept, truncated: kept.length < totalCount, totalCount };
|
|
83
|
+
}
|
|
84
|
+
function getHintFor(command) {
|
|
85
|
+
const cmd = (command || "").toLowerCase().trim();
|
|
86
|
+
if (cmd === "symbol") {
|
|
87
|
+
return "Request a specific method (file.py::Class.method) or use --json for structured access.";
|
|
88
|
+
}
|
|
89
|
+
if (cmd === "heading" || cmd === "section") {
|
|
90
|
+
return "Request a narrower sub-heading, e.g. 'doc.md::Section#2'.";
|
|
91
|
+
}
|
|
92
|
+
if (cmd === "lines") {
|
|
93
|
+
return "Request a smaller line range, e.g. 'file.py@100-150'.";
|
|
94
|
+
}
|
|
95
|
+
if (cmd === "bash-output" || cmd === "web-output") {
|
|
96
|
+
return "Use --grep PATTERN, --section HEADING, or --tail N to narrow the cached output.";
|
|
97
|
+
}
|
|
98
|
+
if (cmd === "semantic") {
|
|
99
|
+
return "Narrow your query text or pass --limit to reduce the number of matches returned.";
|
|
100
|
+
}
|
|
101
|
+
return "Narrow your query or raise overflow_guard max_tokens in config.";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/resident_context.ts
|
|
105
|
+
init_define_import_meta_env();
|
|
106
|
+
import * as fs from "node:fs";
|
|
107
|
+
var LARGE_TASK_LIST_BYTES = 2e4;
|
|
108
|
+
var LARGE_SKILL_BODY_BYTES = 2e4;
|
|
109
|
+
var SKILL_BODY_REPEAT_THRESHOLD = 2;
|
|
110
|
+
var RESIDENT_TAIL_MAX_BYTES = 1048576;
|
|
111
|
+
function createResidentContextStats() {
|
|
112
|
+
return {
|
|
113
|
+
attachmentsByType: /* @__PURE__ */ new Map(),
|
|
114
|
+
latestTaskList: null,
|
|
115
|
+
taskReminderCount: 0,
|
|
116
|
+
taskReminderBytes: 0,
|
|
117
|
+
skillBodies: /* @__PURE__ */ new Map(),
|
|
118
|
+
compactionCount: 0
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function bump(map, key, bytes) {
|
|
122
|
+
const entry = map.get(key);
|
|
123
|
+
if (entry === void 0) map.set(key, { count: 1, bytes });
|
|
124
|
+
else {
|
|
125
|
+
entry.count += 1;
|
|
126
|
+
entry.bytes += bytes;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function messageText(message) {
|
|
130
|
+
if (message === null || typeof message !== "object") return "";
|
|
131
|
+
const content = message["content"];
|
|
132
|
+
if (typeof content === "string") return content;
|
|
133
|
+
if (!Array.isArray(content)) return "";
|
|
134
|
+
let out = "";
|
|
135
|
+
for (const block of content) {
|
|
136
|
+
if (block === null || typeof block !== "object") continue;
|
|
137
|
+
const text = block["text"];
|
|
138
|
+
if (typeof text === "string") out += text;
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
function skillNameFromBody(text) {
|
|
143
|
+
const dir = /^Base directory for this skill:\s*(.+?)\s*$/m.exec(text);
|
|
144
|
+
if (dir?.[1] !== void 0) {
|
|
145
|
+
const segments = dir[1].split(/[\\/]/).filter((s) => s.length > 0);
|
|
146
|
+
const last = segments[segments.length - 1];
|
|
147
|
+
if (last !== void 0 && last.length > 0) return last;
|
|
148
|
+
}
|
|
149
|
+
const heading = /^#\s+(.+?)\s*$/m.exec(text);
|
|
150
|
+
if (heading?.[1] !== void 0 && heading[1].length > 0) return heading[1];
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
function readTaskList(attachment, bytes) {
|
|
154
|
+
const snapshot = {
|
|
155
|
+
bytes,
|
|
156
|
+
itemCount: typeof attachment["itemCount"] === "number" ? attachment["itemCount"] : 0,
|
|
157
|
+
completed: 0,
|
|
158
|
+
inProgress: 0,
|
|
159
|
+
pending: 0,
|
|
160
|
+
descriptionBytes: 0,
|
|
161
|
+
completedDescriptionBytes: 0
|
|
162
|
+
};
|
|
163
|
+
const content = attachment["content"];
|
|
164
|
+
if (!Array.isArray(content)) return snapshot;
|
|
165
|
+
if (content.length > snapshot.itemCount) snapshot.itemCount = content.length;
|
|
166
|
+
for (const item of content) {
|
|
167
|
+
if (item === null || typeof item !== "object") continue;
|
|
168
|
+
const record = item;
|
|
169
|
+
const status = typeof record["status"] === "string" ? record["status"] : "";
|
|
170
|
+
const description = typeof record["description"] === "string" ? record["description"] : "";
|
|
171
|
+
snapshot.descriptionBytes += description.length;
|
|
172
|
+
if (status === "completed") {
|
|
173
|
+
snapshot.completed += 1;
|
|
174
|
+
snapshot.completedDescriptionBytes += description.length;
|
|
175
|
+
} else if (status === "in_progress") snapshot.inProgress += 1;
|
|
176
|
+
else if (status === "pending") snapshot.pending += 1;
|
|
177
|
+
}
|
|
178
|
+
return snapshot;
|
|
179
|
+
}
|
|
180
|
+
function collectInvokedSkills(acc, record) {
|
|
181
|
+
const skills = record["skills"];
|
|
182
|
+
if (!Array.isArray(skills)) return;
|
|
183
|
+
for (const entry of skills) {
|
|
184
|
+
if (entry === null || typeof entry !== "object") continue;
|
|
185
|
+
const skill = entry;
|
|
186
|
+
const content = skill["content"];
|
|
187
|
+
if (typeof content !== "string" || content.length < LARGE_SKILL_BODY_BYTES) continue;
|
|
188
|
+
const name = skillName(skill);
|
|
189
|
+
if (name !== null) bump(acc.skillBodies, name, content.length);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function skillName(skill) {
|
|
193
|
+
const name = skill["name"];
|
|
194
|
+
if (typeof name === "string" && name.trim() !== "") return name.trim();
|
|
195
|
+
const path2 = skill["path"];
|
|
196
|
+
if (typeof path2 !== "string") return null;
|
|
197
|
+
const segments = path2.split(/[\\/]/).filter((part) => part !== "");
|
|
198
|
+
const last = segments[segments.length - 1];
|
|
199
|
+
return last === void 0 || last === "" ? null : last;
|
|
200
|
+
}
|
|
201
|
+
function accumulateResidentLine(acc, parsed, bytes) {
|
|
202
|
+
try {
|
|
203
|
+
if (parsed === null || typeof parsed !== "object") return;
|
|
204
|
+
const line = parsed;
|
|
205
|
+
if (line["type"] === "system" && line["subtype"] === "compact_boundary") acc.compactionCount += 1;
|
|
206
|
+
const attachment = line["attachment"];
|
|
207
|
+
if (attachment !== null && typeof attachment === "object") {
|
|
208
|
+
const record = attachment;
|
|
209
|
+
const type = typeof record["type"] === "string" ? record["type"] : "unknown";
|
|
210
|
+
bump(acc.attachmentsByType, type, bytes);
|
|
211
|
+
if (type === "task_reminder") {
|
|
212
|
+
acc.taskReminderCount += 1;
|
|
213
|
+
acc.taskReminderBytes += bytes;
|
|
214
|
+
acc.latestTaskList = readTaskList(record, bytes);
|
|
215
|
+
}
|
|
216
|
+
if (type === "invoked_skills") collectInvokedSkills(acc, record);
|
|
217
|
+
}
|
|
218
|
+
if (line["isMeta"] === true && line["type"] === "user") {
|
|
219
|
+
const text = messageText(line["message"]);
|
|
220
|
+
if (text.length >= LARGE_SKILL_BODY_BYTES) {
|
|
221
|
+
const skill = skillNameFromBody(text);
|
|
222
|
+
if (skill !== null) bump(acc.skillBodies, skill, text.length);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
} catch {
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function summarizeResidentContext(acc) {
|
|
229
|
+
const attachmentClasses = [...acc.attachmentsByType.entries()].map(([type, v]) => ({ type, count: v.count, bytes: v.bytes })).sort((a, b) => b.bytes - a.bytes || a.type.localeCompare(b.type));
|
|
230
|
+
const repeatedSkillBodies = [...acc.skillBodies.entries()].filter(([, v]) => v.count >= SKILL_BODY_REPEAT_THRESHOLD).map(([skill, v]) => ({ skill, count: v.count, bytes: v.bytes })).sort((a, b) => b.bytes - a.bytes || a.skill.localeCompare(b.skill));
|
|
231
|
+
return {
|
|
232
|
+
attachmentClasses,
|
|
233
|
+
totalAttachmentBytes: attachmentClasses.reduce((n, c) => n + c.bytes, 0),
|
|
234
|
+
latestTaskList: acc.latestTaskList,
|
|
235
|
+
taskReminderCount: acc.taskReminderCount,
|
|
236
|
+
taskReminderBytes: acc.taskReminderBytes,
|
|
237
|
+
repeatedSkillBodies,
|
|
238
|
+
compactionCount: acc.compactionCount
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function formatBytes(bytes) {
|
|
242
|
+
if (bytes >= 1e6) return `${(bytes / 1048576).toFixed(1)} MB`;
|
|
243
|
+
if (bytes >= 1e3) return `${Math.round(bytes / 1024)} KB`;
|
|
244
|
+
return `${bytes} B`;
|
|
245
|
+
}
|
|
246
|
+
function formatTokenEstimate(tokens) {
|
|
247
|
+
return tokens >= 1e3 ? `${Math.round(tokens / 1e3)}K` : String(tokens);
|
|
248
|
+
}
|
|
249
|
+
function taskListPruneHint(snapshot) {
|
|
250
|
+
if (snapshot === null) return null;
|
|
251
|
+
if (snapshot.bytes < LARGE_TASK_LIST_BYTES) return null;
|
|
252
|
+
if (snapshot.completed === 0) return null;
|
|
253
|
+
const tokens = formatTokenEstimate(estimateTokensFromLength(snapshot.bytes));
|
|
254
|
+
const prunable = formatBytes(snapshot.completedDescriptionBytes);
|
|
255
|
+
return `Your task list is ${formatBytes(snapshot.bytes)} (~${tokens} tok est) and is re-injected in full whenever it changes; ${snapshot.completed} of ${snapshot.itemCount} items are already completed and their descriptions alone are ${prunable}. Prune completed items or shorten their descriptions with TaskUpdate. Check ownership first if other agents share this list.`;
|
|
256
|
+
}
|
|
257
|
+
function repeatedSkillBodyHint(injections) {
|
|
258
|
+
const worst = injections[0];
|
|
259
|
+
if (worst === void 0) return null;
|
|
260
|
+
if (worst.count < SKILL_BODY_REPEAT_THRESHOLD || worst.bytes < LARGE_SKILL_BODY_BYTES) return null;
|
|
261
|
+
const skill = displaySafeText(worst.skill);
|
|
262
|
+
const tokens = formatTokenEstimate(estimateTokensFromLength(worst.bytes));
|
|
263
|
+
return `The \`${skill}\` skill body has been injected ${worst.count} times this session (${formatBytes(worst.bytes)} total, ~${tokens} tok est). Slash expansion and the Skill tool both send the whole body every time, and no hook can intercept either. If it is already loaded, work from it instead of re-invoking; to reread one part, use \`token-goat skill-section ${skill} '<heading>'\`.`;
|
|
264
|
+
}
|
|
265
|
+
function readTranscriptTail(transcriptPath, maxBytes = RESIDENT_TAIL_MAX_BYTES) {
|
|
266
|
+
let fd = null;
|
|
267
|
+
try {
|
|
268
|
+
const stat = fs.statSync(transcriptPath);
|
|
269
|
+
if (!stat.isFile() || stat.size === 0) return [];
|
|
270
|
+
const start = Math.max(0, stat.size - maxBytes);
|
|
271
|
+
const length = stat.size - start;
|
|
272
|
+
fd = fs.openSync(transcriptPath, "r");
|
|
273
|
+
const buf = Buffer.allocUnsafe(length);
|
|
274
|
+
const read = fs.readSync(fd, buf, 0, length, start);
|
|
275
|
+
const lines = buf.subarray(0, read).toString("utf8").split("\n");
|
|
276
|
+
if (start > 0) lines.shift();
|
|
277
|
+
return lines;
|
|
278
|
+
} catch {
|
|
279
|
+
return [];
|
|
280
|
+
} finally {
|
|
281
|
+
if (fd !== null) {
|
|
282
|
+
try {
|
|
283
|
+
fs.closeSync(fd);
|
|
284
|
+
} catch {
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function lineMayCarryResidentSignal(line) {
|
|
290
|
+
return line.includes('"task_reminder"') || line.includes('"compact_boundary"') || line.includes('"invoked_skills"') || line.includes('"isMeta"');
|
|
291
|
+
}
|
|
292
|
+
function accumulateResidentLines(lines) {
|
|
293
|
+
const acc = createResidentContextStats();
|
|
294
|
+
for (const line of lines) {
|
|
295
|
+
const trimmed = line.trim();
|
|
296
|
+
if (trimmed === "") continue;
|
|
297
|
+
let parsed;
|
|
298
|
+
try {
|
|
299
|
+
parsed = JSON.parse(trimmed);
|
|
300
|
+
} catch {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
accumulateResidentLine(acc, parsed, trimmed.length);
|
|
304
|
+
}
|
|
305
|
+
return acc;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export {
|
|
309
|
+
claudeConfigDir,
|
|
310
|
+
estimateTokensFromLength,
|
|
311
|
+
estimateTokens,
|
|
312
|
+
trimToBudget,
|
|
313
|
+
capJsonRows,
|
|
314
|
+
createResidentContextStats,
|
|
315
|
+
accumulateResidentLine,
|
|
316
|
+
summarizeResidentContext,
|
|
317
|
+
formatBytes,
|
|
318
|
+
formatTokenEstimate,
|
|
319
|
+
taskListPruneHint,
|
|
320
|
+
repeatedSkillBodyHint,
|
|
321
|
+
readTranscriptTail,
|
|
322
|
+
lineMayCarryResidentSignal,
|
|
323
|
+
accumulateResidentLines
|
|
324
|
+
};
|