token-goat 2.6.18 → 2.6.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.
- package/README.md +5 -1
- package/dist/token-goat-hook.mjs +636 -89
- package/dist/token-goat.mjs +637 -89
- package/package.json +1 -1
package/dist/token-goat.mjs
CHANGED
|
@@ -3050,7 +3050,7 @@ var require_commander = __commonJS({
|
|
|
3050
3050
|
import { createRequire } from "node:module";
|
|
3051
3051
|
function resolveVersion() {
|
|
3052
3052
|
if (true) {
|
|
3053
|
-
return "2.6.
|
|
3053
|
+
return "2.6.19";
|
|
3054
3054
|
}
|
|
3055
3055
|
const require2 = createRequire(import.meta.url);
|
|
3056
3056
|
const pkg = require2("../package.json");
|
|
@@ -4916,7 +4916,7 @@ function markerExists(current, marker) {
|
|
|
4916
4916
|
}
|
|
4917
4917
|
const resolved = fs3.realpathSync(markerPath);
|
|
4918
4918
|
const rel = path4.relative(path4.resolve(current), path4.resolve(resolved));
|
|
4919
|
-
return !rel.startsWith("..");
|
|
4919
|
+
return !rel.startsWith("..") && !path4.isAbsolute(rel);
|
|
4920
4920
|
} catch {
|
|
4921
4921
|
return false;
|
|
4922
4922
|
}
|
|
@@ -6220,6 +6220,33 @@ CREATE TABLE IF NOT EXISTS hint_suppression_probes (
|
|
|
6220
6220
|
PRIMARY KEY (category, harness)
|
|
6221
6221
|
);
|
|
6222
6222
|
|
|
6223
|
+
-- Free-text architecture/rationale notes (the "why" layer -- see notes.ts), attached either to
|
|
6224
|
+
-- a whole file (symbol = '') or to one specific indexed symbol within it (symbol = that
|
|
6225
|
+
-- symbol's name). '' rather than NULL for the whole-file case because SQLite's UNIQUE treats
|
|
6226
|
+
-- NULLs as pairwise-distinct (never conflicting with each other), which would let note-add
|
|
6227
|
+
-- accumulate unlimited duplicate whole-file notes for the same file instead of upserting one;
|
|
6228
|
+
-- '' is a real, comparable value so UNIQUE(file_path, symbol) enforces "at most one note per
|
|
6229
|
+
-- attachment point" for both cases identically. 'fingerprint' is a SHA-256 digest (see
|
|
6230
|
+
-- fingerprintContent in fingerprint.ts) captured at write time of exactly what the note
|
|
6231
|
+
-- describes -- the resolved symbol's current body text for a symbol-scoped note, or a stable
|
|
6232
|
+
-- digest of the file's current top-level symbol manifest (name:kind:line-range per symbol,
|
|
6233
|
+
-- sorted) for a file-scoped note -- so 'token-goat note-list --stale-only' can recompute the
|
|
6234
|
+
-- same fingerprint against the live index later and flag a mismatch (see notes.ts's
|
|
6235
|
+
-- isNoteStale). Staleness detection is purely advisory: nothing here ever auto-rewrites or
|
|
6236
|
+
-- deletes a note's content, only flags that the code it describes has moved since it was
|
|
6237
|
+
-- written -- a human/agent re-review decides what to do with a stale note.
|
|
6238
|
+
CREATE TABLE IF NOT EXISTS notes (
|
|
6239
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6240
|
+
file_path TEXT NOT NULL,
|
|
6241
|
+
symbol TEXT NOT NULL DEFAULT '',
|
|
6242
|
+
content TEXT NOT NULL,
|
|
6243
|
+
fingerprint TEXT NOT NULL,
|
|
6244
|
+
created_at REAL NOT NULL,
|
|
6245
|
+
updated_at REAL NOT NULL,
|
|
6246
|
+
UNIQUE(file_path, symbol)
|
|
6247
|
+
);
|
|
6248
|
+
CREATE INDEX IF NOT EXISTS idx_notes_file_folded ON notes(TG_LOWER(file_path));
|
|
6249
|
+
|
|
6223
6250
|
-- Baseline for skill_version_drift.ts's one-shot nudge: the token-goat CLI version (and its
|
|
6224
6251
|
-- flat command-name set, JSON-encoded) active the moment the token-goat skill's body was
|
|
6225
6252
|
-- last (re)loaded into this session -- see hooks_skill.ts's postSkillHandler. A session that
|
|
@@ -6285,7 +6312,7 @@ CREATE TRIGGER IF NOT EXISTS cache_recall_au AFTER UPDATE ON cache_recall BEGIN
|
|
|
6285
6312
|
VALUES (new.row_id, new.label, new.content);
|
|
6286
6313
|
END;
|
|
6287
6314
|
`;
|
|
6288
|
-
SCHEMA_VERSION =
|
|
6315
|
+
SCHEMA_VERSION = 8;
|
|
6289
6316
|
MIGRATIONS = {
|
|
6290
6317
|
// v1 -> v2: adds files.embed_sha, tracked separately from files.sha so embedding freshness can be gated independently of parse freshness (see makeIndexer in worker.ts). A pre-existing v1 database's `files` table predates the column, so it needs an explicit ALTER TABLE here; a brand-new database already has the column 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, so a genuine ALTER TABLE failure is never silently lost.
|
|
6291
6318
|
1: (conn) => alterTableIdempotent(conn, "ALTER TABLE files ADD COLUMN embed_sha TEXT"),
|
|
@@ -6778,6 +6805,17 @@ function slideShapes(parsedSlide) {
|
|
|
6778
6805
|
function shapeText(shape) {
|
|
6779
6806
|
return collectTextRuns(shape, "a:t").join(" ").trim();
|
|
6780
6807
|
}
|
|
6808
|
+
function tableRowBlocks(parsedSlide) {
|
|
6809
|
+
const blocks = [];
|
|
6810
|
+
for (const tbl of collectElements(parsedSlide, "a:tbl")) {
|
|
6811
|
+
for (const row of collectElements(tbl, "a:tr")) {
|
|
6812
|
+
const cellTexts = collectElements(row, "a:tc").map((cell) => collectTextRuns(cell, "a:t").join(" ").trim());
|
|
6813
|
+
const rowText = cellTexts.join(" | ").trim();
|
|
6814
|
+
if (rowText.length > 0) blocks.push(rowText);
|
|
6815
|
+
}
|
|
6816
|
+
}
|
|
6817
|
+
return blocks;
|
|
6818
|
+
}
|
|
6781
6819
|
async function slidePathsInPresentationOrder(entries) {
|
|
6782
6820
|
const presXml = decodeZipEntry(entries, "ppt/presentation.xml");
|
|
6783
6821
|
const relsXml = decodeZipEntry(entries, "ppt/_rels/presentation.xml.rels");
|
|
@@ -6877,7 +6915,7 @@ async function pptxSlideText(filePath, slideNumber, includeNotes) {
|
|
|
6877
6915
|
const path63 = slidePaths[slideNumber - 1];
|
|
6878
6916
|
const parsed = await parseSlide(entries, path63);
|
|
6879
6917
|
const shapes = slideShapes(parsed);
|
|
6880
|
-
const blocks = shapes.map(shapeText).filter((t) => t.length > 0);
|
|
6918
|
+
const blocks = [...shapes.map(shapeText).filter((t) => t.length > 0), ...tableRowBlocks(parsed)];
|
|
6881
6919
|
const lines2 = [`# Slide ${slideNumber}`, ...blocks];
|
|
6882
6920
|
if (includeNotes) {
|
|
6883
6921
|
const notes = await notesTextFor(entries, await notesPathFor(entries, path63));
|
|
@@ -9621,7 +9659,7 @@ function _renderByDaySection(stats) {
|
|
|
9621
9659
|
function share(d) {
|
|
9622
9660
|
return _tokenOrByteShare(d.tokens, d.bytes, stats.totals.tokens, stats.totals.bytes);
|
|
9623
9661
|
}
|
|
9624
|
-
for (const d of [...stats.by_day].sort((a, b) => b.date.
|
|
9662
|
+
for (const d of [...stats.by_day].sort((a, b) => b.date < a.date ? -1 : b.date > a.date ? 1 : 0)) {
|
|
9625
9663
|
const s = share(d);
|
|
9626
9664
|
lines2.push(
|
|
9627
9665
|
_tableRow({
|
|
@@ -10231,6 +10269,7 @@ var init_stats = __esm({
|
|
|
10231
10269
|
docx_text: SOURCE_READ,
|
|
10232
10270
|
transcript_outline: SOURCE_READ,
|
|
10233
10271
|
transcript: SOURCE_READ,
|
|
10272
|
+
video_chapters: SOURCE_READ,
|
|
10234
10273
|
coverage_report_gaps: SOURCE_READ,
|
|
10235
10274
|
json_query: SOURCE_READ,
|
|
10236
10275
|
json_outline: SOURCE_READ,
|
|
@@ -10248,6 +10287,14 @@ var init_stats = __esm({
|
|
|
10248
10287
|
session_slice: SOURCE_READ,
|
|
10249
10288
|
gdrive_sections: SOURCE_READ,
|
|
10250
10289
|
pr_slice: SOURCE_READ,
|
|
10290
|
+
note_read: SOURCE_READ,
|
|
10291
|
+
note_list: SOURCE_READ,
|
|
10292
|
+
// note-add is a write (like insert-section/replace, which record no stat at all -- neither
|
|
10293
|
+
// has a "full source it replaces" savings concept). It still gets an event-only entry here
|
|
10294
|
+
// (no bytesSaved/tokensSaved argument, same as skill_load) purely so `token-goat note-add`
|
|
10295
|
+
// usage is visible in `token-goat stats --full` at all -- SOURCE_OTHER, not SOURCE_READ,
|
|
10296
|
+
// since it is not a token-savings substitute for a read.
|
|
10297
|
+
note_write: SOURCE_OTHER,
|
|
10251
10298
|
web_fetch: SOURCE_WEB,
|
|
10252
10299
|
injection_detected: SOURCE_WEB,
|
|
10253
10300
|
skill_load: SOURCE_SKILL,
|
|
@@ -10292,6 +10339,7 @@ var init_stats = __esm({
|
|
|
10292
10339
|
"docx-text": /* @__PURE__ */ new Set(["docx_text"]),
|
|
10293
10340
|
"transcript-outline": /* @__PURE__ */ new Set(["transcript_outline"]),
|
|
10294
10341
|
transcript: /* @__PURE__ */ new Set(["transcript"]),
|
|
10342
|
+
"video-chapters": /* @__PURE__ */ new Set(["video_chapters"]),
|
|
10295
10343
|
"coverage-report-gaps": /* @__PURE__ */ new Set(["coverage_report_gaps"]),
|
|
10296
10344
|
"json-query": /* @__PURE__ */ new Set(["json_query"]),
|
|
10297
10345
|
"json-outline": /* @__PURE__ */ new Set(["json_outline"]),
|
|
@@ -10309,6 +10357,9 @@ var init_stats = __esm({
|
|
|
10309
10357
|
"session-slice": /* @__PURE__ */ new Set(["session_slice"]),
|
|
10310
10358
|
"gdrive-sections": /* @__PURE__ */ new Set(["gdrive_sections"]),
|
|
10311
10359
|
"pr-slice": /* @__PURE__ */ new Set(["pr_slice"]),
|
|
10360
|
+
"note-add": /* @__PURE__ */ new Set(["note_write"]),
|
|
10361
|
+
"note-get": /* @__PURE__ */ new Set(["note_read"]),
|
|
10362
|
+
"note-list": /* @__PURE__ */ new Set(["note_list"]),
|
|
10312
10363
|
npm: /* @__PURE__ */ new Set([
|
|
10313
10364
|
"bash_compress:npm_install",
|
|
10314
10365
|
"bash_compress:npm_ci",
|
|
@@ -14137,6 +14188,7 @@ function formatHeadingTree(headings, filePath) {
|
|
|
14137
14188
|
const lines2 = [];
|
|
14138
14189
|
lines2.push(`Large markdown file (${headings.length} headings). Use token-goat section to read a specific section:`);
|
|
14139
14190
|
lines2.push(` token-goat section "${filePath}::Heading Name"`);
|
|
14191
|
+
lines2.push(` Tip: an unambiguous heading prefix also resolves (e.g. "Lesson 16" instead of the full heading text) \u2014 shorter to type and avoids shell-quoting issues with punctuation in long headings.`);
|
|
14140
14192
|
lines2.push(``);
|
|
14141
14193
|
lines2.push(`Sections:`);
|
|
14142
14194
|
let headingsAdded = 0;
|
|
@@ -15176,12 +15228,12 @@ function parseSectionOrdinal(heading) {
|
|
|
15176
15228
|
const match2 = heading.match(/^(.*?)(?:#(\d+))?$/);
|
|
15177
15229
|
if (!match2) return [heading, 1];
|
|
15178
15230
|
const baseHeading = match2[1] || heading;
|
|
15179
|
-
const
|
|
15180
|
-
return [baseHeading,
|
|
15231
|
+
const ordinal2 = match2[2] ? Math.max(1, parseInt(match2[2], 10)) : 1;
|
|
15232
|
+
return [baseHeading, ordinal2];
|
|
15181
15233
|
}
|
|
15182
15234
|
function extractNamedSection(body, heading) {
|
|
15183
15235
|
if (!body || !heading) return null;
|
|
15184
|
-
const [baseHeading,
|
|
15236
|
+
const [baseHeading, ordinal2] = parseSectionOrdinal(heading);
|
|
15185
15237
|
const headingLower = stripLower(baseHeading);
|
|
15186
15238
|
const lines2 = body.split("\n");
|
|
15187
15239
|
let matchCount = 0;
|
|
@@ -15191,7 +15243,7 @@ function extractNamedSection(body, heading) {
|
|
|
15191
15243
|
const headingText = stripLower(stripped.slice(3));
|
|
15192
15244
|
if (headingText === headingLower) {
|
|
15193
15245
|
matchCount++;
|
|
15194
|
-
if (matchCount ===
|
|
15246
|
+
if (matchCount === ordinal2) {
|
|
15195
15247
|
startIdx = i + 1;
|
|
15196
15248
|
break;
|
|
15197
15249
|
}
|
|
@@ -16265,6 +16317,11 @@ function isNodeModulesPath(p) {
|
|
|
16265
16317
|
const check2 = foldPath(p);
|
|
16266
16318
|
return check2.includes("/node_modules/") || check2.includes("\\node_modules\\");
|
|
16267
16319
|
}
|
|
16320
|
+
function relPathWithinRoot(root, target) {
|
|
16321
|
+
const rel = path21.relative(root, target).replace(/\\/g, "/");
|
|
16322
|
+
if (rel.startsWith("..") || path21.isAbsolute(rel)) return null;
|
|
16323
|
+
return rel;
|
|
16324
|
+
}
|
|
16268
16325
|
function _isDocFile(filePath) {
|
|
16269
16326
|
const lower = filePath.toLowerCase();
|
|
16270
16327
|
return lower.endsWith(".md") || lower.endsWith(".mdx") || lower.endsWith(".markdown") || lower.endsWith(".rst");
|
|
@@ -16429,7 +16486,8 @@ function loadSnapshotDiff(sessionId, normalized, basename19) {
|
|
|
16429
16486
|
}
|
|
16430
16487
|
function scanCrossSessionManifests(projectRoot, projectHash2, filePath, ttlSecs) {
|
|
16431
16488
|
try {
|
|
16432
|
-
const relPath =
|
|
16489
|
+
const relPath = relPathWithinRoot(projectRoot, filePath);
|
|
16490
|
+
if (relPath === null) return false;
|
|
16433
16491
|
const foldedRelPath = foldPath(relPath);
|
|
16434
16492
|
const manifests = readAllSessionManifests(projectHash2, ttlSecs);
|
|
16435
16493
|
for (const data of manifests) {
|
|
@@ -16473,7 +16531,7 @@ function isProtectedRecentRead(normalized, n) {
|
|
|
16473
16531
|
if (n <= 0) return false;
|
|
16474
16532
|
const ranked = Array.from(getSessionFiles().entries()).sort((a, b) => {
|
|
16475
16533
|
const byRecency = b[1].lastReadAt - a[1].lastReadAt;
|
|
16476
|
-
return byRecency !== 0 ? byRecency : a[0]
|
|
16534
|
+
return byRecency !== 0 ? byRecency : a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0;
|
|
16477
16535
|
});
|
|
16478
16536
|
const rank = ranked.findIndex(([filePath]) => filePath === normalized);
|
|
16479
16537
|
return rank !== -1 && rank < n;
|
|
@@ -16717,8 +16775,7 @@ function preReadHandlerInner(event) {
|
|
|
16717
16775
|
if (!project) {
|
|
16718
16776
|
project = makeProjectAt(cwd);
|
|
16719
16777
|
}
|
|
16720
|
-
|
|
16721
|
-
if (!relPath.startsWith("..")) {
|
|
16778
|
+
if (relPathWithinRoot(project.root, normalized) !== null) {
|
|
16722
16779
|
const ttlSecs = config2.hints.cross_session_read_dedup_ttl_secs;
|
|
16723
16780
|
if (scanCrossSessionManifests(project.root, project.hash, normalized, ttlSecs)) {
|
|
16724
16781
|
recordActualRead(event, normalized);
|
|
@@ -16855,7 +16912,7 @@ function estimateTruncatedLineCount(normalized) {
|
|
|
16855
16912
|
return Infinity;
|
|
16856
16913
|
}
|
|
16857
16914
|
function editAnywayHint(normalized) {
|
|
16858
|
-
return 'To edit it anyway, use `token-goat replace "' + normalized + '" --old-from <oldfile> --new-from <newfile>` for a snippet edit, or `token-goat write-file "' + normalized + "\" --from <newfile>` to rewrite the whole file \u2014 Read/Edit's own precondition can't be satisfied after this deny.";
|
|
16915
|
+
return 'To edit it anyway, use `token-goat replace "' + normalized + '" --old-b64 <base64> --new-b64 <base64>` (preferred \u2014 no temp files needed) or `--old-from <oldfile> --new-from <newfile>` for a snippet edit, or `token-goat write-file "' + normalized + "\" --b64 <base64>` (or `--from <newfile>`) to rewrite the whole file \u2014 Read/Edit's own precondition can't be satisfied after this deny.";
|
|
16859
16916
|
}
|
|
16860
16917
|
function truncatedReadDenyMessage(normalized) {
|
|
16861
16918
|
return 'File was truncated on last read (>33K tokens). Use `token-goat skeleton "' + normalized + '"` for structure or `token-goat read "' + normalized + '::SymbolName"` for one function. ' + editAnywayHint(normalized);
|
|
@@ -16890,8 +16947,8 @@ function postReadHandlerInner(event) {
|
|
|
16890
16947
|
const sessionState = exportSessionState();
|
|
16891
16948
|
const mappedFiles = [];
|
|
16892
16949
|
for (const fileEntry of sessionState.files) {
|
|
16893
|
-
const relPath =
|
|
16894
|
-
if (
|
|
16950
|
+
const relPath = relPathWithinRoot(project.root, fileEntry.path);
|
|
16951
|
+
if (relPath !== null) {
|
|
16895
16952
|
mappedFiles.push({
|
|
16896
16953
|
rel_path: relPath,
|
|
16897
16954
|
hit_count: fileEntry.readCount
|
|
@@ -18616,9 +18673,6 @@ function extractPhp(content, filePath) {
|
|
|
18616
18673
|
let braceDepth = 0;
|
|
18617
18674
|
let inComment = false;
|
|
18618
18675
|
let mlState = null;
|
|
18619
|
-
function currentClass() {
|
|
18620
|
-
return contextStack.length > 0 ? contextStack[contextStack.length - 1]?.[0] ?? null : null;
|
|
18621
|
-
}
|
|
18622
18676
|
for (let i = 0; i < lines2.length; i++) {
|
|
18623
18677
|
const rawLine = lines2[i] ?? "";
|
|
18624
18678
|
const lineNum = i + 1;
|
|
@@ -18682,7 +18736,9 @@ function extractPhp(content, filePath) {
|
|
|
18682
18736
|
if (clsM) {
|
|
18683
18737
|
const kind = clsM[1] ?? "class";
|
|
18684
18738
|
const name2 = clsM[2] ?? "";
|
|
18685
|
-
const
|
|
18739
|
+
const preLineDepth = braceDepth - openB + closeB;
|
|
18740
|
+
const topFrame2 = contextStack.length > 0 ? contextStack[contextStack.length - 1] : void 0;
|
|
18741
|
+
const parent = topFrame2 !== void 0 && preLineDepth === topFrame2[1] + 1 ? topFrame2[0] : null;
|
|
18686
18742
|
symbols.push(makeLineSymbol(filePath, name2, kind, lineNum, stripped.slice(0, 200), parent ?? void 0));
|
|
18687
18743
|
contextStack.push([name2, braceDepth - openB + closeB, false]);
|
|
18688
18744
|
if (openB > 0 && openB === closeB) {
|
|
@@ -19280,6 +19336,9 @@ function extractScala(content, filePath) {
|
|
|
19280
19336
|
const parent = typeStack.length > 0 ? typeStack[typeStack.length - 1].name : void 0;
|
|
19281
19337
|
symbols.push(makeLineSymbol(filePath, cname, "class", lineNum, stripped.slice(0, 200), parent));
|
|
19282
19338
|
typeStack.push({ name: cname, startDepth: braceDepth, bodyEntered: false });
|
|
19339
|
+
if (/\bcase\s+class\b/.test(stripped) && !stripStringLiterals(line).includes("{")) {
|
|
19340
|
+
typeStack.pop();
|
|
19341
|
+
}
|
|
19283
19342
|
matched = true;
|
|
19284
19343
|
}
|
|
19285
19344
|
const om = !matched && typeDetectionGateOk && (!isIndented || typeStack.length > 0) ? OBJECT_RE.exec(stripped) : null;
|
|
@@ -19288,6 +19347,9 @@ function extractScala(content, filePath) {
|
|
|
19288
19347
|
const parent = typeStack.length > 0 ? typeStack[typeStack.length - 1].name : void 0;
|
|
19289
19348
|
symbols.push(makeLineSymbol(filePath, oname, "object", lineNum, stripped.slice(0, 200), parent));
|
|
19290
19349
|
typeStack.push({ name: oname, startDepth: braceDepth, bodyEntered: false });
|
|
19350
|
+
if (/\bcase\s+object\b/.test(stripped) && !stripStringLiterals(line).includes("{")) {
|
|
19351
|
+
typeStack.pop();
|
|
19352
|
+
}
|
|
19291
19353
|
matched = true;
|
|
19292
19354
|
}
|
|
19293
19355
|
const tm = !matched && typeDetectionGateOk && (!isIndented || typeStack.length > 0) ? TRAIT_RE.exec(stripped) : null;
|
|
@@ -19375,13 +19437,13 @@ var init_scala = __esm({
|
|
|
19375
19437
|
init_common();
|
|
19376
19438
|
IMPORT_RE3 = /^import\s+([A-Za-z_][A-Za-z0-9_.]*(?:\._)?)/;
|
|
19377
19439
|
BRACE_IMPORT_RE = /^import\s+([A-Za-z_][A-Za-z0-9_.]*)\.\{([^}]*)\}/;
|
|
19378
|
-
CLASS_RE3 = /^\s*(?:implicit|lazy|sealed|abstract|final|private|protected|override|covariant|contravariant|case)
|
|
19379
|
-
OBJECT_RE = /^\s*(?:implicit|lazy|sealed|abstract|final|private|protected|override|covariant|contravariant|case)
|
|
19380
|
-
TRAIT_RE = /^\s*(?:implicit|lazy|sealed|abstract|final|private|protected|override|covariant|contravariant|case)
|
|
19440
|
+
CLASS_RE3 = /^\s*(?:(?:implicit|lazy|sealed|abstract|final|private|protected|override|covariant|contravariant|case)\s+)*class\s+([A-Za-z_][A-Za-z0-9_]*)(?:\s|\[|\(|:|$)/;
|
|
19441
|
+
OBJECT_RE = /^\s*(?:(?:implicit|lazy|sealed|abstract|final|private|protected|override|covariant|contravariant|case)\s+)*object\s+([A-Za-z_][A-Za-z0-9_]*)(?:\s|:|$)/;
|
|
19442
|
+
TRAIT_RE = /^\s*(?:(?:implicit|lazy|sealed|abstract|final|private|protected|override|covariant|contravariant|case)\s+)*trait\s+([A-Za-z_][A-Za-z0-9_]*)(?:\s|\[|:|$)/;
|
|
19381
19443
|
ENUM_RE = /^\s*(?:private|protected)?\s*enum\s+([A-Za-z_][A-Za-z0-9_]*)(?:\s|\[|\(|:|$)/;
|
|
19382
|
-
FUNC_RE2 = /^\s*(?:implicit|lazy|sealed|abstract|final|private|protected|override|covariant|contravariant|case)
|
|
19383
|
-
VAL_RE = /^\s*(?:implicit|lazy|sealed|abstract|final|private|protected|override|covariant|contravariant|case)
|
|
19384
|
-
VAR_RE = /^\s*(?:implicit|lazy|sealed|abstract|final|private|protected|override|covariant|contravariant|case)
|
|
19444
|
+
FUNC_RE2 = /^\s*(?:(?:implicit|lazy|sealed|abstract|final|private|protected|override|covariant|contravariant|case)\s+)*def\s+([A-Za-z_][A-Za-z0-9_]*|[+\-*/%=!<>&|^~]+)(?:\s*\[|\s*\(|\s*:)/;
|
|
19445
|
+
VAL_RE = /^\s*(?:(?:implicit|lazy|sealed|abstract|final|private|protected|override|covariant|contravariant|case)\s+)*val\s+([A-Za-z_][A-Za-z0-9_]*)/;
|
|
19446
|
+
VAR_RE = /^\s*(?:(?:implicit|lazy|sealed|abstract|final|private|protected|override|covariant|contravariant|case)\s+)*var\s+([A-Za-z_][A-Za-z0-9_]*)/;
|
|
19385
19447
|
}
|
|
19386
19448
|
});
|
|
19387
19449
|
|
|
@@ -19471,8 +19533,9 @@ function extractLua(content, filePath) {
|
|
|
19471
19533
|
funcStack.push({ name: "", endKeywordNeeded: true, isBlock: true });
|
|
19472
19534
|
continue;
|
|
19473
19535
|
}
|
|
19474
|
-
if (
|
|
19475
|
-
|
|
19536
|
+
if (/^(?:end(?:\s+|$))+$/.test(stripped)) {
|
|
19537
|
+
const popCount = (stripped.match(/\bend\b/g) ?? []).length;
|
|
19538
|
+
for (let k = 0; k < popCount && funcStack.length > 0; k++) {
|
|
19476
19539
|
funcStack.pop();
|
|
19477
19540
|
}
|
|
19478
19541
|
}
|
|
@@ -19515,6 +19578,7 @@ function extractElixir(content, filePath) {
|
|
|
19515
19578
|
continue;
|
|
19516
19579
|
}
|
|
19517
19580
|
const opensDoBlock = /\bdo\s*$/.test(stripped);
|
|
19581
|
+
const opensFnBlock = /\bfn\b/.test(stripped) && !/\bend\b/.test(stripped) && /(?:fn|->)\s*$/.test(stripped);
|
|
19518
19582
|
const modM = MODULE_RE.exec(stripped);
|
|
19519
19583
|
if (modM) {
|
|
19520
19584
|
const modName = modM[1] ?? "";
|
|
@@ -19564,7 +19628,7 @@ function extractElixir(content, filePath) {
|
|
|
19564
19628
|
}
|
|
19565
19629
|
continue;
|
|
19566
19630
|
}
|
|
19567
|
-
if (opensDoBlock) {
|
|
19631
|
+
if (opensDoBlock || opensFnBlock) {
|
|
19568
19632
|
moduleStack.push({ name: "", endKeywordNeeded: true, isBlock: true });
|
|
19569
19633
|
continue;
|
|
19570
19634
|
}
|
|
@@ -19640,6 +19704,14 @@ function extractDart(content, filePath) {
|
|
|
19640
19704
|
typeStack.push({ name: mname, startDepth: braceDepth, bodyEntered: false });
|
|
19641
19705
|
matched = true;
|
|
19642
19706
|
}
|
|
19707
|
+
const etm = !matched ? EXTENSION_TYPE_RE.exec(stripped) : null;
|
|
19708
|
+
if (etm) {
|
|
19709
|
+
const etname = etm[1] ?? "";
|
|
19710
|
+
const parent = typeStack.length > 0 ? typeStack[typeStack.length - 1].name : void 0;
|
|
19711
|
+
symbols.push(makeLineSymbol(filePath, etname, "extension_type", lineNum, stripped.slice(0, 200), parent));
|
|
19712
|
+
typeStack.push({ name: etname, startDepth: braceDepth, bodyEntered: false });
|
|
19713
|
+
matched = true;
|
|
19714
|
+
}
|
|
19643
19715
|
const extm = !matched ? EXTENSION_RE.exec(stripped) : null;
|
|
19644
19716
|
if (extm) {
|
|
19645
19717
|
const extname14 = extm[1] ?? "extension";
|
|
@@ -19690,7 +19762,7 @@ function extractDart(content, filePath) {
|
|
|
19690
19762
|
}
|
|
19691
19763
|
return { symbols, imports };
|
|
19692
19764
|
}
|
|
19693
|
-
var CLASS_RE4, ENUM_RE2, MIXIN_RE, EXTENSION_RE, FUNC_RE5;
|
|
19765
|
+
var CLASS_RE4, ENUM_RE2, MIXIN_RE, EXTENSION_RE, EXTENSION_TYPE_RE, FUNC_RE5;
|
|
19694
19766
|
var init_dart = __esm({
|
|
19695
19767
|
"src/languages/dart.ts"() {
|
|
19696
19768
|
"use strict";
|
|
@@ -19700,6 +19772,7 @@ var init_dart = __esm({
|
|
|
19700
19772
|
ENUM_RE2 = /^enum\s+([A-Za-z_][A-Za-z0-9_]*)/;
|
|
19701
19773
|
MIXIN_RE = /^(?:base\s+)?mixin\s+([A-Za-z_][A-Za-z0-9_]*)/;
|
|
19702
19774
|
EXTENSION_RE = /^extension\s+(?:([A-Za-z_][A-Za-z0-9_]*)\s+)?on\s+/;
|
|
19775
|
+
EXTENSION_TYPE_RE = /^extension\s+type\s+([A-Za-z_][A-Za-z0-9_]*)/;
|
|
19703
19776
|
FUNC_RE5 = /(?:^|\s)(?:static\s+)?(?:(?:void|Future|Stream|async|external)\s+|[A-Za-z_][A-Za-z0-9_<>]*(?:\s*\?)?\s+)([A-Za-z_][A-Za-z0-9_]*)\s*(?:<[^>]*>)?\s*\(/;
|
|
19704
19777
|
}
|
|
19705
19778
|
});
|
|
@@ -19726,33 +19799,39 @@ function extractZig(content, filePath) {
|
|
|
19726
19799
|
}
|
|
19727
19800
|
const isIndented = line[0] === " " || line[0] === " ";
|
|
19728
19801
|
let matched = false;
|
|
19729
|
-
|
|
19802
|
+
const outerFrame = scopeStack.length > 0 ? scopeStack[scopeStack.length - 1] : null;
|
|
19803
|
+
const outerDepthInType = outerFrame !== null ? braceDepth - outerFrame.startDepth : 0;
|
|
19804
|
+
const typeDetectionGateOk = scopeStack.length === 0 || outerDepthInType === 1;
|
|
19805
|
+
if (typeDetectionGateOk && (!isIndented || scopeStack.length > 0)) {
|
|
19730
19806
|
const sm = CONTAINER_RE.exec(stripped);
|
|
19731
19807
|
if (sm) {
|
|
19732
19808
|
const sname = sm[1] ?? "";
|
|
19733
19809
|
const skind = sm[2] ?? "struct";
|
|
19734
19810
|
if (sname) {
|
|
19735
|
-
const parent =
|
|
19811
|
+
const parent = outerFrame !== null ? outerFrame.name : void 0;
|
|
19736
19812
|
symbols.push(makeLineSymbol(filePath, sname, skind, lineNum, stripped.slice(0, 200), parent));
|
|
19737
19813
|
scopeStack.push({ name: sname, startDepth: braceDepth, bodyEntered: false });
|
|
19738
19814
|
matched = true;
|
|
19739
19815
|
}
|
|
19740
19816
|
}
|
|
19741
19817
|
}
|
|
19742
|
-
|
|
19818
|
+
const frame = scopeStack.length > 0 ? scopeStack[scopeStack.length - 1] : null;
|
|
19819
|
+
if (!matched && !isIndented && frame === null) {
|
|
19743
19820
|
const fm = FUNC_RE6.exec(stripped);
|
|
19744
19821
|
if (fm) {
|
|
19745
19822
|
const fname = fm[2] ?? "";
|
|
19746
19823
|
symbols.push(makeLineSymbol(filePath, fname, "function", lineNum, stripped.slice(0, 200)));
|
|
19747
19824
|
matched = true;
|
|
19748
19825
|
}
|
|
19749
|
-
} else if (!matched &&
|
|
19750
|
-
const
|
|
19751
|
-
if (
|
|
19752
|
-
const
|
|
19753
|
-
|
|
19754
|
-
|
|
19755
|
-
|
|
19826
|
+
} else if (!matched && frame !== null) {
|
|
19827
|
+
const depthInType = braceDepth - frame.startDepth;
|
|
19828
|
+
if (depthInType === 1) {
|
|
19829
|
+
const fm = FUNC_RE6.exec(stripped);
|
|
19830
|
+
if (fm) {
|
|
19831
|
+
const fname = fm[2] ?? "";
|
|
19832
|
+
symbols.push(makeLineSymbol(filePath, fname, "function", lineNum, stripped.slice(0, 200), frame.name));
|
|
19833
|
+
matched = true;
|
|
19834
|
+
}
|
|
19756
19835
|
}
|
|
19757
19836
|
}
|
|
19758
19837
|
if (!matched && !isIndented) {
|
|
@@ -19773,9 +19852,9 @@ function extractZig(content, filePath) {
|
|
|
19773
19852
|
if (ch === "{") {
|
|
19774
19853
|
braceDepth++;
|
|
19775
19854
|
if (scopeStack.length > 0) {
|
|
19776
|
-
const
|
|
19777
|
-
if (braceDepth >
|
|
19778
|
-
|
|
19855
|
+
const frame2 = scopeStack[scopeStack.length - 1];
|
|
19856
|
+
if (braceDepth > frame2.startDepth) {
|
|
19857
|
+
frame2.bodyEntered = true;
|
|
19779
19858
|
}
|
|
19780
19859
|
}
|
|
19781
19860
|
} else if (ch === "}") {
|
|
@@ -23190,7 +23269,18 @@ var init_parser = __esm({
|
|
|
23190
23269
|
// `visit` already descends into `interface_body`/class bodies to find them.
|
|
23191
23270
|
["method_signature", "method"],
|
|
23192
23271
|
["property_signature", "var"],
|
|
23193
|
-
["abstract_method_signature", "method"]
|
|
23272
|
+
["abstract_method_signature", "method"],
|
|
23273
|
+
// `namespace Foo { ... }` (and the legacy `module Foo { ... }` synonym) parses as
|
|
23274
|
+
// `internal_module`; `declare module "some-string" { ... }` (an ambient module declaration,
|
|
23275
|
+
// common in .d.ts files) parses as `module` -- a distinct node type from either. Neither had a
|
|
23276
|
+
// kind-map entry, so the namespace/module declaration itself was silently invisible to
|
|
23277
|
+
// `symbol`/`outline`/`skeleton`/`read`, even though everything nested inside it still indexed
|
|
23278
|
+
// fine (the walk recurses into every node's children regardless of the parent's kind-map
|
|
23279
|
+
// membership) -- the same container-drop shape already fixed for C++ `namespace_definition`
|
|
23280
|
+
// and Rust `mod_item`. Both node types expose their name on the standard `name` field
|
|
23281
|
+
// (an identifier, nested_identifier, or string), so `nodeName` resolves it without special-casing.
|
|
23282
|
+
["internal_module", "namespace"],
|
|
23283
|
+
["module", "namespace"]
|
|
23194
23284
|
]);
|
|
23195
23285
|
TSJS_FN_SCOPE_TYPES = /* @__PURE__ */ new Set([
|
|
23196
23286
|
"function_declaration",
|
|
@@ -24226,6 +24316,7 @@ function workerHealthCheckMarkerPath(dir) {
|
|
|
24226
24316
|
return path33.join(dir, "worker-healthcheck.marker");
|
|
24227
24317
|
}
|
|
24228
24318
|
function ensureWorkerAlive(dir = dataDir()) {
|
|
24319
|
+
if (process.env["TOKEN_GOAT_NO_WORKER_SPAWN"] === "1") return;
|
|
24229
24320
|
const markerPath = workerHealthCheckMarkerPath(dir);
|
|
24230
24321
|
try {
|
|
24231
24322
|
const stat2 = fs27.statSync(markerPath);
|
|
@@ -24337,6 +24428,7 @@ async function runWorkerLoop(dir, pollIntervalMs, shouldStop = () => false) {
|
|
|
24337
24428
|
let lastSnapshotCleanupMs = 0;
|
|
24338
24429
|
let lastKnownRootsSweepMs = 0;
|
|
24339
24430
|
while (!shouldStop()) {
|
|
24431
|
+
if (!fs27.existsSync(dir)) break;
|
|
24340
24432
|
try {
|
|
24341
24433
|
drainOnce(dir);
|
|
24342
24434
|
} catch {
|
|
@@ -24657,7 +24749,7 @@ function tableSectionEndIndex(headers, headerPos, totalLines) {
|
|
|
24657
24749
|
}
|
|
24658
24750
|
return totalLines;
|
|
24659
24751
|
}
|
|
24660
|
-
function resolveHeaderPos(headers, base,
|
|
24752
|
+
function resolveHeaderPos(headers, base, ordinal2) {
|
|
24661
24753
|
const target = base.toLowerCase();
|
|
24662
24754
|
const normalizedTarget = normalizeHeading(base).toLowerCase();
|
|
24663
24755
|
const strippedTarget = normalizeHeadingStrip(base).toLowerCase();
|
|
@@ -24678,12 +24770,12 @@ function resolveHeaderPos(headers, base, ordinal) {
|
|
|
24678
24770
|
}
|
|
24679
24771
|
const matches2 = exactMatches.length > 0 ? exactMatches : normalizedMatches.length > 0 ? normalizedMatches : strippedMatches;
|
|
24680
24772
|
if (matches2.length > 0) {
|
|
24681
|
-
const pick3 =
|
|
24773
|
+
const pick3 = ordinal2 === null ? 0 : ordinal2 - 1;
|
|
24682
24774
|
const headerPos = matches2[pick3];
|
|
24683
24775
|
if (headerPos === void 0) return null;
|
|
24684
24776
|
return { headerPos, redirectedFrom: null };
|
|
24685
24777
|
}
|
|
24686
|
-
if (
|
|
24778
|
+
if (ordinal2 !== null || normalizedTarget.length === 0) return null;
|
|
24687
24779
|
let prefixPos = -1;
|
|
24688
24780
|
const distinct = /* @__PURE__ */ new Set();
|
|
24689
24781
|
for (let i = 0; i < headers.length; i++) {
|
|
@@ -24719,10 +24811,10 @@ function buildSectionResult(headers, kind, lines2, headerPos, redirectedFrom) {
|
|
|
24719
24811
|
}
|
|
24720
24812
|
function resolveSectionFromText(text, headingSpec, language) {
|
|
24721
24813
|
const { headers, kind } = findHeaders(text, language);
|
|
24722
|
-
const { base, ordinal } = parseHeadingSpec(headingSpec, headers);
|
|
24814
|
+
const { base, ordinal: ordinal2 } = parseHeadingSpec(headingSpec, headers);
|
|
24723
24815
|
if (base.length === 0) return null;
|
|
24724
24816
|
const lines2 = text.split("\n");
|
|
24725
|
-
const resolved = resolveHeaderPos(headers, base,
|
|
24817
|
+
const resolved = resolveHeaderPos(headers, base, ordinal2);
|
|
24726
24818
|
if (resolved === null) return null;
|
|
24727
24819
|
return buildSectionResult(headers, kind, lines2, resolved.headerPos, resolved.redirectedFrom);
|
|
24728
24820
|
}
|
|
@@ -25669,6 +25761,11 @@ var init_graph_commands = __esm({
|
|
|
25669
25761
|
"opaque",
|
|
25670
25762
|
"mixin",
|
|
25671
25763
|
"extension",
|
|
25764
|
+
// 'extension_type' (Dart 3.3's zero-cost wrapper type, `extension type Meters(int value)`) is
|
|
25765
|
+
// a distinct declaration kind from a plain 'extension' -- languages/dart.ts's
|
|
25766
|
+
// EXTENSION_TYPE_RE emits it via the same makeLineSymbol code path as 'class'/'mixin'/
|
|
25767
|
+
// 'extension' above, so it needs the same TYPE_KINDS entry those already have.
|
|
25768
|
+
"extension_type",
|
|
25672
25769
|
"actor",
|
|
25673
25770
|
// Protocol Buffers (languages/proto_idx.ts's KIND_MAP) uses its own kind strings rather
|
|
25674
25771
|
// than the generic 'enum'/'interface' -- a proto message is a struct-shaped type, a proto
|
|
@@ -25710,7 +25807,26 @@ var init_graph_commands = __esm({
|
|
|
25710
25807
|
// excluded from `token-goat types` in its entirety, the same class of gap already fixed for
|
|
25711
25808
|
// Rust union, Swift protocol/actor, Zig opaque, Dart mixin/extension, proto message/enum/
|
|
25712
25809
|
// service, Apex class/interface/enum, and GraphQL type/interface/input/enum/union.
|
|
25713
|
-
"object"
|
|
25810
|
+
"object",
|
|
25811
|
+
// languages/sfc_idx.ts (Vue/Svelte/Astro single-file components) emits 'sfc_script_class' for
|
|
25812
|
+
// a top-level class declaration in the component's script block, a distinct kind string rather
|
|
25813
|
+
// than the generic 'class' -- so unlike a plain class it never reaches the looksLikeTypeClass
|
|
25814
|
+
// fallback below either, which only ever queries kind === 'class' literally. Every SFC
|
|
25815
|
+
// top-level class was indexed but silently excluded from `token-goat types` in its entirety,
|
|
25816
|
+
// the same class of gap already fixed for Rust union, Swift protocol/actor, Zig opaque, Dart
|
|
25817
|
+
// mixin/extension, proto message/enum/service, Apex class/interface/enum, GraphQL
|
|
25818
|
+
// type/interface/input/enum/union, and Kotlin/Scala object.
|
|
25819
|
+
"sfc_script_class",
|
|
25820
|
+
// languages/graphql_idx.ts's TYPE_RE handler maps EVERY `extend type|interface|input|enum|
|
|
25821
|
+
// union|scalar Foo { ... }` declaration to this one shared kind regardless of which keyword
|
|
25822
|
+
// follows `extend` -- GraphQL's mechanism for adding fields to a type from another file/module,
|
|
25823
|
+
// ubiquitous in federation and schema-stitching -- but unlike the six non-extend KIND_MAP
|
|
25824
|
+
// entries already listed above, this kind was never added here, so every `extend` declaration
|
|
25825
|
+
// was indexed but silently excluded from `token-goat types` in its entirety, the same class of
|
|
25826
|
+
// gap already fixed for the other six GraphQL kinds, Rust union, Swift protocol/actor, Zig
|
|
25827
|
+
// opaque, Dart mixin/extension, proto message/enum/service, Apex class/interface/enum, and
|
|
25828
|
+
// Kotlin/Scala object.
|
|
25829
|
+
"graphql_extend"
|
|
25714
25830
|
];
|
|
25715
25831
|
MAX_CYCLES = 200;
|
|
25716
25832
|
}
|
|
@@ -28153,7 +28269,8 @@ function extractOperations(spec) {
|
|
|
28153
28269
|
});
|
|
28154
28270
|
}
|
|
28155
28271
|
}
|
|
28156
|
-
|
|
28272
|
+
const ordinal2 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
28273
|
+
operations.sort((a, b) => ordinal2(a.path, b.path) || ordinal2(a.method, b.method));
|
|
28157
28274
|
return operations;
|
|
28158
28275
|
}
|
|
28159
28276
|
function formatOpenApiOutline(operations) {
|
|
@@ -28241,7 +28358,7 @@ function listZipEntries(data) {
|
|
|
28241
28358
|
return false;
|
|
28242
28359
|
}
|
|
28243
28360
|
});
|
|
28244
|
-
entries.sort((a, b) => a.path.
|
|
28361
|
+
entries.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
28245
28362
|
return entries;
|
|
28246
28363
|
}
|
|
28247
28364
|
function formatZipList(entries) {
|
|
@@ -28724,7 +28841,11 @@ function hasGap(f) {
|
|
|
28724
28841
|
}
|
|
28725
28842
|
function rankAndFilter(files) {
|
|
28726
28843
|
const withGaps = files.filter(hasGap);
|
|
28727
|
-
withGaps.sort((a, b) =>
|
|
28844
|
+
withGaps.sort((a, b) => {
|
|
28845
|
+
const byCount = b.uncoveredLineCount - a.uncoveredLineCount;
|
|
28846
|
+
if (byCount !== 0) return byCount;
|
|
28847
|
+
return a.filePath < b.filePath ? -1 : a.filePath > b.filePath ? 1 : 0;
|
|
28848
|
+
});
|
|
28728
28849
|
return withGaps;
|
|
28729
28850
|
}
|
|
28730
28851
|
function detectCoverageFormat(text) {
|
|
@@ -29272,6 +29393,83 @@ var init_screenshot = __esm({
|
|
|
29272
29393
|
}
|
|
29273
29394
|
});
|
|
29274
29395
|
|
|
29396
|
+
// src/notes.ts
|
|
29397
|
+
function toNoteRow(row) {
|
|
29398
|
+
return {
|
|
29399
|
+
id: row.id,
|
|
29400
|
+
filePath: row.file_path,
|
|
29401
|
+
symbol: row.symbol,
|
|
29402
|
+
content: row.content,
|
|
29403
|
+
fingerprint: row.fingerprint,
|
|
29404
|
+
createdAt: row.created_at,
|
|
29405
|
+
updatedAt: row.updated_at
|
|
29406
|
+
};
|
|
29407
|
+
}
|
|
29408
|
+
function pickEarliest(matches2) {
|
|
29409
|
+
return matches2.reduce((best, s) => s.lineStart < best.lineStart ? s : best);
|
|
29410
|
+
}
|
|
29411
|
+
function resolveSymbolMatch(filePath, symbolName, dbPath = globalDbPath()) {
|
|
29412
|
+
const matches2 = querySymbols({ filePath, name: symbolName }, dbPath);
|
|
29413
|
+
return matches2.length === 0 ? null : pickEarliest(matches2);
|
|
29414
|
+
}
|
|
29415
|
+
function symbolNamesInFile(filePath, dbPath = globalDbPath()) {
|
|
29416
|
+
const symbols = querySymbols({ filePath, limit: 1e5 }, dbPath);
|
|
29417
|
+
return [...new Set(symbols.map((s) => s.name))].sort();
|
|
29418
|
+
}
|
|
29419
|
+
function computeFileFingerprint(filePath, dbPath = globalDbPath()) {
|
|
29420
|
+
const symbols = querySymbols({ filePath, limit: 1e6 }, dbPath);
|
|
29421
|
+
const manifest = symbols.map((s) => `${s.name}:${s.kind}:${s.lineStart}-${s.lineEnd}`).sort().join("\n");
|
|
29422
|
+
return fingerprintContent(manifest);
|
|
29423
|
+
}
|
|
29424
|
+
function computeSymbolFingerprint(filePath, symbolName, dbPath = globalDbPath()) {
|
|
29425
|
+
const match2 = resolveSymbolMatch(filePath, symbolName, dbPath);
|
|
29426
|
+
return match2 === null ? null : fingerprintContent(match2.body);
|
|
29427
|
+
}
|
|
29428
|
+
function upsertNote(filePath, symbol3, content, fingerprint, dbPath = globalDbPath()) {
|
|
29429
|
+
const db = getDb(dbPath);
|
|
29430
|
+
const now = Date.now() / 1e3;
|
|
29431
|
+
db.prepare(
|
|
29432
|
+
`INSERT INTO notes (file_path, symbol, content, fingerprint, created_at, updated_at)
|
|
29433
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
29434
|
+
ON CONFLICT(file_path, symbol) DO UPDATE SET
|
|
29435
|
+
content = excluded.content,
|
|
29436
|
+
fingerprint = excluded.fingerprint,
|
|
29437
|
+
updated_at = excluded.updated_at`
|
|
29438
|
+
).run(filePath, symbol3, content, fingerprint, now, now);
|
|
29439
|
+
}
|
|
29440
|
+
function getNote(filePath, symbol3, dbPath = globalDbPath()) {
|
|
29441
|
+
const db = getDb(dbPath);
|
|
29442
|
+
const row = db.prepare(
|
|
29443
|
+
`SELECT id, file_path, symbol, content, fingerprint, created_at, updated_at FROM notes WHERE ${pathEqClause("file_path")} AND symbol = ?`
|
|
29444
|
+
).get(foldPath(filePath), symbol3);
|
|
29445
|
+
return row === void 0 ? null : toNoteRow(row);
|
|
29446
|
+
}
|
|
29447
|
+
function listNotes(dbPath = globalDbPath()) {
|
|
29448
|
+
const db = getDb(dbPath);
|
|
29449
|
+
const rows = db.prepare(
|
|
29450
|
+
"SELECT id, file_path, symbol, content, fingerprint, created_at, updated_at FROM notes ORDER BY file_path, symbol"
|
|
29451
|
+
).all();
|
|
29452
|
+
return rows.map(toNoteRow);
|
|
29453
|
+
}
|
|
29454
|
+
function isNoteStale(note, dbPath = globalDbPath()) {
|
|
29455
|
+
const current = note.symbol === WHOLE_FILE_NOTE_SYMBOL ? computeFileFingerprint(note.filePath, dbPath) : computeSymbolFingerprint(note.filePath, note.symbol, dbPath);
|
|
29456
|
+
return current === null || current !== note.fingerprint;
|
|
29457
|
+
}
|
|
29458
|
+
var WHOLE_FILE_NOTE_SYMBOL;
|
|
29459
|
+
var init_notes = __esm({
|
|
29460
|
+
"src/notes.ts"() {
|
|
29461
|
+
"use strict";
|
|
29462
|
+
init_define_import_meta_env();
|
|
29463
|
+
init_constants();
|
|
29464
|
+
init_db();
|
|
29465
|
+
init_fingerprint();
|
|
29466
|
+
init_index_reader();
|
|
29467
|
+
init_sql_path();
|
|
29468
|
+
init_util2();
|
|
29469
|
+
WHOLE_FILE_NOTE_SYMBOL = "";
|
|
29470
|
+
}
|
|
29471
|
+
});
|
|
29472
|
+
|
|
29275
29473
|
// src/ts_refs.ts
|
|
29276
29474
|
import { createRequire as createRequire6 } from "node:module";
|
|
29277
29475
|
import * as path45 from "node:path";
|
|
@@ -29824,6 +30022,9 @@ function runSection(opts) {
|
|
|
29824
30022
|
const heading = opts.spec.slice(colonIdx + 2);
|
|
29825
30023
|
const result = readSection(filePath, heading);
|
|
29826
30024
|
if (result === null) {
|
|
30025
|
+
if (!fs36.existsSync(filePath)) {
|
|
30026
|
+
return { text: `File not found: '${filePath}'`, code: 1 };
|
|
30027
|
+
}
|
|
29827
30028
|
const messages = [`Section '${heading}' not found in '${filePath}'`];
|
|
29828
30029
|
const available = listSections(filePath);
|
|
29829
30030
|
if (available.length > 0) messages.push(didYouMean(available));
|
|
@@ -29968,7 +30169,7 @@ function runRefsSingle(opts) {
|
|
|
29968
30169
|
function groupRefsByFile(refs) {
|
|
29969
30170
|
const byFile = /* @__PURE__ */ new Map();
|
|
29970
30171
|
for (const ref2 of refs) byFile.set(ref2.filePath, (byFile.get(ref2.filePath) ?? 0) + 1);
|
|
29971
|
-
return [...byFile.entries()].map(([file2, count]) => ({ file: file2, count })).sort((a, b) => b.count - a.count || a.file.
|
|
30172
|
+
return [...byFile.entries()].map(([file2, count]) => ({ file: file2, count })).sort((a, b) => b.count - a.count || (a.file < b.file ? -1 : a.file > b.file ? 1 : 0));
|
|
29972
30173
|
}
|
|
29973
30174
|
function renderTopFilesSummary(refs, topN) {
|
|
29974
30175
|
const grouped = groupRefsByFile(refs);
|
|
@@ -31478,6 +31679,11 @@ function extractImports(text, ext2) {
|
|
|
31478
31679
|
const m = /^\s*#\s*include\s+[<"]([^>"]+)[>"]/.exec(line);
|
|
31479
31680
|
if (m) push(m[1]);
|
|
31480
31681
|
}
|
|
31682
|
+
} else if ([".sh", ".bash"].includes(e)) {
|
|
31683
|
+
for (const line of lines2) {
|
|
31684
|
+
const m = /^\s*(?:source|\.)\s+['"]?([^\s'";]+)['"]?/.exec(line);
|
|
31685
|
+
if (m) push(m[1]);
|
|
31686
|
+
}
|
|
31481
31687
|
} else if ([".ps1", ".psm1"].includes(e)) {
|
|
31482
31688
|
for (const line of lines2) {
|
|
31483
31689
|
const importMod = /^\s*Import-Module\s+(?:-Name\s+)?['"]?([^\s'";]+)/i.exec(line);
|
|
@@ -31718,6 +31924,59 @@ ${previewLines(s.body, 3)}`);
|
|
|
31718
31924
|
recordReadStat("semantic_search", sumFileSizes(results.map((s) => s.filePath)), text, query);
|
|
31719
31925
|
return { text, code: 0 };
|
|
31720
31926
|
}
|
|
31927
|
+
function runNoteGet(opts) {
|
|
31928
|
+
const resolvedPath = resolveIndexPath(opts.file, opts.projectRoot ?? process.cwd());
|
|
31929
|
+
healStaleIndex(resolvedPath);
|
|
31930
|
+
const symbol3 = opts.symbol ?? WHOLE_FILE_NOTE_SYMBOL;
|
|
31931
|
+
const note = getNote(resolvedPath, symbol3);
|
|
31932
|
+
if (note === null) {
|
|
31933
|
+
const where = opts.symbol !== void 0 ? ` for symbol '${opts.symbol}'` : " (whole-file note)";
|
|
31934
|
+
return { text: `No note found for '${opts.file}'${where}`, code: 1 };
|
|
31935
|
+
}
|
|
31936
|
+
const stale = isNoteStale(note);
|
|
31937
|
+
if (opts.json === true) {
|
|
31938
|
+
const payload = {
|
|
31939
|
+
filePath: note.filePath,
|
|
31940
|
+
symbol: note.symbol === WHOLE_FILE_NOTE_SYMBOL ? null : note.symbol,
|
|
31941
|
+
content: note.content,
|
|
31942
|
+
stale,
|
|
31943
|
+
createdAt: note.createdAt,
|
|
31944
|
+
updatedAt: note.updatedAt
|
|
31945
|
+
};
|
|
31946
|
+
const text2 = JSON.stringify(payload, null, 2);
|
|
31947
|
+
recordStat("note_read");
|
|
31948
|
+
return { text: text2, code: 0 };
|
|
31949
|
+
}
|
|
31950
|
+
const target = note.symbol === WHOLE_FILE_NOTE_SYMBOL ? opts.file : `${opts.file}::${note.symbol}`;
|
|
31951
|
+
const staleTag = stale ? " [STALE \u2014 code changed since this note was written]" : "";
|
|
31952
|
+
const text = `# note \u2014 ${target}${staleTag}
|
|
31953
|
+
${note.content}`;
|
|
31954
|
+
recordStat("note_read");
|
|
31955
|
+
return { text, code: 0 };
|
|
31956
|
+
}
|
|
31957
|
+
function runNoteList(opts = {}) {
|
|
31958
|
+
const withStale = listNotes().map((note) => ({ note, stale: isNoteStale(note) }));
|
|
31959
|
+
const filtered = opts.staleOnly === true ? withStale.filter((n) => n.stale) : withStale;
|
|
31960
|
+
if (opts.json === true) {
|
|
31961
|
+
const items = filtered.map(({ note, stale }) => ({
|
|
31962
|
+
filePath: note.filePath,
|
|
31963
|
+
symbol: note.symbol === WHOLE_FILE_NOTE_SYMBOL ? null : note.symbol,
|
|
31964
|
+
stale,
|
|
31965
|
+
updatedAt: note.updatedAt
|
|
31966
|
+
}));
|
|
31967
|
+
recordStat("note_list");
|
|
31968
|
+
return { text: JSON.stringify(items, null, 2), code: 0 };
|
|
31969
|
+
}
|
|
31970
|
+
recordStat("note_list");
|
|
31971
|
+
if (filtered.length === 0) {
|
|
31972
|
+
return { text: opts.staleOnly === true ? "No stale notes." : "No notes recorded.", code: 0 };
|
|
31973
|
+
}
|
|
31974
|
+
const lines2 = filtered.map(({ note, stale }) => {
|
|
31975
|
+
const target = note.symbol === WHOLE_FILE_NOTE_SYMBOL ? note.filePath : `${note.filePath}::${note.symbol}`;
|
|
31976
|
+
return `${stale ? "[STALE] " : ""}${target}`;
|
|
31977
|
+
});
|
|
31978
|
+
return { text: lines2.join("\n"), code: 0 };
|
|
31979
|
+
}
|
|
31721
31980
|
var DIDYOUMEAN_LIMIT, GREP_MAX_LINES, FIND_SCAN_LIMIT, REFS_TOP_SCAN_LIMIT, STALE_WARNING, PARENT_IDENTIFIER_RE, SKELETON_SYMBOL_CAP, HUNK_HEADER_RE, DEFAULT_LOG_MAX_COUNT;
|
|
31722
31981
|
var init_read_commands = __esm({
|
|
31723
31982
|
"src/read_commands.ts"() {
|
|
@@ -31752,6 +32011,7 @@ var init_read_commands = __esm({
|
|
|
31752
32011
|
init_pdf_extract();
|
|
31753
32012
|
init_screenshot();
|
|
31754
32013
|
init_stats();
|
|
32014
|
+
init_notes();
|
|
31755
32015
|
init_ts_refs();
|
|
31756
32016
|
DIDYOUMEAN_LIMIT = 5;
|
|
31757
32017
|
GREP_MAX_LINES = 200;
|
|
@@ -48167,9 +48427,10 @@ async function storeBashOutput(command, output, exitCode, cwd = null) {
|
|
|
48167
48427
|
const id = await commandHash(command, cwd);
|
|
48168
48428
|
const fingerprints = computeBashFingerprints(command, cwd);
|
|
48169
48429
|
const redactedOutput = redactSecrets(output).text;
|
|
48430
|
+
const redactedCommand = redactSecrets(command).text;
|
|
48170
48431
|
const entry = {
|
|
48171
48432
|
id,
|
|
48172
|
-
command,
|
|
48433
|
+
command: redactedCommand,
|
|
48173
48434
|
output: redactedOutput,
|
|
48174
48435
|
exitCode,
|
|
48175
48436
|
storedAt: Date.now(),
|
|
@@ -48178,7 +48439,7 @@ async function storeBashOutput(command, output, exitCode, cwd = null) {
|
|
|
48178
48439
|
};
|
|
48179
48440
|
_byId.set(id, entry);
|
|
48180
48441
|
storeBlob(BASH_OUTPUT_SUBDIR, id, entry);
|
|
48181
|
-
indexRecallEntry("bash", id,
|
|
48442
|
+
indexRecallEntry("bash", id, redactedCommand, `${redactedCommand}
|
|
48182
48443
|
${redactedOutput}`, entry.storedAt);
|
|
48183
48444
|
return id;
|
|
48184
48445
|
}
|
|
@@ -48608,7 +48869,8 @@ function storeWebOutput(url2, content, dedupKey = url2) {
|
|
|
48608
48869
|
_byId2.set(cacheId, redactedContent);
|
|
48609
48870
|
_urlIndex.set(url2, cacheId);
|
|
48610
48871
|
storeBlob(WEB_OUTPUT_SUBDIR, cacheId, { url: url2, content: redactedContent });
|
|
48611
|
-
|
|
48872
|
+
const redactedUrl = redactSecrets(url2).text;
|
|
48873
|
+
indexRecallEntry("web", cacheId, redactedUrl, `${redactedUrl}
|
|
48612
48874
|
${redactedContent}`, Date.now());
|
|
48613
48875
|
return cacheId;
|
|
48614
48876
|
}
|
|
@@ -54653,7 +54915,7 @@ function preFetchHandler(event) {
|
|
|
54653
54915
|
if (cached2 !== null) {
|
|
54654
54916
|
const cachedBytes = Buffer.byteLength(cached2, "utf-8");
|
|
54655
54917
|
if (cachedBytes >= loadConfig().hints.web_dedup_min_bytes) {
|
|
54656
|
-
recordStat("webfetch:recall", cachedBytes, Math.round(
|
|
54918
|
+
recordStat("webfetch:recall", cachedBytes, Math.round(cachedBytes / 4));
|
|
54657
54919
|
return denyOutput(
|
|
54658
54920
|
"Already fetched this URL with this prompt; the response is cached. Use `token-goat web-output " + cacheId + "` to recall it (append `--grep PATTERN` to filter or `--section Heading` for a markdown section) instead of re-fetching."
|
|
54659
54921
|
);
|
|
@@ -55172,8 +55434,17 @@ function pathStem(p) {
|
|
|
55172
55434
|
const dot = name2.lastIndexOf(".");
|
|
55173
55435
|
return dot > 0 ? name2.slice(0, dot) : name2;
|
|
55174
55436
|
}
|
|
55175
|
-
function positionalArgs(args) {
|
|
55176
|
-
|
|
55437
|
+
function positionalArgs(args, valueFlags) {
|
|
55438
|
+
const out2 = [];
|
|
55439
|
+
for (let i = 0; i < args.length; i++) {
|
|
55440
|
+
const a = args[i];
|
|
55441
|
+
if (a.startsWith("-")) {
|
|
55442
|
+
if (valueFlags?.has(a) === true && i + 1 < args.length) i++;
|
|
55443
|
+
continue;
|
|
55444
|
+
}
|
|
55445
|
+
out2.push(a);
|
|
55446
|
+
}
|
|
55447
|
+
return out2;
|
|
55177
55448
|
}
|
|
55178
55449
|
var SHORT_FLAGS_WITH_VALUE = /* @__PURE__ */ new Set(["-n", "-c", "-u", "-e"]);
|
|
55179
55450
|
function stripPrefixes(argv) {
|
|
@@ -55311,6 +55582,7 @@ function byteLength(s) {
|
|
|
55311
55582
|
|
|
55312
55583
|
// src/tool_filters/base.ts
|
|
55313
55584
|
init_define_import_meta_env();
|
|
55585
|
+
init_secret_redact();
|
|
55314
55586
|
var CompressedOutput = class {
|
|
55315
55587
|
constructor(text, originalBytes, compressedBytes, filterName, exitCode = 0, notes = []) {
|
|
55316
55588
|
this.text = text;
|
|
@@ -55480,6 +55752,9 @@ ${stderr.replace(/\s+$/, "")}`;
|
|
|
55480
55752
|
const lines2 = body.split("\n");
|
|
55481
55753
|
if (lines2.length > maxLines) body = truncateMiddleSmart(lines2, maxLines).join("\n");
|
|
55482
55754
|
body = capBytes(body, maxBytes);
|
|
55755
|
+
const redacted = redactSecrets(body);
|
|
55756
|
+
body = redacted.text;
|
|
55757
|
+
if (redacted.count > 0) notes.push(`redacted ${redacted.count} secret-shaped value(s)`);
|
|
55483
55758
|
if (notes.length) body = `[${notes.join("; ")}]
|
|
55484
55759
|
${body}`;
|
|
55485
55760
|
return new CompressedOutput(body, originalBytes, byteLength(body), this.name, exitCode, notes);
|
|
@@ -58730,7 +59005,9 @@ var _GIT_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
|
58730
59005
|
"-F",
|
|
58731
59006
|
"--file",
|
|
58732
59007
|
"--author",
|
|
58733
|
-
"--date"
|
|
59008
|
+
"--date",
|
|
59009
|
+
"--git-dir",
|
|
59010
|
+
"--work-tree"
|
|
58734
59011
|
]);
|
|
58735
59012
|
function gitPositionalArgs(args) {
|
|
58736
59013
|
const out2 = [];
|
|
@@ -61258,6 +61535,7 @@ var _DOCKER_OLD_SHA_RE = /^ *---> (?:sha256:)?[0-9a-f]{12,}\s*$/;
|
|
|
61258
61535
|
var _DOCKER_OLD_STEP_RE = /^Step \d+\/\d+ : /;
|
|
61259
61536
|
var _DOCKER_OLD_SUCCESS_RE = /^Successfully built [0-9a-f]+/;
|
|
61260
61537
|
var _DOCKER_OLD_INTERMEDIATE_RE = /^Removing intermediate container [0-9a-f]+/;
|
|
61538
|
+
var _DOCKER_OLD_STEP_ERROR_RE = /error|returned a non-zero code/i;
|
|
61261
61539
|
var DockerFilter = class extends ToolFilter {
|
|
61262
61540
|
name = "docker";
|
|
61263
61541
|
binaries = /* @__PURE__ */ new Set(["docker", "buildah", "podman", "nerdctl"]);
|
|
@@ -61338,7 +61616,7 @@ var DockerFilter = class extends ToolFilter {
|
|
|
61338
61616
|
oldStepErr = false;
|
|
61339
61617
|
continue;
|
|
61340
61618
|
}
|
|
61341
|
-
if (
|
|
61619
|
+
if (_DOCKER_OLD_STEP_ERROR_RE.test(ol)) oldStepErr = true;
|
|
61342
61620
|
oldNew.push(ol);
|
|
61343
61621
|
}
|
|
61344
61622
|
if (oldStepHdr !== null) {
|
|
@@ -61575,12 +61853,26 @@ function _compressKubectlDescribe(text) {
|
|
|
61575
61853
|
}
|
|
61576
61854
|
return kept.join("\n");
|
|
61577
61855
|
}
|
|
61856
|
+
var KUBECTL_GLOBAL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
61857
|
+
"-n",
|
|
61858
|
+
"--namespace",
|
|
61859
|
+
"--context",
|
|
61860
|
+
"--kubeconfig",
|
|
61861
|
+
"--cluster",
|
|
61862
|
+
"--user",
|
|
61863
|
+
"-s",
|
|
61864
|
+
"--server",
|
|
61865
|
+
"--token",
|
|
61866
|
+
"--as",
|
|
61867
|
+
"--as-group",
|
|
61868
|
+
"--request-timeout"
|
|
61869
|
+
]);
|
|
61578
61870
|
var KubectlFilter = class extends ToolFilter {
|
|
61579
61871
|
name = "kubectl";
|
|
61580
61872
|
binaries = /* @__PURE__ */ new Set(["kubectl", "k", "k9s", "oc"]);
|
|
61581
61873
|
errorPassthrough = true;
|
|
61582
61874
|
compressBody(stdout, stderr, _exitCode, argv) {
|
|
61583
|
-
const pos = positionalArgs(argv.slice(1));
|
|
61875
|
+
const pos = positionalArgs(argv.slice(1), KUBECTL_GLOBAL_VALUE_FLAGS);
|
|
61584
61876
|
const subcommand = pos[0] ?? "";
|
|
61585
61877
|
let text = stdout;
|
|
61586
61878
|
if (subcommand === "get" || subcommand === "top") {
|
|
@@ -61762,7 +62054,7 @@ var KubectlLogsFilter = class extends ToolFilter {
|
|
|
61762
62054
|
const stem = pathStem(argv[0]).toLowerCase();
|
|
61763
62055
|
const name2 = pathName(argv[0]).toLowerCase();
|
|
61764
62056
|
if (!["kubectl", "k"].includes(stem) && !["kubectl", "k"].includes(name2)) return false;
|
|
61765
|
-
const pos = positionalArgs(argv.slice(1));
|
|
62057
|
+
const pos = positionalArgs(argv.slice(1), KUBECTL_GLOBAL_VALUE_FLAGS);
|
|
61766
62058
|
return pos.length > 0 && pos[0] === "logs";
|
|
61767
62059
|
}
|
|
61768
62060
|
compressBody(stdout, stderr, _exitCode, _argv) {
|
|
@@ -62208,6 +62500,19 @@ var awsFilter = new AwsFilter();
|
|
|
62208
62500
|
var _AWS_UPLOAD_RE = /^upload:\s+\S+\s+to\s+s3:\/\//i;
|
|
62209
62501
|
var _AWS_DOWNLOAD_RE = /^download:\s+s3:\/\//i;
|
|
62210
62502
|
var _AWS_S3_PROGRESS_RE = /^(?:Completed\s+\d|\d+(?:\.\d+)?\s*(?:KiB|MiB|GiB|B)\/s|Calculating|upload\s+failed:|download\s+failed:)/i;
|
|
62503
|
+
var AWS_GLOBAL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
62504
|
+
"--profile",
|
|
62505
|
+
"--region",
|
|
62506
|
+
"--endpoint-url",
|
|
62507
|
+
"--output",
|
|
62508
|
+
"--query",
|
|
62509
|
+
"--color",
|
|
62510
|
+
"--ca-bundle",
|
|
62511
|
+
"--cli-read-timeout",
|
|
62512
|
+
"--cli-connect-timeout",
|
|
62513
|
+
"--cli-binary-format",
|
|
62514
|
+
"--cli-pager"
|
|
62515
|
+
]);
|
|
62211
62516
|
var AwsCliFilter = class extends ToolFilter {
|
|
62212
62517
|
name = "aws-cli";
|
|
62213
62518
|
binaries = /* @__PURE__ */ new Set(["aws", "aws2"]);
|
|
@@ -62215,7 +62520,7 @@ var AwsCliFilter = class extends ToolFilter {
|
|
|
62215
62520
|
_JSON_ARRAY_THRESHOLD = 10;
|
|
62216
62521
|
_JSON_ARRAY_KEEP = 3;
|
|
62217
62522
|
compressBody(stdout, stderr, _exitCode, argv) {
|
|
62218
|
-
const positionals = positionalArgs(argv.slice(1));
|
|
62523
|
+
const positionals = positionalArgs(argv.slice(1), AWS_GLOBAL_VALUE_FLAGS);
|
|
62219
62524
|
const isS3Transfer = positionals.length >= 2 && positionals[0] === "s3" && (positionals[1] === "cp" || positionals[1] === "sync" || positionals[1] === "mv");
|
|
62220
62525
|
const isCfnEvents = positionals.length >= 2 && positionals[0] === "cloudformation" && positionals[1] === "describe-stack-events";
|
|
62221
62526
|
let text = stdout;
|
|
@@ -63606,6 +63911,7 @@ var _GH_RUN_PASS_STEP_RE = /^\s*[✓√]\s/;
|
|
|
63606
63911
|
var _GH_RUN_FAIL_STEP_RE = /^\s*[X✗❌]\s|^\s*FAIL(:|ED|URE)\b|^\s*Error:\s/;
|
|
63607
63912
|
var _GH_API_URL_SUFFIX = "_url";
|
|
63608
63913
|
var _GH_API_URL_KEEP = /* @__PURE__ */ new Set(["html_url", "avatar_url", "clone_url", "ssh_url"]);
|
|
63914
|
+
var GH_GLOBAL_VALUE_FLAGS = /* @__PURE__ */ new Set(["-R", "--repo", "--hostname"]);
|
|
63609
63915
|
var _GH_API_NOISE_KEYS = /* @__PURE__ */ new Set(["gravatar_id", "site_admin"]);
|
|
63610
63916
|
var _GH_CONTENT_B64_RE = /^[A-Za-z0-9+/=\n]+$/;
|
|
63611
63917
|
var _GH_BASE64_MIN_LEN = 200;
|
|
@@ -63890,7 +64196,7 @@ var GhFilter = class extends ToolFilter {
|
|
|
63890
64196
|
binaries = /* @__PURE__ */ new Set(["gh"]);
|
|
63891
64197
|
compress(stdout, stderr, _exitCode, argv) {
|
|
63892
64198
|
const redactedStdout = redactGhBase64Content(stdout);
|
|
63893
|
-
const positionals = positionalArgs(argv.slice(1));
|
|
64199
|
+
const positionals = positionalArgs(argv.slice(1), GH_GLOBAL_VALUE_FLAGS);
|
|
63894
64200
|
const subcommand = positionals[0] ?? "";
|
|
63895
64201
|
const action = positionals[1] ?? "";
|
|
63896
64202
|
const merged = this.combineOutput(redactedStdout, stderr);
|
|
@@ -64695,9 +65001,38 @@ var LsFilter = class _LsFilter extends ToolFilter {
|
|
|
64695
65001
|
var _EZA_PASSTHROUGH = 30;
|
|
64696
65002
|
var _HEADER_KEYWORDS = /* @__PURE__ */ new Set(["permission", "size", "date", "user", "name"]);
|
|
64697
65003
|
var _SUMMARY_KEYWORDS = ["director", "file", "total"];
|
|
64698
|
-
var EzaFilter = class extends ToolFilter {
|
|
65004
|
+
var EzaFilter = class _EzaFilter extends ToolFilter {
|
|
64699
65005
|
name = "eza";
|
|
64700
65006
|
binaries = /* @__PURE__ */ new Set(["eza", "exa", "ls"]);
|
|
65007
|
+
// Flags eza/exa support that plain GNU/BSD `ls` does not -- used to disambiguate the shared
|
|
65008
|
+
// 'ls' binary claim below (a common `alias ls=eza` setup means the literal command text is
|
|
65009
|
+
// "ls ...", but token-goat only ever sees that raw text, never the shell's alias resolution).
|
|
65010
|
+
static _EZA_ONLY_FLAGS = /* @__PURE__ */ new Set([
|
|
65011
|
+
"--tree",
|
|
65012
|
+
"-T",
|
|
65013
|
+
"--icons",
|
|
65014
|
+
"--no-icons",
|
|
65015
|
+
"--git",
|
|
65016
|
+
"--git-repos",
|
|
65017
|
+
"--git-repos-no-status",
|
|
65018
|
+
"--level"
|
|
65019
|
+
]);
|
|
65020
|
+
// LsFilter (registered before EzaFilter in SHELL_FILE_FILTERS) always wins a plain `ls`
|
|
65021
|
+
// invocation since binary-name matching alone can't tell a real GNU/BSD `ls` from an
|
|
65022
|
+
// `alias ls=eza` shell alias -- the literal command text is "ls ..." either way. Without this
|
|
65023
|
+
// gate, EzaFilter's own 'ls' binary claim was permanently unreachable dead code: LsFilter's
|
|
65024
|
+
// generic ls-format compressor (no awareness of eza's tree/column output) silently ran on
|
|
65025
|
+
// every aliased `ls --tree`/`ls --icons`/etc. invocation instead. Mirrors RgFilter's own
|
|
65026
|
+
// `_hasContextFlags` gate, which resolves the same kind of shared-binary ambiguity between
|
|
65027
|
+
// itself and GrepFilter.
|
|
65028
|
+
matches(argv) {
|
|
65029
|
+
if (!super.matches(argv)) return false;
|
|
65030
|
+
const first2 = argv[0];
|
|
65031
|
+
const stem = pathStem(first2).toLowerCase();
|
|
65032
|
+
const name2 = pathName(first2).toLowerCase();
|
|
65033
|
+
if (stem !== "ls" && name2 !== "ls") return true;
|
|
65034
|
+
return argv.slice(1).some((a) => _EzaFilter._EZA_ONLY_FLAGS.has(a));
|
|
65035
|
+
}
|
|
64701
65036
|
compressBody(stdout, stderr, _exitCode, argv) {
|
|
64702
65037
|
const merged = this.combineOutput(stdout, stderr);
|
|
64703
65038
|
const text = normalise(merged);
|
|
@@ -65455,9 +65790,11 @@ var SHELL_FILE_FILTERS = [
|
|
|
65455
65790
|
ffmpegFilter,
|
|
65456
65791
|
// Diff tool (plain POSIX diff; git diff is handled by GitFilter)
|
|
65457
65792
|
diffFilter,
|
|
65458
|
-
// Directory listings — LsFilter (
|
|
65459
|
-
|
|
65793
|
+
// Directory listings — EzaFilter before LsFilter: EzaFilter's matches() gate falls through to
|
|
65794
|
+
// LsFilter for a plain `ls` with no eza-only flag, but must run FIRST so it can actually claim
|
|
65795
|
+
// an aliased `ls --tree`/`ls --icons`/etc. invocation (see EzaFilter.matches doc comment).
|
|
65460
65796
|
ezaFilter,
|
|
65797
|
+
lsFilter,
|
|
65461
65798
|
fdFilter,
|
|
65462
65799
|
wcFilter,
|
|
65463
65800
|
treeFilter,
|
|
@@ -65967,7 +66304,7 @@ var flutterFilter = new FlutterFilter();
|
|
|
65967
66304
|
var DART_ANALYZING_RE = /^Analyzing\s/;
|
|
65968
66305
|
var DART_ANALYZE_RESULT_RE = /^(?:No issues found!|\d+ issue[s]? found\.|warning -|error -|info -|hint -)/;
|
|
65969
66306
|
var DART_TEST_PROGRESS_RE = /^\d{2}:\d{2}\s+[+\d]|^[.]+$/;
|
|
65970
|
-
var DART_COMPILE_DONE_RE = /^
|
|
66307
|
+
var DART_COMPILE_DONE_RE = /^Generated:\s/;
|
|
65971
66308
|
var DART_TEST_SUMMARY_RE = /(?:All tests passed\.?|\d+\s+test[s]?\s+(?:passed|failed))/;
|
|
65972
66309
|
var PUB_KEEP_RE2 = /^(?:Resolving dependencies|Changed \d+|No dependencies changed|Got dependencies|Downloading packages|Building package executable)/;
|
|
65973
66310
|
var PUB_PKG_LINE_RE2 = /^[+>!]\s+\S+\s+\S+/;
|
|
@@ -69710,8 +70047,9 @@ function storeMcpOutput(sessionId, toolName, toolInput, resultText) {
|
|
|
69710
70047
|
const rawSizeBytes = Buffer.byteLength(resultText, "utf-8");
|
|
69711
70048
|
if (rawSizeBytes > MCP_MAX_CACHE_BYTES) return null;
|
|
69712
70049
|
const id = mcpOutputId(sessionId, mcpHash(toolName, toolInput));
|
|
69713
|
-
const
|
|
70050
|
+
const rawLabel = `mcp:${toolName} ${mcpInputPreview(toolInput)}`.trim();
|
|
69714
70051
|
const redactedOutput = redactSecrets(resultText).text;
|
|
70052
|
+
const label = redactSecrets(rawLabel).text;
|
|
69715
70053
|
const entry = {
|
|
69716
70054
|
id,
|
|
69717
70055
|
command: label,
|
|
@@ -69980,6 +70318,7 @@ function compressMcpResultWithPacks(toolName, resultText) {
|
|
|
69980
70318
|
}
|
|
69981
70319
|
|
|
69982
70320
|
// src/hooks_mcp.ts
|
|
70321
|
+
init_secret_redact();
|
|
69983
70322
|
function isMcpErrorResponse(raw) {
|
|
69984
70323
|
const tr = raw["tool_response"];
|
|
69985
70324
|
if (!tr || typeof tr !== "object") return false;
|
|
@@ -70019,7 +70358,7 @@ function postMcpHandler(event) {
|
|
|
70019
70358
|
return {
|
|
70020
70359
|
hookType: "rewriteOutput",
|
|
70021
70360
|
updatedOutput: `[token-goat: compressed, full via mcp-output ${id}]
|
|
70022
|
-
${compressed}`
|
|
70361
|
+
${redactSecrets(compressed).text}`
|
|
70023
70362
|
};
|
|
70024
70363
|
}
|
|
70025
70364
|
}
|
|
@@ -71753,6 +72092,7 @@ function recordSavings(result) {
|
|
|
71753
72092
|
|
|
71754
72093
|
// src/cli.ts
|
|
71755
72094
|
init_read_commands();
|
|
72095
|
+
init_notes();
|
|
71756
72096
|
|
|
71757
72097
|
// src/bridges_status.ts
|
|
71758
72098
|
init_define_import_meta_env();
|
|
@@ -72168,6 +72508,7 @@ function formatCues(cues) {
|
|
|
72168
72508
|
init_graph_commands();
|
|
72169
72509
|
init_skill_cache();
|
|
72170
72510
|
init_hooks_read();
|
|
72511
|
+
init_section_reader();
|
|
72171
72512
|
init_util2();
|
|
72172
72513
|
init_ansi();
|
|
72173
72514
|
init_config();
|
|
@@ -75515,6 +75856,9 @@ import * as fs43 from "node:fs";
|
|
|
75515
75856
|
import * as path53 from "node:path";
|
|
75516
75857
|
var MAX_ENTRIES = 30;
|
|
75517
75858
|
var KEY_RE2 = /^[A-Za-z0-9_-]{1,80}$/;
|
|
75859
|
+
function ordinal(a, b) {
|
|
75860
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
75861
|
+
}
|
|
75518
75862
|
function memoryPath(projectHash2) {
|
|
75519
75863
|
return path53.join(dataDir(), "projects", `${projectHash2}_memory.toml`);
|
|
75520
75864
|
}
|
|
@@ -75569,7 +75913,7 @@ function loadRaw(filePath) {
|
|
|
75569
75913
|
}
|
|
75570
75914
|
function save(filePath, entries) {
|
|
75571
75915
|
const lines2 = [];
|
|
75572
|
-
const sorted = Object.entries(entries).sort(([a], [b]) => a
|
|
75916
|
+
const sorted = Object.entries(entries).sort(([a], [b]) => ordinal(a, b));
|
|
75573
75917
|
for (const [k, v] of sorted) {
|
|
75574
75918
|
const escaped = v.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
|
75575
75919
|
lines2.push(`${k} = "${escaped}"`);
|
|
@@ -75592,7 +75936,7 @@ function setEntry(projectHash2, key, value) {
|
|
|
75592
75936
|
const isNewKey = !(key in entries);
|
|
75593
75937
|
if (isNewKey && Object.keys(entries).length >= MAX_ENTRIES) {
|
|
75594
75938
|
const keysToKeep = MAX_ENTRIES - 1;
|
|
75595
|
-
const allKeys = Object.keys(entries).sort((a, b) => a
|
|
75939
|
+
const allKeys = Object.keys(entries).sort((a, b) => ordinal(a, b));
|
|
75596
75940
|
for (const k of allKeys.slice(keysToKeep)) {
|
|
75597
75941
|
delete entries[k];
|
|
75598
75942
|
}
|
|
@@ -76237,7 +76581,7 @@ function parseRequirementsTxt(content) {
|
|
|
76237
76581
|
const deps = [];
|
|
76238
76582
|
for (const raw of splitLines2(content)) {
|
|
76239
76583
|
if (/^\s*#/.test(raw)) continue;
|
|
76240
|
-
const eggMatch = /^\s*(?:git|hg|svn|bzr)\+.*#egg=([A-Za-z0-9_.-]+)/.exec(raw);
|
|
76584
|
+
const eggMatch = /^\s*(?:-e\s+|--editable[\s=]+)?(?:git|hg|svn|bzr)\+.*#egg=([A-Za-z0-9_.-]+)/.exec(raw);
|
|
76241
76585
|
if (eggMatch !== null) {
|
|
76242
76586
|
deps.push({ name: eggMatch[1] ?? "", version: "", kind: "unknown" });
|
|
76243
76587
|
continue;
|
|
@@ -76805,8 +77149,11 @@ function resolveTypesLocation(pkgDir, pkgJson, nodeModulesDir, pkgName) {
|
|
|
76805
77149
|
try {
|
|
76806
77150
|
const typesPkgJson = JSON.parse(typesPkgJsonRaw);
|
|
76807
77151
|
const entry = (typeof typesPkgJson["types"] === "string" ? typesPkgJson["types"] : void 0) ?? (typeof typesPkgJson["main"] === "string" ? typesPkgJson["main"] : void 0) ?? "index.d.ts";
|
|
76808
|
-
const
|
|
76809
|
-
|
|
77152
|
+
const entryCandidates = [entry, entry.endsWith(".d.ts") ? entry : `${entry}.d.ts`, entry.replace(/\.[cm]?[jt]s$/, ".d.ts")];
|
|
77153
|
+
for (const c of entryCandidates) {
|
|
77154
|
+
const entryPath = path55.join(typesPkgDir, c);
|
|
77155
|
+
if (fileExists2(entryPath)) return { path: entryPath, source: "@types" };
|
|
77156
|
+
}
|
|
76810
77157
|
} catch {
|
|
76811
77158
|
}
|
|
76812
77159
|
const fallback = path55.join(typesPkgDir, "index.d.ts");
|
|
@@ -78457,8 +78804,11 @@ var FILE_PATH_TOOLS = /* @__PURE__ */ new Set(["Read", "Edit", "Write", "Noteboo
|
|
|
78457
78804
|
function extractFilePath(name2, input) {
|
|
78458
78805
|
if (!FILE_PATH_TOOLS.has(name2)) return null;
|
|
78459
78806
|
if (input === null || typeof input !== "object") return null;
|
|
78460
|
-
const
|
|
78461
|
-
|
|
78807
|
+
const o = input;
|
|
78808
|
+
const fp = o["file_path"];
|
|
78809
|
+
if (typeof fp === "string") return fp;
|
|
78810
|
+
const notebookPath = o["notebook_path"];
|
|
78811
|
+
return typeof notebookPath === "string" ? notebookPath : null;
|
|
78462
78812
|
}
|
|
78463
78813
|
function extractCommand2(name2, input) {
|
|
78464
78814
|
if (name2 !== "Bash") return null;
|
|
@@ -79751,7 +80101,11 @@ function cmdVideoChapters(file2) {
|
|
|
79751
80101
|
}
|
|
79752
80102
|
lines2.push("(extract a subtitle stream to .vtt/.srt with ffmpeg, then use transcript/transcript-outline on it)");
|
|
79753
80103
|
}
|
|
79754
|
-
|
|
80104
|
+
const text = lines2.join("\n");
|
|
80105
|
+
out(text);
|
|
80106
|
+
const fullSourceBytes = fileSizeOrZero(file2);
|
|
80107
|
+
const bytesSaved = Math.max(1, fullSourceBytes - Buffer.byteLength(text, "utf8"));
|
|
80108
|
+
recordStat("video_chapters", bytesSaved, Math.round(bytesSaved / 4));
|
|
79755
80109
|
}
|
|
79756
80110
|
function formatVideoTimestamp(totalSeconds) {
|
|
79757
80111
|
const hours = Math.floor(totalSeconds / 3600);
|
|
@@ -80388,6 +80742,50 @@ function decodeBase64Buffer(payload, label) {
|
|
|
80388
80742
|
}
|
|
80389
80743
|
return Buffer.from(normalized, "base64");
|
|
80390
80744
|
}
|
|
80745
|
+
function cmdNoteAdd(file2, opts) {
|
|
80746
|
+
if (!file2 || !file2.trim()) {
|
|
80747
|
+
throw new CliError("file path cannot be empty");
|
|
80748
|
+
}
|
|
80749
|
+
const usingFrom = opts.contentFrom !== void 0;
|
|
80750
|
+
const usingB64 = opts.contentB64 !== void 0;
|
|
80751
|
+
if (usingFrom && usingB64) {
|
|
80752
|
+
throw new CliError("cannot mix --content-from with --content-b64");
|
|
80753
|
+
}
|
|
80754
|
+
if (!usingFrom && !usingB64) {
|
|
80755
|
+
throw new CliError("must provide either --content-from or --content-b64");
|
|
80756
|
+
}
|
|
80757
|
+
const contentBytes = usingFrom ? readFileBoundedRaw(opts.contentFrom, "--content-from") : decodeBase64Buffer(opts.contentB64, "--content-b64");
|
|
80758
|
+
if (contentBytes.length === 0) {
|
|
80759
|
+
throw new CliError("note content cannot be empty");
|
|
80760
|
+
}
|
|
80761
|
+
if (Buffer.compare(Buffer.from(contentBytes.toString("utf8"), "utf8"), contentBytes) !== 0) {
|
|
80762
|
+
throw new CliError("note content must be valid UTF-8 text");
|
|
80763
|
+
}
|
|
80764
|
+
const resolvedPath = resolveIndexPath(file2);
|
|
80765
|
+
if (!fs52.existsSync(resolvedPath)) {
|
|
80766
|
+
throw new CliError(`File not found: '${resolvedPath}'`);
|
|
80767
|
+
}
|
|
80768
|
+
healStaleIndex(resolvedPath);
|
|
80769
|
+
let symbol3 = WHOLE_FILE_NOTE_SYMBOL;
|
|
80770
|
+
let fingerprint;
|
|
80771
|
+
if (opts.symbol !== void 0) {
|
|
80772
|
+
const match2 = resolveSymbolMatch(resolvedPath, opts.symbol);
|
|
80773
|
+
if (match2 === null) {
|
|
80774
|
+
const messages = [`No symbol named '${opts.symbol}' is indexed in '${file2}'`];
|
|
80775
|
+
const available = symbolNamesInFile(resolvedPath);
|
|
80776
|
+
if (available.length > 0) messages.push(didYouMean(available));
|
|
80777
|
+
throw new CliError(messages.join("\n"));
|
|
80778
|
+
}
|
|
80779
|
+
symbol3 = opts.symbol;
|
|
80780
|
+
fingerprint = fingerprintContent(match2.body);
|
|
80781
|
+
} else {
|
|
80782
|
+
fingerprint = computeFileFingerprint(resolvedPath);
|
|
80783
|
+
}
|
|
80784
|
+
upsertNote(resolvedPath, symbol3, contentBytes.toString("utf8"), fingerprint);
|
|
80785
|
+
const target = opts.symbol !== void 0 ? `${file2}::${opts.symbol}` : file2;
|
|
80786
|
+
out(`Note saved: ${target} (fingerprint ${fingerprint.slice(0, 12)})`);
|
|
80787
|
+
recordStat("note_write");
|
|
80788
|
+
}
|
|
80391
80789
|
function cmdWriteFile(dest, opts) {
|
|
80392
80790
|
validateWritablePath(dest, "destination");
|
|
80393
80791
|
if (opts.from !== void 0 && opts.b64 !== void 0) {
|
|
@@ -80493,6 +80891,56 @@ function diagnoseNearMiss(targetText, oldText) {
|
|
|
80493
80891
|
}
|
|
80494
80892
|
return void 0;
|
|
80495
80893
|
}
|
|
80894
|
+
function detectDominantEol(buf) {
|
|
80895
|
+
let crlf = 0;
|
|
80896
|
+
let lfOnly = 0;
|
|
80897
|
+
for (let i = 0; i < buf.length; i++) {
|
|
80898
|
+
if (buf[i] === 10) {
|
|
80899
|
+
if (i > 0 && buf[i - 1] === 13) crlf++;
|
|
80900
|
+
else lfOnly++;
|
|
80901
|
+
}
|
|
80902
|
+
}
|
|
80903
|
+
return crlf > lfOnly ? "\r\n" : "\n";
|
|
80904
|
+
}
|
|
80905
|
+
function normalizeEolToMatch(source, reference) {
|
|
80906
|
+
const eol = detectDominantEol(reference);
|
|
80907
|
+
const CR = 13;
|
|
80908
|
+
const LF = 10;
|
|
80909
|
+
const collapsed = [];
|
|
80910
|
+
for (let i = 0; i < source.length; i++) {
|
|
80911
|
+
if (source[i] === CR && source[i + 1] === LF) continue;
|
|
80912
|
+
collapsed.push(source[i]);
|
|
80913
|
+
}
|
|
80914
|
+
if (eol === "\n") return Buffer.from(collapsed);
|
|
80915
|
+
const expanded = [];
|
|
80916
|
+
for (const b of collapsed) {
|
|
80917
|
+
if (b === LF) expanded.push(CR, LF);
|
|
80918
|
+
else expanded.push(b);
|
|
80919
|
+
}
|
|
80920
|
+
return Buffer.from(expanded);
|
|
80921
|
+
}
|
|
80922
|
+
var MAX_CLOSEST_MATCH_COMPARISONS = 2e6;
|
|
80923
|
+
function findClosestLineWindow(targetText, oldText) {
|
|
80924
|
+
const targetLines = targetText.split("\n");
|
|
80925
|
+
const oldLines = oldText.split("\n");
|
|
80926
|
+
const windowSize = oldLines.length;
|
|
80927
|
+
if (windowSize === 0 || windowSize > targetLines.length) return void 0;
|
|
80928
|
+
if ((targetLines.length - windowSize + 1) * windowSize > MAX_CLOSEST_MATCH_COMPARISONS) return void 0;
|
|
80929
|
+
let bestIdx = -1;
|
|
80930
|
+
let bestScore = 0;
|
|
80931
|
+
for (let i = 0; i <= targetLines.length - windowSize; i++) {
|
|
80932
|
+
let score = 0;
|
|
80933
|
+
for (let j = 0; j < windowSize; j++) {
|
|
80934
|
+
if (targetLines[i + j] === oldLines[j]) score++;
|
|
80935
|
+
}
|
|
80936
|
+
if (score > bestScore) {
|
|
80937
|
+
bestScore = score;
|
|
80938
|
+
bestIdx = i;
|
|
80939
|
+
}
|
|
80940
|
+
}
|
|
80941
|
+
if (bestIdx === -1) return void 0;
|
|
80942
|
+
return { lineStart: bestIdx + 1, region: targetLines.slice(bestIdx, bestIdx + windowSize).join("\n") };
|
|
80943
|
+
}
|
|
80496
80944
|
function cmdReplace(file2, opts) {
|
|
80497
80945
|
validateWritablePath(file2, "target file");
|
|
80498
80946
|
const targetBuf = readFileBoundedRaw(file2, "target file", true);
|
|
@@ -80520,19 +80968,32 @@ function cmdReplace(file2, opts) {
|
|
|
80520
80968
|
}
|
|
80521
80969
|
const oldBytes = usingFrom ? readFileBoundedRaw(opts.oldFrom, "--old-from") : decodeBase64Buffer(opts.oldB64, "--old-b64");
|
|
80522
80970
|
const newBytes = usingFrom ? readFileBoundedRaw(opts.newFrom, "--new-from") : decodeBase64Buffer(opts.newB64, "--new-b64");
|
|
80523
|
-
|
|
80971
|
+
const normalizedOldBytes = opts.normalizeNewlines === true ? normalizeEolToMatch(oldBytes, targetBuf) : oldBytes;
|
|
80972
|
+
const normalizedNewBytes = opts.normalizeNewlines === true ? normalizeEolToMatch(newBytes, targetBuf) : newBytes;
|
|
80973
|
+
if (normalizedOldBytes.length === 0) {
|
|
80524
80974
|
throw new CliError("old string cannot be empty");
|
|
80525
80975
|
}
|
|
80526
80976
|
const matches2 = [];
|
|
80527
80977
|
let cursor = 0;
|
|
80528
|
-
while ((cursor = targetBuf.indexOf(
|
|
80978
|
+
while ((cursor = targetBuf.indexOf(normalizedOldBytes, cursor)) !== -1) {
|
|
80529
80979
|
matches2.push(cursor);
|
|
80530
|
-
cursor +=
|
|
80980
|
+
cursor += normalizedOldBytes.length;
|
|
80531
80981
|
}
|
|
80532
80982
|
const occurrences = matches2.length;
|
|
80533
80983
|
if (occurrences === 0) {
|
|
80534
|
-
const nearMiss = diagnoseNearMiss(targetBuf.toString("utf8"),
|
|
80535
|
-
|
|
80984
|
+
const nearMiss = diagnoseNearMiss(targetBuf.toString("utf8"), normalizedOldBytes.toString("utf8"));
|
|
80985
|
+
if (nearMiss !== void 0) {
|
|
80986
|
+
throw new CliError(`old string not found in ${file2} \u2014 ${nearMiss}`);
|
|
80987
|
+
}
|
|
80988
|
+
const closest = findClosestLineWindow(targetBuf.toString("utf8"), normalizedOldBytes.toString("utf8"));
|
|
80989
|
+
if (closest !== void 0) {
|
|
80990
|
+
const diff = buildLineDiff(closest.region, normalizedOldBytes.toString("utf8"), file2);
|
|
80991
|
+
throw new CliError(
|
|
80992
|
+
`old string not found in ${file2} \u2014 closest match at line ${closest.lineStart} (showing: what's actually there vs. what --old-from/--old-b64 searched for):
|
|
80993
|
+
${diff}`
|
|
80994
|
+
);
|
|
80995
|
+
}
|
|
80996
|
+
throw new CliError(`old string not found in ${file2}`);
|
|
80536
80997
|
}
|
|
80537
80998
|
if (occurrences > 1 && !opts.all) {
|
|
80538
80999
|
throw new CliError(`old string appears ${occurrences} times in ${file2} \u2014 pass --all to replace every occurrence, or provide a more specific match`);
|
|
@@ -80541,8 +81002,8 @@ function cmdReplace(file2, opts) {
|
|
|
80541
81002
|
let prevEnd = 0;
|
|
80542
81003
|
for (const pos of matches2) {
|
|
80543
81004
|
parts.push(targetBuf.subarray(prevEnd, pos));
|
|
80544
|
-
parts.push(
|
|
80545
|
-
prevEnd = pos +
|
|
81005
|
+
parts.push(normalizedNewBytes);
|
|
81006
|
+
prevEnd = pos + normalizedOldBytes.length;
|
|
80546
81007
|
}
|
|
80547
81008
|
parts.push(targetBuf.subarray(prevEnd));
|
|
80548
81009
|
const replacedBuf = Buffer.concat(parts);
|
|
@@ -80569,6 +81030,65 @@ function cmdReplace(file2, opts) {
|
|
|
80569
81030
|
enqueueDirtyPathSafe(file2);
|
|
80570
81031
|
out(`replaced ${occurrences} occurrence${occurrences === 1 ? "" : "s"} in ${file2}`);
|
|
80571
81032
|
}
|
|
81033
|
+
function cmdInsertSection(file2, opts) {
|
|
81034
|
+
validateWritablePath(file2, "target file");
|
|
81035
|
+
const usingFrom = opts.contentFrom !== void 0;
|
|
81036
|
+
const usingB64 = opts.contentB64 !== void 0;
|
|
81037
|
+
if (usingFrom && usingB64) {
|
|
81038
|
+
throw new CliError("cannot mix --content-from with --content-b64");
|
|
81039
|
+
}
|
|
81040
|
+
if (!usingFrom && !usingB64) {
|
|
81041
|
+
throw new CliError("must provide either --content-from or --content-b64");
|
|
81042
|
+
}
|
|
81043
|
+
const contentBytes = usingFrom ? readFileBoundedRaw(opts.contentFrom, "--content-from") : decodeBase64Buffer(opts.contentB64, "--content-b64");
|
|
81044
|
+
if (contentBytes.length === 0) {
|
|
81045
|
+
throw new CliError("content to insert cannot be empty");
|
|
81046
|
+
}
|
|
81047
|
+
let preWriteStat;
|
|
81048
|
+
try {
|
|
81049
|
+
preWriteStat = fs52.statSync(file2);
|
|
81050
|
+
} catch {
|
|
81051
|
+
}
|
|
81052
|
+
const result = readSection(file2, opts.after);
|
|
81053
|
+
if (result === null) {
|
|
81054
|
+
const available = listSections(file2);
|
|
81055
|
+
const messages = [`Section '${opts.after}' not found in '${file2}'`];
|
|
81056
|
+
if (available.length > 0) messages.push(didYouMean(available));
|
|
81057
|
+
throw new CliError(messages.join("\n"));
|
|
81058
|
+
}
|
|
81059
|
+
let rawText;
|
|
81060
|
+
try {
|
|
81061
|
+
rawText = fs52.readFileSync(file2, "utf-8");
|
|
81062
|
+
} catch (e) {
|
|
81063
|
+
mapFsError(e, void 0, file2);
|
|
81064
|
+
}
|
|
81065
|
+
if (rawText.charCodeAt(0) === 65279) rawText = rawText.slice(1);
|
|
81066
|
+
const eol = detectDominantEol(Buffer.from(rawText, "utf8"));
|
|
81067
|
+
const lfLines = rawText.replace(/\r\n/g, "\n").split("\n");
|
|
81068
|
+
const insertAt = result.lineEnd;
|
|
81069
|
+
const insertedLines = contentBytes.toString("utf8").replace(/\r\n/g, "\n").split("\n");
|
|
81070
|
+
if (insertedLines.length > 0 && insertedLines[insertedLines.length - 1] === "") insertedLines.pop();
|
|
81071
|
+
const mergedLfText = [...lfLines.slice(0, insertAt), ...insertedLines, ...lfLines.slice(insertAt)].join("\n");
|
|
81072
|
+
const mergedText = eol === "\n" ? mergedLfText : mergedLfText.replace(/\n/g, "\r\n");
|
|
81073
|
+
if (preWriteStat !== void 0) {
|
|
81074
|
+
let preRenameStat;
|
|
81075
|
+
try {
|
|
81076
|
+
preRenameStat = fs52.statSync(file2);
|
|
81077
|
+
} catch {
|
|
81078
|
+
}
|
|
81079
|
+
if (preRenameStat !== void 0 && (preRenameStat.mtimeMs !== preWriteStat.mtimeMs || preRenameStat.size !== preWriteStat.size)) {
|
|
81080
|
+
throw new CliError(`${file2} changed on disk while insert-section was running -- the file was modified concurrently, so the insert was NOT applied. Retry.`);
|
|
81081
|
+
}
|
|
81082
|
+
}
|
|
81083
|
+
try {
|
|
81084
|
+
atomicWriteBuffer(file2, Buffer.from(mergedText, "utf8"));
|
|
81085
|
+
} catch (e) {
|
|
81086
|
+
mapFsError(e, void 0, file2);
|
|
81087
|
+
}
|
|
81088
|
+
enqueueDirtyPathSafe(file2);
|
|
81089
|
+
const redirectNote = result.redirectedFrom !== void 0 ? ` (redirected from: '${result.redirectedFrom}')` : "";
|
|
81090
|
+
out(`inserted after '${result.heading}'${redirectNote} in ${file2}`);
|
|
81091
|
+
}
|
|
80572
81092
|
async function cmdGdriveSections(fileId, opts) {
|
|
80573
81093
|
const fetchOpts = { fresh: opts.fresh === true };
|
|
80574
81094
|
const text = await fetchDoc(fileId, fetchOpts);
|
|
@@ -80783,7 +81303,9 @@ function buildProgram() {
|
|
|
80783
81303
|
})
|
|
80784
81304
|
)
|
|
80785
81305
|
);
|
|
80786
|
-
program2.command("section <spec>").description(
|
|
81306
|
+
program2.command("section <spec>").description(
|
|
81307
|
+
'read one section from a file (spec: file::heading, or file::<unambiguous heading prefix> \u2014 e.g. "Lesson 16" resolves a longer unique heading), or list all sections with --list'
|
|
81308
|
+
).option("-j, --json", "output as JSON").option("--list", "list all section headings in the file instead of reading one").action(
|
|
80787
81309
|
(spec, opts) => opts.list === true ? runExit(() => runListSections({ file: spec, ...opts.json === true ? { json: true } : {} })) : runExitText(() => runSection({ spec, ...opts.json === true ? { json: true } : {} }))
|
|
80788
81310
|
);
|
|
80789
81311
|
program2.command("semantic <query>").description("semantic search (falls back to full-text search)").option("-l, --limit <n>", "max results").action(guard(cmdSemantic));
|
|
@@ -81144,7 +81666,33 @@ function buildProgram() {
|
|
|
81144
81666
|
).option("--summary", "line ranges and ours/base/theirs labels only, omitting the conflict content").option("--json", "emit the results as JSON instead of text").action(guard(cmdConflicts));
|
|
81145
81667
|
program2.command("screenshot <url> <destPath>").description("capture a local headless-browser screenshot, shrunk the same way local image reads are").option("--executable-path <path>", "Chrome/Chromium executable to launch (overrides config/auto-detect)").option("--width <n>", "viewport width in pixels (default: 1280)").option("--height <n>", "viewport height in pixels (default: 800)").option("--full-page", "capture the full scrollable page instead of just the viewport").action(guard(cmdScreenshot));
|
|
81146
81668
|
program2.command("write-file <dest>").description("write exact bytes to a file \u2014 handles backticks, quotes, $vars, CRLF without escaping\n\nModes: --b64 PAYLOAD (base64), --from SOURCE (copy file), or piped stdin").option("--from <source>", "copy bytes from this source file instead of stdin/base64").option("--b64 <payload>", "decode base64 payload and write to dest").action(guard(cmdWriteFile));
|
|
81147
|
-
program2.command("replace <file>").description("replace one string in a file; supply old/new text via --old-from/--new-from or --old-b64/--new-b64, and use --all to replace every occurrence").option("--old-from <source>", "read the old text from this source file").option("--new-from <source>", "read the new text from this source file").option("--old-b64 <payload>", "base64 payload for the old text").option("--new-b64 <payload>", "base64 payload for the new text").option("--all", "replace every occurrence instead of requiring a unique match").
|
|
81669
|
+
program2.command("replace <file>").description("replace one string in a file; supply old/new text via --old-from/--new-from or --old-b64/--new-b64, and use --all to replace every occurrence").option("--old-from <source>", "read the old text from this source file").option("--new-from <source>", "read the new text from this source file").option("--old-b64 <payload>", "base64 payload for the old text").option("--new-b64 <payload>", "base64 payload for the new text").option("--all", "replace every occurrence instead of requiring a unique match").option(
|
|
81670
|
+
"--normalize-newlines",
|
|
81671
|
+
"convert the old/new text's line endings (CRLF/LF) to match the target file's dominant line ending before matching, instead of requiring a byte-exact line-ending match"
|
|
81672
|
+
).action(guard(cmdReplace));
|
|
81673
|
+
program2.command("insert-section <file>").description(
|
|
81674
|
+
"insert content immediately after a matched section (spec resolved the same way as `section`: exact heading, or an unambiguous prefix), avoiding a stale byte-exact anchor for append-to-a-running-log edits"
|
|
81675
|
+
).requiredOption("--after <heading>", "heading text (or unambiguous prefix) to insert after").option("--content-from <source>", "read the content to insert from this source file").option("--content-b64 <payload>", "base64 payload for the content to insert").action(guard(cmdInsertSection));
|
|
81676
|
+
program2.command("note-add <file>").description(
|
|
81677
|
+
"attach a free-text architecture note to a file, or to one specific indexed symbol within it (--symbol NAME), fingerprinting what the note describes so `note-list --stale-only` can flag it once the code changes"
|
|
81678
|
+
).option("--symbol <name>", "attach the note to one indexed symbol in the file instead of the whole file").option("--content-from <source>", "read the note content (Markdown) from this source file").option("--content-b64 <payload>", "base64 payload for the note content").action(guard(cmdNoteAdd));
|
|
81679
|
+
program2.command("note-get <file>").description("read back the note attached to a file, or to one indexed symbol within it (--symbol NAME); flags whether it has gone stale since it was written").option("--symbol <name>", "read the note attached to this indexed symbol instead of the whole-file note").option("-j, --json", "output as JSON").action(
|
|
81680
|
+
(file2, opts) => runExitText(
|
|
81681
|
+
() => runNoteGet({
|
|
81682
|
+
file: file2,
|
|
81683
|
+
...opts.symbol !== void 0 ? { symbol: opts.symbol } : {},
|
|
81684
|
+
...opts.json === true ? { json: true } : {}
|
|
81685
|
+
})
|
|
81686
|
+
)
|
|
81687
|
+
);
|
|
81688
|
+
program2.command("note-list").description("list every recorded architecture note; --stale-only shows just the notes whose attached file/symbol changed since they were written").option("--stale-only", "only list notes whose fingerprint no longer matches the current index").option("-j, --json", "output as JSON").action(
|
|
81689
|
+
(opts) => runExitText(
|
|
81690
|
+
() => runNoteList({
|
|
81691
|
+
...opts.staleOnly === true ? { staleOnly: true } : {},
|
|
81692
|
+
...opts.json === true ? { json: true } : {}
|
|
81693
|
+
})
|
|
81694
|
+
)
|
|
81695
|
+
);
|
|
81148
81696
|
program2.command("gdrive-sections <file-id>").description("fetch and list sections from a public Google Doc").option("--heading <name>", "get content of one named section").option("--fresh", "skip the on-disk cache and force a live fetch").action(guard(cmdGdriveSections));
|
|
81149
81697
|
program2.command("compress").description("run a shell command and emit a compressed view of its output").requiredOption("-c, --cmd <command>", "the shell command to run, as one string").option("-f, --filter <name>", "filter name (auto-detected from the command when omitted)").option("--timeout <seconds>", "wall-clock timeout in seconds (0 = built-in default)").option("--no-compress", "stream output raw without compression (debug the wrapper)").option("--profile <name>", "compression profile: aggressive | balanced | minimal").option("--max-tokens <n>", "post-compress token cap (0 = no cap)").action(cmdCompress);
|
|
81150
81698
|
program2.command("version").description("print the token-goat version").action(
|