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