token-goat 2.8.4 → 2.9.1
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 +20 -8
- package/dist/{token-goat-chunk-NZFTWBGV.mjs → token-goat-chunk-4KD2N46D.mjs} +28 -27
- package/dist/{token-goat-chunk-CZALRRGN.mjs → token-goat-chunk-4ZDRTNGV.mjs} +9942 -95
- package/dist/{token-goat-chunk-L6YP6FKQ.mjs → token-goat-chunk-AUM22UQ2.mjs} +5 -5
- package/dist/{token-goat-chunk-LZOAPGWR.mjs → token-goat-chunk-CD6U5OD6.mjs} +28 -4
- package/dist/{token-goat-chunk-4OTIB7SB.mjs → token-goat-chunk-FDXINYK4.mjs} +2120 -3658
- package/dist/token-goat-chunk-LHMC5ENL.mjs +79 -0
- package/dist/{token-goat-chunk-IZRXU64B.mjs → token-goat-chunk-MR7MNSDR.mjs} +155 -517
- package/dist/{token-goat-chunk-ZQ3PUOP3.mjs → token-goat-chunk-RRNRPCLX.mjs} +167 -116
- package/dist/{token-goat-chunk-U4UI5F3S.mjs → token-goat-chunk-SQSB5P5P.mjs} +842 -368
- package/dist/{token-goat-chunk-RE7S7H26.mjs → token-goat-chunk-SXSWQMNP.mjs} +512 -206
- package/dist/{token-goat-chunk-HNODKNWO.mjs → token-goat-chunk-V5T3YNYR.mjs} +5 -5
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/package.json +1 -1
- package/dist/token-goat-chunk-E76UNTVK.mjs +0 -7133
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
enqueueDirtyPathSafe,
|
|
5
5
|
estimateTokensFromLength,
|
|
6
|
+
getProjectFileEntries,
|
|
7
|
+
getTrackedFiles,
|
|
8
|
+
indexRecallEntry,
|
|
6
9
|
isBlobStale,
|
|
7
|
-
isBuildCommand,
|
|
8
10
|
loadBlob,
|
|
9
|
-
sanitizeFtsQuery,
|
|
10
11
|
storeBlob
|
|
11
|
-
} from "./token-goat-chunk-
|
|
12
|
+
} from "./token-goat-chunk-FDXINYK4.mjs";
|
|
12
13
|
import {
|
|
13
14
|
SYMBOL_BODY_CHAR_CAP,
|
|
15
|
+
copilotCliMcpToolsDir,
|
|
16
|
+
countNoun,
|
|
14
17
|
extractErrorMessage,
|
|
18
|
+
fingerprintFile,
|
|
19
|
+
foldPath,
|
|
15
20
|
getDb,
|
|
16
|
-
|
|
21
|
+
getDisplayRoot,
|
|
17
22
|
normalizePath,
|
|
18
23
|
redactSecrets,
|
|
19
|
-
|
|
20
|
-
shortFingerprint
|
|
21
|
-
|
|
24
|
+
resolveIndexPath,
|
|
25
|
+
shortFingerprint,
|
|
26
|
+
toDisplayPath
|
|
27
|
+
} from "./token-goat-chunk-4ZDRTNGV.mjs";
|
|
22
28
|
import {
|
|
23
29
|
registerReset
|
|
24
30
|
} from "./token-goat-chunk-AO2QD2AG.mjs";
|
|
@@ -307,492 +313,9 @@ function normalizePayload(payload, harness = "claude") {
|
|
|
307
313
|
return result;
|
|
308
314
|
}
|
|
309
315
|
|
|
310
|
-
// src/recall_index.ts
|
|
311
|
-
var VALID_TYPES = ["bash", "web", "mcp"];
|
|
312
|
-
function isRecallCacheType(value) {
|
|
313
|
-
return VALID_TYPES.includes(value);
|
|
314
|
-
}
|
|
315
|
-
function indexRecallEntry(cacheType, id, label, content, storedAt) {
|
|
316
|
-
try {
|
|
317
|
-
const db = getDb(globalDbPath());
|
|
318
|
-
db.prepare(
|
|
319
|
-
`INSERT INTO cache_recall (cache_type, entry_id, label, content, stored_at)
|
|
320
|
-
VALUES (@cacheType, @id, @label, @content, @storedAt)
|
|
321
|
-
ON CONFLICT(cache_type, entry_id) DO UPDATE SET
|
|
322
|
-
label = excluded.label,
|
|
323
|
-
content = excluded.content,
|
|
324
|
-
stored_at = excluded.stored_at`
|
|
325
|
-
).run({ cacheType, id, label, content, storedAt });
|
|
326
|
-
} catch {
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
var _ftsAvailable = null;
|
|
330
|
-
function hasFtsTable() {
|
|
331
|
-
if (_ftsAvailable !== null) return _ftsAvailable;
|
|
332
|
-
try {
|
|
333
|
-
const db = getDb(globalDbPath());
|
|
334
|
-
const row = db.prepare(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'cache_recall_fts'`).get();
|
|
335
|
-
_ftsAvailable = row !== void 0;
|
|
336
|
-
} catch {
|
|
337
|
-
_ftsAvailable = false;
|
|
338
|
-
}
|
|
339
|
-
return _ftsAvailable;
|
|
340
|
-
}
|
|
341
|
-
function buildSnippet(content, query, maxLen = 160) {
|
|
342
|
-
const flat = content.replace(/\s+/g, " ").trim();
|
|
343
|
-
const tokens = query.split(/\s+/).filter(Boolean);
|
|
344
|
-
let idx = -1;
|
|
345
|
-
const lowerFlat = flat.toLowerCase();
|
|
346
|
-
for (const token of tokens) {
|
|
347
|
-
const at = lowerFlat.indexOf(token.toLowerCase());
|
|
348
|
-
if (at !== -1) {
|
|
349
|
-
idx = at;
|
|
350
|
-
break;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
if (idx === -1) {
|
|
354
|
-
return flat.length > maxLen ? flat.slice(0, maxLen) + "..." : flat;
|
|
355
|
-
}
|
|
356
|
-
const start = Math.max(0, idx - Math.floor(maxLen / 3));
|
|
357
|
-
const end = Math.min(flat.length, start + maxLen);
|
|
358
|
-
const prefix = start > 0 ? "..." : "";
|
|
359
|
-
const suffix = end < flat.length ? "..." : "";
|
|
360
|
-
return prefix + flat.slice(start, end) + suffix;
|
|
361
|
-
}
|
|
362
|
-
function toFtsMatchExpr(query) {
|
|
363
|
-
if (query.trim().length === 0) return null;
|
|
364
|
-
return sanitizeFtsQuery(query);
|
|
365
|
-
}
|
|
366
|
-
function mapRowsToHits(rows, snippetQuery) {
|
|
367
|
-
return rows.filter((r) => isRecallCacheType(r.cacheType)).map((r) => ({
|
|
368
|
-
id: r.id,
|
|
369
|
-
cacheType: r.cacheType,
|
|
370
|
-
label: r.label ?? "",
|
|
371
|
-
snippet: buildSnippet(r.content ?? "", snippetQuery),
|
|
372
|
-
storedAt: r.storedAt ?? 0
|
|
373
|
-
}));
|
|
374
|
-
}
|
|
375
|
-
function ftsSearch(query, type, limit) {
|
|
376
|
-
const matchExpr = toFtsMatchExpr(query);
|
|
377
|
-
if (matchExpr === null) return [];
|
|
378
|
-
const db = getDb(globalDbPath());
|
|
379
|
-
const rows = type !== void 0 ? db.prepare(
|
|
380
|
-
`SELECT c.entry_id AS id, c.cache_type AS cacheType, c.label AS label, c.content AS content, c.stored_at AS storedAt
|
|
381
|
-
FROM cache_recall_fts
|
|
382
|
-
JOIN cache_recall c ON c.row_id = cache_recall_fts.rowid
|
|
383
|
-
WHERE cache_recall_fts MATCH ? AND c.cache_type = ?
|
|
384
|
-
ORDER BY bm25(cache_recall_fts)
|
|
385
|
-
LIMIT ?`
|
|
386
|
-
).all(matchExpr, type, limit) : db.prepare(
|
|
387
|
-
`SELECT c.entry_id AS id, c.cache_type AS cacheType, c.label AS label, c.content AS content, c.stored_at AS storedAt
|
|
388
|
-
FROM cache_recall_fts
|
|
389
|
-
JOIN cache_recall c ON c.row_id = cache_recall_fts.rowid
|
|
390
|
-
WHERE cache_recall_fts MATCH ?
|
|
391
|
-
ORDER BY bm25(cache_recall_fts)
|
|
392
|
-
LIMIT ?`
|
|
393
|
-
).all(matchExpr, limit);
|
|
394
|
-
return mapRowsToHits(rows, query);
|
|
395
|
-
}
|
|
396
|
-
function likeSearch(query, type, limit) {
|
|
397
|
-
const trimmed = query.trim();
|
|
398
|
-
if (!trimmed) return [];
|
|
399
|
-
const db = getDb(globalDbPath());
|
|
400
|
-
const needle = `%${trimmed.replace(/\\/g, "\\\\").replace(/[%_]/g, (c) => `\\${c}`)}%`;
|
|
401
|
-
const rows = type !== void 0 ? db.prepare(
|
|
402
|
-
`SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt
|
|
403
|
-
FROM cache_recall
|
|
404
|
-
WHERE cache_type = ? AND (label LIKE ? ESCAPE '\\' OR content LIKE ? ESCAPE '\\')
|
|
405
|
-
ORDER BY stored_at DESC
|
|
406
|
-
LIMIT ?`
|
|
407
|
-
).all(type, needle, needle, limit) : db.prepare(
|
|
408
|
-
`SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt
|
|
409
|
-
FROM cache_recall
|
|
410
|
-
WHERE label LIKE ? ESCAPE '\\' OR content LIKE ? ESCAPE '\\'
|
|
411
|
-
ORDER BY stored_at DESC
|
|
412
|
-
LIMIT ?`
|
|
413
|
-
).all(needle, needle, limit);
|
|
414
|
-
return mapRowsToHits(rows, trimmed);
|
|
415
|
-
}
|
|
416
|
-
function listRecentRecall(opts = {}) {
|
|
417
|
-
const limit = opts.limit ?? 10;
|
|
418
|
-
const columns = `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt FROM cache_recall`;
|
|
419
|
-
try {
|
|
420
|
-
const db = getDb(globalDbPath());
|
|
421
|
-
const rows = opts.type !== void 0 ? db.prepare(`${columns} WHERE cache_type = ? ORDER BY stored_at DESC LIMIT ?`).all(opts.type, limit) : db.prepare(`${columns} ORDER BY stored_at DESC LIMIT ?`).all(limit);
|
|
422
|
-
return mapRowsToHits(rows, "");
|
|
423
|
-
} catch {
|
|
424
|
-
return [];
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
function searchRecall(query, opts = {}) {
|
|
428
|
-
const limit = opts.limit ?? 10;
|
|
429
|
-
if (query.trim() === "") return [];
|
|
430
|
-
try {
|
|
431
|
-
if (hasFtsTable()) {
|
|
432
|
-
return ftsSearch(query, opts.type, limit);
|
|
433
|
-
}
|
|
434
|
-
return likeSearch(query, opts.type, limit);
|
|
435
|
-
} catch {
|
|
436
|
-
try {
|
|
437
|
-
return likeSearch(query, opts.type, limit);
|
|
438
|
-
} catch {
|
|
439
|
-
return [];
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// src/bash_output_cache.ts
|
|
445
|
-
import { readdirSync, readFileSync, statSync } from "fs";
|
|
446
|
-
import { resolve } from "path";
|
|
447
|
-
var BASH_OUTPUT_SUBDIR = "bash_outputs";
|
|
448
|
-
var _byId = /* @__PURE__ */ new Map();
|
|
449
|
-
var COMMAND_PATTERNS = {
|
|
450
|
-
gitMutable: /^\s*git\s+(diff|status)\b/i,
|
|
451
|
-
gitImmutable: /^\s*git\s+show\s+[0-9a-f]{40}\b/i,
|
|
452
|
-
gitDiffScoped: /\s--\s+\S/,
|
|
453
|
-
dirListing: /^\s*(?:ls|eza|exa|dir|Get-ChildItem|gci)\b/i,
|
|
454
|
-
depList: /^\s*(?:npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:ls|list)\b|pip\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:list|freeze)\b|uv\s+pip\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:list|freeze)\b|pnpm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:list|ls)\b|yarn\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:list)\b|cargo\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*tree\b|bundle\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:list|show)\b|composer\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*show\b)/i,
|
|
455
|
-
npmInstall: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*(?:install|ci)\b/i,
|
|
456
|
-
npmAudit: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*audit\b(?!.*(?:--fix|fix)\b)/i,
|
|
457
|
-
npmOutdated: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=)?)*outdated\b/i,
|
|
458
|
-
envProbe: /^\s*(?:node\s+(?:-v|--version)|npm\s+(?:-v|--version)|python3?\s+(?:(?:-V)\b|--?version)|git\s+--version|uv\s+--version|go\s+version|rustc\s+--version|cargo\s+--version|java\s+--version|ruby\s+--version|gem\s+--version|php\s+--version|which\b|where\b)/i,
|
|
459
|
-
npx: /^\s*npx\s+(?:--?yes\s+)?(?!.*\b(?:install|add|remove|uninstall|i|rm|update|upgrade|set|get|publish|link|ci|audit|shrinkwrap|dedupe|prune|rebuild)\b)/i,
|
|
460
|
-
gitPush: /^\s*git\s+push\b/i,
|
|
461
|
-
testRunner: /^\s*(?:npx\s+)?(?:pytest|vitest|jest|go\s+test)\b/i,
|
|
462
|
-
lintCommand: /^\s*(?:(?:npx\s+)?eslint|(?:uv\s+run\s+)?ruff)\b/i,
|
|
463
|
-
npmRunScript: /^\s*npm\s+run(?:-script)?\b/i,
|
|
464
|
-
catCommand: /^\s*cat\b/i
|
|
465
|
-
};
|
|
466
|
-
var DEP_LOCKFILES = {
|
|
467
|
-
npm: ["package-lock.json", "yarn.lock"],
|
|
468
|
-
pip: ["requirements.txt"],
|
|
469
|
-
uv: ["uv.lock", "requirements.txt"],
|
|
470
|
-
pnpm: ["pnpm-lock.yaml"],
|
|
471
|
-
yarn: ["yarn.lock"],
|
|
472
|
-
cargo: ["Cargo.lock"],
|
|
473
|
-
bundle: ["Gemfile.lock"],
|
|
474
|
-
composer: ["composer.lock"]
|
|
475
|
-
};
|
|
476
|
-
function isCommandOfType(cmd, type) {
|
|
477
|
-
const pattern = COMMAND_PATTERNS[type];
|
|
478
|
-
return pattern?.test(cmd) ?? false;
|
|
479
|
-
}
|
|
480
|
-
var isGitMutableCommand = (cmd) => isCommandOfType(cmd, "gitMutable");
|
|
481
|
-
var isDirListingCommand = (cmd) => isCommandOfType(cmd, "dirListing");
|
|
482
|
-
var isDepListCommand = (cmd) => isCommandOfType(cmd, "depList");
|
|
483
|
-
var isNpmInstallCommand = (cmd) => isCommandOfType(cmd, "npmInstall");
|
|
484
|
-
var isNpmAuditCommand = (cmd) => isCommandOfType(cmd, "npmAudit");
|
|
485
|
-
var isNpmOutdatedCommand = (cmd) => isCommandOfType(cmd, "npmOutdated");
|
|
486
|
-
var isGitPushCommand = (cmd) => isCommandOfType(cmd, "gitPush");
|
|
487
|
-
var isTestRunnerCommand = (cmd) => isCommandOfType(cmd, "testRunner");
|
|
488
|
-
var isLintCommand = (cmd) => isCommandOfType(cmd, "lintCommand");
|
|
489
|
-
var isNpmRunScriptCommand = (cmd) => isCommandOfType(cmd, "npmRunScript");
|
|
490
|
-
var isCatCommand = (cmd) => isCommandOfType(cmd, "catCommand");
|
|
491
|
-
function isScopedGitStatusOrDiffStatCommand(cmd) {
|
|
492
|
-
if (!isCommandOfType(cmd, "gitMutable")) return false;
|
|
493
|
-
if (!isCommandOfType(cmd, "gitDiffScoped")) return false;
|
|
494
|
-
if (/^\s*git\s+diff\b/i.test(cmd) && !/--stat\b/.test(cmd)) return false;
|
|
495
|
-
return true;
|
|
496
|
-
}
|
|
497
|
-
function gitStateFingerprintSync(cwd) {
|
|
498
|
-
try {
|
|
499
|
-
const headResult = runGit(["rev-parse", "HEAD"], { cwd });
|
|
500
|
-
if (headResult.exitCode !== 0) return null;
|
|
501
|
-
const headSha = headResult.stdout.trim();
|
|
502
|
-
let statusHash = "";
|
|
503
|
-
const statusResult = runGit(["status", "--porcelain"], { cwd });
|
|
504
|
-
if (statusResult.exitCode === 0) {
|
|
505
|
-
statusHash = shortFingerprint(statusResult.stdout);
|
|
506
|
-
}
|
|
507
|
-
const key = `${headSha}\0${statusHash}`;
|
|
508
|
-
return shortFingerprint(key);
|
|
509
|
-
} catch {
|
|
510
|
-
return null;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
var DIR_FINGERPRINT_LISTING_CAP_ENTRIES = 1e4;
|
|
514
|
-
function dirStateFingerprintSync(path2) {
|
|
515
|
-
try {
|
|
516
|
-
const stat = statSync(path2);
|
|
517
|
-
if (!stat.isDirectory()) return null;
|
|
518
|
-
const entries = readdirSync(path2);
|
|
519
|
-
if (entries.length <= DIR_FINGERPRINT_LISTING_CAP_ENTRIES) {
|
|
520
|
-
return shortFingerprint(entries.slice().sort().join("\0"));
|
|
521
|
-
}
|
|
522
|
-
return shortFingerprint(stat.mtimeMs.toString());
|
|
523
|
-
} catch {
|
|
524
|
-
return null;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
var FILE_FINGERPRINT_CONTENT_CAP_BYTES = 2 * 1024 * 1024;
|
|
528
|
-
function fileStateFingerprintSync(path2) {
|
|
529
|
-
try {
|
|
530
|
-
const stat = statSync(path2);
|
|
531
|
-
if (!stat.isFile()) return null;
|
|
532
|
-
if (stat.size <= FILE_FINGERPRINT_CONTENT_CAP_BYTES) {
|
|
533
|
-
return shortFingerprint(readFileSync(path2));
|
|
534
|
-
}
|
|
535
|
-
return shortFingerprint(`${stat.mtimeMs}\0${stat.size}`);
|
|
536
|
-
} catch {
|
|
537
|
-
return null;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
function depLockfileFingerprintSync(cmd, cwd) {
|
|
541
|
-
if (!cwd) return null;
|
|
542
|
-
const stripped = cmd.trim();
|
|
543
|
-
const firstToken = stripped.split(/\s+/)[0]?.toLowerCase() || "";
|
|
544
|
-
if (!firstToken) return null;
|
|
545
|
-
const candidates = firstToken === "uv" ? DEP_LOCKFILES["uv"] : DEP_LOCKFILES[firstToken];
|
|
546
|
-
if (!candidates) return null;
|
|
547
|
-
for (const lockfile of candidates) {
|
|
548
|
-
try {
|
|
549
|
-
const content = readFileSync(resolve(cwd, lockfile));
|
|
550
|
-
return shortFingerprint(content);
|
|
551
|
-
} catch {
|
|
552
|
-
continue;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
return null;
|
|
556
|
-
}
|
|
557
|
-
function normalizeCommandForCacheKey(cmd) {
|
|
558
|
-
const trimmed = cmd.trim();
|
|
559
|
-
const tokens = [];
|
|
560
|
-
let current = "";
|
|
561
|
-
let quoteChar = null;
|
|
562
|
-
const flush = () => {
|
|
563
|
-
if (current.length > 0) {
|
|
564
|
-
tokens.push(current);
|
|
565
|
-
current = "";
|
|
566
|
-
}
|
|
567
|
-
};
|
|
568
|
-
for (let i = 0; i < trimmed.length; i++) {
|
|
569
|
-
const ch = trimmed[i];
|
|
570
|
-
if (quoteChar !== null) {
|
|
571
|
-
current += ch;
|
|
572
|
-
if (ch === quoteChar) quoteChar = null;
|
|
573
|
-
continue;
|
|
574
|
-
}
|
|
575
|
-
if (ch === '"' || ch === "'") {
|
|
576
|
-
quoteChar = ch;
|
|
577
|
-
current += ch;
|
|
578
|
-
continue;
|
|
579
|
-
}
|
|
580
|
-
if (/\s/.test(ch)) {
|
|
581
|
-
flush();
|
|
582
|
-
continue;
|
|
583
|
-
}
|
|
584
|
-
current += ch === "\\" ? "/" : ch;
|
|
585
|
-
}
|
|
586
|
-
flush();
|
|
587
|
-
const normalized_tokens = tokens.map((token) => {
|
|
588
|
-
if (token.startsWith("-") || ["&&", "||", "|", ">", ">>", ";", "&"].includes(token)) {
|
|
589
|
-
return token;
|
|
590
|
-
}
|
|
591
|
-
if (token.startsWith("./") && !token.startsWith("../")) {
|
|
592
|
-
token = token.slice(2);
|
|
593
|
-
}
|
|
594
|
-
if (token.endsWith("/") && token !== "/") {
|
|
595
|
-
token = token.slice(0, -1);
|
|
596
|
-
}
|
|
597
|
-
return token || ".";
|
|
598
|
-
});
|
|
599
|
-
return normalized_tokens.join(" ");
|
|
600
|
-
}
|
|
601
|
-
async function commandHash(command, cwd = null) {
|
|
602
|
-
const normalized = normalizeCommandForCacheKey(command);
|
|
603
|
-
let key = cwd ? `${normalizePath(cwd)}\0${normalized}` : normalized;
|
|
604
|
-
if (cwd && isGitMutableCommand(command)) {
|
|
605
|
-
const fp = gitStateFingerprintSync(cwd);
|
|
606
|
-
if (fp) key = `${key}\0git:${fp}`;
|
|
607
|
-
}
|
|
608
|
-
if (cwd && isDirListingCommand(command)) {
|
|
609
|
-
const target = extractFirstPathArg(command, cwd, cwd);
|
|
610
|
-
if (target) {
|
|
611
|
-
const fp = dirStateFingerprintSync(target);
|
|
612
|
-
if (fp) key = `${key}\0dir:${fp}`;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
if (isDepListCommand(command)) {
|
|
616
|
-
const fp = depLockfileFingerprintSync(command, cwd);
|
|
617
|
-
if (fp) key = `${key}\0lockfile:${fp}`;
|
|
618
|
-
}
|
|
619
|
-
if (cwd && (isNpmInstallCommand(command) || isNpmAuditCommand(command) || isNpmOutdatedCommand(command))) {
|
|
620
|
-
const fp = depLockfileFingerprintSync(command, cwd);
|
|
621
|
-
if (fp) key = `${key}\0npm-install:${fp}`;
|
|
622
|
-
}
|
|
623
|
-
return shortFingerprint(key);
|
|
624
|
-
}
|
|
625
|
-
function computeBashFingerprints(command, cwd) {
|
|
626
|
-
const fingerprints = {};
|
|
627
|
-
if (cwd && (isGitMutableCommand(command) || isGitPushCommand(command) || isTestRunnerCommand(command) || isLintCommand(command) || isBuildCommand(command) || isNpmRunScriptCommand(command))) {
|
|
628
|
-
const fp = gitStateFingerprintSync(cwd);
|
|
629
|
-
if (fp) fingerprints.git = fp;
|
|
630
|
-
}
|
|
631
|
-
if (cwd && isDirListingCommand(command)) {
|
|
632
|
-
const target = extractFirstPathArg(command, cwd, cwd);
|
|
633
|
-
if (target) {
|
|
634
|
-
const fp = dirStateFingerprintSync(target);
|
|
635
|
-
if (fp) fingerprints.dir = fp;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
if (isDepListCommand(command) || cwd && (isNpmInstallCommand(command) || isNpmAuditCommand(command) || isNpmOutdatedCommand(command))) {
|
|
639
|
-
const fp = depLockfileFingerprintSync(command, cwd);
|
|
640
|
-
if (fp) fingerprints.lockfile = fp;
|
|
641
|
-
}
|
|
642
|
-
if (cwd && isCatCommand(command)) {
|
|
643
|
-
const target = extractFirstPathArg(command, cwd, null);
|
|
644
|
-
if (target) {
|
|
645
|
-
const fp = fileStateFingerprintSync(target);
|
|
646
|
-
if (fp) fingerprints.file = fp;
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
return Object.keys(fingerprints).length > 0 ? fingerprints : void 0;
|
|
650
|
-
}
|
|
651
|
-
function isBashEntryStale(entry, command, cwd) {
|
|
652
|
-
const stored = entry.fingerprints;
|
|
653
|
-
if (!stored) return false;
|
|
654
|
-
const current = computeBashFingerprints(command, cwd);
|
|
655
|
-
if (stored.git !== void 0 && stored.git !== current?.git) return true;
|
|
656
|
-
if (stored.dir !== void 0 && stored.dir !== current?.dir) return true;
|
|
657
|
-
if (stored.lockfile !== void 0 && stored.lockfile !== current?.lockfile) return true;
|
|
658
|
-
if (stored.file !== void 0 && stored.file !== current?.file) return true;
|
|
659
|
-
return false;
|
|
660
|
-
}
|
|
661
|
-
function tokenizeShellArgs(cmd) {
|
|
662
|
-
const trimmed = cmd.trim();
|
|
663
|
-
const tokens = [];
|
|
664
|
-
let current = "";
|
|
665
|
-
let quoteChar = null;
|
|
666
|
-
let hasToken = false;
|
|
667
|
-
for (let i = 0; i < trimmed.length; i++) {
|
|
668
|
-
const ch = trimmed[i];
|
|
669
|
-
if (quoteChar !== null) {
|
|
670
|
-
if (ch === quoteChar) {
|
|
671
|
-
quoteChar = null;
|
|
672
|
-
} else {
|
|
673
|
-
current += ch;
|
|
674
|
-
}
|
|
675
|
-
continue;
|
|
676
|
-
}
|
|
677
|
-
if (ch === '"' || ch === "'") {
|
|
678
|
-
quoteChar = ch;
|
|
679
|
-
hasToken = true;
|
|
680
|
-
continue;
|
|
681
|
-
}
|
|
682
|
-
if (/\s/.test(ch)) {
|
|
683
|
-
if (hasToken) {
|
|
684
|
-
tokens.push(current);
|
|
685
|
-
current = "";
|
|
686
|
-
hasToken = false;
|
|
687
|
-
}
|
|
688
|
-
continue;
|
|
689
|
-
}
|
|
690
|
-
current += ch;
|
|
691
|
-
hasToken = true;
|
|
692
|
-
}
|
|
693
|
-
if (hasToken) tokens.push(current);
|
|
694
|
-
return tokens;
|
|
695
|
-
}
|
|
696
|
-
function extractFirstPathArg(cmd, cwd, fallback) {
|
|
697
|
-
const tokens = tokenizeShellArgs(cmd);
|
|
698
|
-
for (let i = 1; i < tokens.length; i++) {
|
|
699
|
-
const token = tokens[i];
|
|
700
|
-
if (!token.startsWith("-")) {
|
|
701
|
-
if (!token.startsWith("/")) {
|
|
702
|
-
return resolve(cwd, token);
|
|
703
|
-
}
|
|
704
|
-
return token;
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
return fallback;
|
|
708
|
-
}
|
|
709
|
-
var ISSUE_LINE_PATTERN = /\b(?:error|fail(?:ed|ure)?|warning)\b/i;
|
|
710
|
-
function summarizeOutputDelta(oldOutput, newOutput) {
|
|
711
|
-
if (oldOutput === newOutput) return null;
|
|
712
|
-
const oldLines = oldOutput.split("\n");
|
|
713
|
-
const newLines = newOutput.split("\n");
|
|
714
|
-
const oldIssueLines = oldLines.filter((l) => ISSUE_LINE_PATTERN.test(l));
|
|
715
|
-
if (oldIssueLines.length > 0) {
|
|
716
|
-
const newIssueLines = newLines.filter((l) => ISSUE_LINE_PATTERN.test(l));
|
|
717
|
-
const priorTotal = oldIssueLines.length;
|
|
718
|
-
const availableCounts = /* @__PURE__ */ new Map();
|
|
719
|
-
for (const l of newIssueLines) availableCounts.set(l, (availableCounts.get(l) ?? 0) + 1);
|
|
720
|
-
let resolved = 0;
|
|
721
|
-
for (const l of oldIssueLines) {
|
|
722
|
-
const avail = availableCounts.get(l) ?? 0;
|
|
723
|
-
if (avail > 0) {
|
|
724
|
-
availableCounts.set(l, avail - 1);
|
|
725
|
-
} else {
|
|
726
|
-
resolved++;
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
const remaining = newIssueLines.length;
|
|
730
|
-
return `[token-goat: delta] ${resolved} of ${priorTotal} prior issues resolved; remaining: ${remaining}`;
|
|
731
|
-
}
|
|
732
|
-
return `[token-goat: delta] output changed: ${oldLines.length} -> ${newLines.length} lines`;
|
|
733
|
-
}
|
|
734
|
-
async function storeBashOutput(command, output, exitCode, cwd = null) {
|
|
735
|
-
const id = await commandHash(command, cwd);
|
|
736
|
-
const fingerprints = computeBashFingerprints(command, cwd);
|
|
737
|
-
const redactedOutput = redactSecrets(output).text;
|
|
738
|
-
const redactedCommand = redactSecrets(command).text;
|
|
739
|
-
const entry = {
|
|
740
|
-
id,
|
|
741
|
-
command: redactedCommand,
|
|
742
|
-
output: redactedOutput,
|
|
743
|
-
exitCode,
|
|
744
|
-
storedAt: Date.now(),
|
|
745
|
-
sizeBytes: Buffer.byteLength(redactedOutput, "utf-8"),
|
|
746
|
-
...fingerprints ? { fingerprints } : {}
|
|
747
|
-
};
|
|
748
|
-
_byId.set(id, entry);
|
|
749
|
-
storeBlob(BASH_OUTPUT_SUBDIR, id, entry);
|
|
750
|
-
indexRecallEntry("bash", id, redactedCommand, `${redactedCommand}
|
|
751
|
-
${redactedOutput}`, entry.storedAt);
|
|
752
|
-
return id;
|
|
753
|
-
}
|
|
754
|
-
function coerceBashEntry(raw) {
|
|
755
|
-
if (raw === null || typeof raw !== "object") return null;
|
|
756
|
-
const o = raw;
|
|
757
|
-
if (typeof o["id"] !== "string" || typeof o["command"] !== "string" || typeof o["output"] !== "string" || typeof o["exitCode"] !== "number" || typeof o["storedAt"] !== "number" || typeof o["sizeBytes"] !== "number") {
|
|
758
|
-
return null;
|
|
759
|
-
}
|
|
760
|
-
const entry = {
|
|
761
|
-
id: o["id"],
|
|
762
|
-
command: o["command"],
|
|
763
|
-
output: o["output"],
|
|
764
|
-
exitCode: o["exitCode"],
|
|
765
|
-
storedAt: o["storedAt"],
|
|
766
|
-
sizeBytes: o["sizeBytes"]
|
|
767
|
-
};
|
|
768
|
-
const rawFingerprints = o["fingerprints"];
|
|
769
|
-
if (rawFingerprints !== null && typeof rawFingerprints === "object") {
|
|
770
|
-
const f = rawFingerprints;
|
|
771
|
-
const fingerprints = {};
|
|
772
|
-
if (typeof f["git"] === "string") fingerprints.git = f["git"];
|
|
773
|
-
if (typeof f["dir"] === "string") fingerprints.dir = f["dir"];
|
|
774
|
-
if (typeof f["lockfile"] === "string") fingerprints.lockfile = f["lockfile"];
|
|
775
|
-
if (typeof f["file"] === "string") fingerprints.file = f["file"];
|
|
776
|
-
if (Object.keys(fingerprints).length > 0) return { ...entry, fingerprints };
|
|
777
|
-
}
|
|
778
|
-
return entry;
|
|
779
|
-
}
|
|
780
|
-
function getBashOutput(id) {
|
|
781
|
-
const hit = _byId.get(id);
|
|
782
|
-
if (hit !== void 0) return hit;
|
|
783
|
-
if (isBlobStale(BASH_OUTPUT_SUBDIR, id)) return null;
|
|
784
|
-
const entry = coerceBashEntry(loadBlob(BASH_OUTPUT_SUBDIR, id));
|
|
785
|
-
if (entry === null) return null;
|
|
786
|
-
_byId.set(id, entry);
|
|
787
|
-
return entry;
|
|
788
|
-
}
|
|
789
|
-
registerReset(() => {
|
|
790
|
-
_byId = /* @__PURE__ */ new Map();
|
|
791
|
-
});
|
|
792
|
-
|
|
793
316
|
// src/web_cache.ts
|
|
794
317
|
var WEB_OUTPUT_SUBDIR = "web_outputs";
|
|
795
|
-
var
|
|
318
|
+
var _byId = /* @__PURE__ */ new Map();
|
|
796
319
|
var _rawById = /* @__PURE__ */ new Map();
|
|
797
320
|
var _urlIndex = /* @__PURE__ */ new Map();
|
|
798
321
|
function cacheIdForUrl(url) {
|
|
@@ -801,7 +324,7 @@ function cacheIdForUrl(url) {
|
|
|
801
324
|
function storeWebOutput(url, content, dedupKey = url, rawContent) {
|
|
802
325
|
const cacheId = cacheIdForUrl(dedupKey);
|
|
803
326
|
const redactedContent = redactSecrets(content).text;
|
|
804
|
-
|
|
327
|
+
_byId.set(cacheId, redactedContent);
|
|
805
328
|
_urlIndex.set(url, cacheId);
|
|
806
329
|
const redactedRaw = rawContent !== void 0 && rawContent !== content ? redactSecrets(rawContent).text : void 0;
|
|
807
330
|
if (redactedRaw !== void 0) _rawById.set(cacheId, redactedRaw);
|
|
@@ -819,12 +342,12 @@ function coerceWebBlob(raw) {
|
|
|
819
342
|
return { url: typeof o["url"] === "string" ? o["url"] : null, content: o["content"], raw: typeof o["raw"] === "string" ? o["raw"] : null };
|
|
820
343
|
}
|
|
821
344
|
function getWebOutput(cacheId) {
|
|
822
|
-
const hit =
|
|
345
|
+
const hit = _byId.get(cacheId);
|
|
823
346
|
if (hit !== void 0) return hit;
|
|
824
347
|
if (isBlobStale(WEB_OUTPUT_SUBDIR, cacheId)) return null;
|
|
825
348
|
const blob = coerceWebBlob(loadBlob(WEB_OUTPUT_SUBDIR, cacheId));
|
|
826
349
|
if (blob === null) return null;
|
|
827
|
-
|
|
350
|
+
_byId.set(cacheId, blob.content);
|
|
828
351
|
if (blob.raw !== null) _rawById.set(cacheId, blob.raw);
|
|
829
352
|
if (blob.url !== null) _urlIndex.set(blob.url, cacheId);
|
|
830
353
|
return blob.content;
|
|
@@ -841,7 +364,7 @@ function getWebOutputByUrlFromDisk(url, dedupKey = url) {
|
|
|
841
364
|
return { cacheId, content };
|
|
842
365
|
}
|
|
843
366
|
registerReset(() => {
|
|
844
|
-
|
|
367
|
+
_byId = /* @__PURE__ */ new Map();
|
|
845
368
|
_rawById = /* @__PURE__ */ new Map();
|
|
846
369
|
_urlIndex = /* @__PURE__ */ new Map();
|
|
847
370
|
});
|
|
@@ -928,12 +451,135 @@ function formatCommandManifest(manifest) {
|
|
|
928
451
|
return lines.join("\n").trimEnd();
|
|
929
452
|
}
|
|
930
453
|
|
|
454
|
+
// src/reconcile.ts
|
|
455
|
+
import * as fs2 from "node:fs";
|
|
456
|
+
var DEFAULT_RECONCILE_BUDGET_MS = 1500;
|
|
457
|
+
function runReconcile(opts = {}) {
|
|
458
|
+
const raw = reconcileProject(opts);
|
|
459
|
+
const root = getDisplayRoot(opts.cwd ?? process.cwd());
|
|
460
|
+
const display = (paths) => paths.map((p) => toDisplayPath(root, p)).sort();
|
|
461
|
+
const result = {
|
|
462
|
+
...raw,
|
|
463
|
+
changed: display(raw.changed),
|
|
464
|
+
added: display(raw.added),
|
|
465
|
+
removed: display(raw.removed)
|
|
466
|
+
};
|
|
467
|
+
if (opts.json === true) {
|
|
468
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
469
|
+
`);
|
|
470
|
+
return 0;
|
|
471
|
+
}
|
|
472
|
+
const lines = [];
|
|
473
|
+
if (isReconcileClean(result)) {
|
|
474
|
+
if (!result.trackedUnavailable) {
|
|
475
|
+
lines.push(`Index matches disk: ${countNoun(result.scanned, "file")} checked in ${result.elapsedMs}ms.`);
|
|
476
|
+
}
|
|
477
|
+
} else {
|
|
478
|
+
const verb = opts.dryRun === true ? "would reindex" : "queued for reindexing";
|
|
479
|
+
const dropVerb = opts.dryRun === true ? "would drop from the index" : "queued for removal";
|
|
480
|
+
if (result.changed.length > 0) lines.push(`${countNoun(result.changed.length, "file")} changed since indexing (${verb}):`);
|
|
481
|
+
for (const f of result.changed) lines.push(` ~ ${f}`);
|
|
482
|
+
if (result.added.length > 0) lines.push(`${countNoun(result.added.length, "file")} not in the index (${verb}):`);
|
|
483
|
+
for (const f of result.added) lines.push(` + ${f}`);
|
|
484
|
+
if (result.removed.length > 0) lines.push(`${countNoun(result.removed.length, "file")} indexed but gone from disk (${dropVerb}):`);
|
|
485
|
+
for (const f of result.removed) lines.push(` - ${f}`);
|
|
486
|
+
lines.push(`Checked ${countNoun(result.scanned, "file")} in ${result.elapsedMs}ms.`);
|
|
487
|
+
}
|
|
488
|
+
if (result.mtimeOnly > 0) {
|
|
489
|
+
lines.push(`${countNoun(result.mtimeOnly, "file")} had a newer timestamp but identical content, so ${result.mtimeOnly === 1 ? "it was" : "they were"} left alone.`);
|
|
490
|
+
}
|
|
491
|
+
if (result.trackedUnavailable) {
|
|
492
|
+
lines.push("This project has an index but git listed no files in it, so nothing could be compared and no deletions were computed. Run token-goat inside the repository, or reindex with --walk if this directory is deliberately not under git.");
|
|
493
|
+
}
|
|
494
|
+
if (result.budgetExhausted) {
|
|
495
|
+
lines.push(`Stopped at the ${result.elapsedMs}ms budget with ${countNoun(result.unscanned, "file")} unchecked, so there may be more drift; deletions were not computed at all, because an unchecked file is indistinguishable from a deleted one. Raise --budget-ms for a complete sweep.`);
|
|
496
|
+
}
|
|
497
|
+
process.stdout.write(`${lines.join("\n")}
|
|
498
|
+
`);
|
|
499
|
+
return 0;
|
|
500
|
+
}
|
|
501
|
+
function isReconcileClean(r) {
|
|
502
|
+
return r.changed.length === 0 && r.added.length === 0 && r.removed.length === 0;
|
|
503
|
+
}
|
|
504
|
+
function reconcileProject(opts = {}) {
|
|
505
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
506
|
+
const budgetMs = opts.budgetMs ?? DEFAULT_RECONCILE_BUDGET_MS;
|
|
507
|
+
const startedAt = Date.now();
|
|
508
|
+
const tracked = getTrackedFiles(cwd);
|
|
509
|
+
const projectRoot = resolveIndexPath(".", cwd);
|
|
510
|
+
const indexed = getProjectFileEntries(projectRoot);
|
|
511
|
+
const changed = [];
|
|
512
|
+
const added = [];
|
|
513
|
+
const seenOnDisk = /* @__PURE__ */ new Set();
|
|
514
|
+
let mtimeOnly = 0;
|
|
515
|
+
let scanned = 0;
|
|
516
|
+
let budgetExhausted = false;
|
|
517
|
+
for (const file of tracked) {
|
|
518
|
+
if (Date.now() - startedAt > budgetMs) {
|
|
519
|
+
budgetExhausted = true;
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
scanned++;
|
|
523
|
+
const folded = foldPath(normalizePath(file));
|
|
524
|
+
seenOnDisk.add(folded);
|
|
525
|
+
const entry = indexed.get(folded);
|
|
526
|
+
if (entry === void 0) {
|
|
527
|
+
added.push(file);
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
let mtimeMs;
|
|
531
|
+
try {
|
|
532
|
+
mtimeMs = fs2.statSync(file).mtimeMs;
|
|
533
|
+
} catch {
|
|
534
|
+
changed.push(file);
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
if (entry.mtime !== 0 && mtimeMs === entry.mtime) continue;
|
|
538
|
+
const diskSha = fingerprintFile(file);
|
|
539
|
+
if (diskSha === null) {
|
|
540
|
+
changed.push(file);
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
if (diskSha === entry.sha) {
|
|
544
|
+
mtimeOnly++;
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
changed.push(file);
|
|
548
|
+
}
|
|
549
|
+
const trackedUnavailable = tracked.length === 0 && indexed.size > 0;
|
|
550
|
+
const removed = [];
|
|
551
|
+
if (!budgetExhausted && !trackedUnavailable) {
|
|
552
|
+
for (const [folded, entry] of indexed) {
|
|
553
|
+
if (!seenOnDisk.has(folded)) removed.push(entry.filePath);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
let enqueued = 0;
|
|
557
|
+
if (opts.dryRun !== true) {
|
|
558
|
+
for (const p of [...changed, ...added, ...removed]) {
|
|
559
|
+
enqueueDirtyPathSafe(resolveIndexPath(p, cwd), { alreadyResolved: true });
|
|
560
|
+
enqueued++;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return {
|
|
564
|
+
scanned,
|
|
565
|
+
changed,
|
|
566
|
+
added,
|
|
567
|
+
removed,
|
|
568
|
+
mtimeOnly,
|
|
569
|
+
budgetExhausted,
|
|
570
|
+
trackedUnavailable,
|
|
571
|
+
unscanned: Math.max(0, tracked.length - scanned),
|
|
572
|
+
enqueued,
|
|
573
|
+
elapsedMs: Date.now() - startedAt
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
|
|
931
577
|
// src/stdin_json.ts
|
|
932
578
|
var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
|
|
933
579
|
var MAX_STDIN_WALL_MS = 6e4;
|
|
934
580
|
var MAX_STDIN_BYTES = 64 * 1024 * 1024;
|
|
935
581
|
function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDIN_BYTES) {
|
|
936
|
-
return new Promise((
|
|
582
|
+
return new Promise((resolve, reject) => {
|
|
937
583
|
const chunks = [];
|
|
938
584
|
let totalBytes = 0;
|
|
939
585
|
let settled = false;
|
|
@@ -979,7 +625,7 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
|
|
|
979
625
|
return;
|
|
980
626
|
}
|
|
981
627
|
try {
|
|
982
|
-
|
|
628
|
+
resolve(JSON.parse(text));
|
|
983
629
|
} catch (err) {
|
|
984
630
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
985
631
|
}
|
|
@@ -995,10 +641,10 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
|
|
|
995
641
|
}
|
|
996
642
|
|
|
997
643
|
// src/symbol_body_probe.ts
|
|
998
|
-
import * as
|
|
644
|
+
import * as fs3 from "fs";
|
|
999
645
|
var OVERSIZED_BODY_PROBE_SQL = `SELECT 1 FROM symbols WHERE LENGTH(body) > ${SYMBOL_BODY_CHAR_CAP} LIMIT 1`;
|
|
1000
646
|
function checkSymbolBodySize(dbPath) {
|
|
1001
|
-
if (!
|
|
647
|
+
if (!fs3.existsSync(dbPath)) {
|
|
1002
648
|
return { name: "Symbol body size", status: "ok", message: "no database yet" };
|
|
1003
649
|
}
|
|
1004
650
|
try {
|
|
@@ -1022,7 +668,7 @@ function checkSymbolBodySize(dbPath) {
|
|
|
1022
668
|
}
|
|
1023
669
|
|
|
1024
670
|
// src/resident_context.ts
|
|
1025
|
-
import * as
|
|
671
|
+
import * as fs4 from "node:fs";
|
|
1026
672
|
var LARGE_TASK_LIST_BYTES = 2e4;
|
|
1027
673
|
var LARGE_SKILL_BODY_BYTES = 2e4;
|
|
1028
674
|
var SKILL_BODY_REPEAT_THRESHOLD = 2;
|
|
@@ -1183,13 +829,13 @@ function repeatedSkillBodyHint(injections) {
|
|
|
1183
829
|
function readTranscriptTail(transcriptPath, maxBytes = RESIDENT_TAIL_MAX_BYTES) {
|
|
1184
830
|
let fd = null;
|
|
1185
831
|
try {
|
|
1186
|
-
const stat =
|
|
832
|
+
const stat = fs4.statSync(transcriptPath);
|
|
1187
833
|
if (!stat.isFile() || stat.size === 0) return [];
|
|
1188
834
|
const start = Math.max(0, stat.size - maxBytes);
|
|
1189
835
|
const length = stat.size - start;
|
|
1190
|
-
fd =
|
|
836
|
+
fd = fs4.openSync(transcriptPath, "r");
|
|
1191
837
|
const buf = Buffer.allocUnsafe(length);
|
|
1192
|
-
const read =
|
|
838
|
+
const read = fs4.readSync(fd, buf, 0, length, start);
|
|
1193
839
|
const lines = buf.subarray(0, read).toString("utf8").split("\n");
|
|
1194
840
|
if (start > 0) lines.shift();
|
|
1195
841
|
return lines;
|
|
@@ -1198,7 +844,7 @@ function readTranscriptTail(transcriptPath, maxBytes = RESIDENT_TAIL_MAX_BYTES)
|
|
|
1198
844
|
} finally {
|
|
1199
845
|
if (fd !== null) {
|
|
1200
846
|
try {
|
|
1201
|
-
|
|
847
|
+
fs4.closeSync(fd);
|
|
1202
848
|
} catch {
|
|
1203
849
|
}
|
|
1204
850
|
}
|
|
@@ -1227,18 +873,6 @@ export {
|
|
|
1227
873
|
readCopilotMcpTools,
|
|
1228
874
|
GEMINI_TOOL_NAME_MAP,
|
|
1229
875
|
normalizePayload,
|
|
1230
|
-
isRecallCacheType,
|
|
1231
|
-
indexRecallEntry,
|
|
1232
|
-
listRecentRecall,
|
|
1233
|
-
searchRecall,
|
|
1234
|
-
BASH_OUTPUT_SUBDIR,
|
|
1235
|
-
isScopedGitStatusOrDiffStatCommand,
|
|
1236
|
-
normalizeCommandForCacheKey,
|
|
1237
|
-
commandHash,
|
|
1238
|
-
isBashEntryStale,
|
|
1239
|
-
summarizeOutputDelta,
|
|
1240
|
-
storeBashOutput,
|
|
1241
|
-
getBashOutput,
|
|
1242
876
|
WEB_OUTPUT_SUBDIR,
|
|
1243
877
|
storeWebOutput,
|
|
1244
878
|
getWebOutput,
|
|
@@ -1249,6 +883,10 @@ export {
|
|
|
1249
883
|
flattenCommandNames,
|
|
1250
884
|
filterCommandManifest,
|
|
1251
885
|
formatCommandManifest,
|
|
886
|
+
DEFAULT_RECONCILE_BUDGET_MS,
|
|
887
|
+
runReconcile,
|
|
888
|
+
isReconcileClean,
|
|
889
|
+
reconcileProject,
|
|
1252
890
|
checkSymbolBodySize,
|
|
1253
891
|
createResidentContextStats,
|
|
1254
892
|
accumulateResidentLine,
|