token-goat 2.9.13 → 2.9.15
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 +28 -1
- package/dist/token-goat-chunk-2X2EBBC6.mjs +277 -0
- package/dist/token-goat-chunk-3BTK54F3.mjs +1733 -0
- package/dist/token-goat-chunk-3NSDDTGL.mjs +34 -0
- package/dist/token-goat-chunk-4EXFN2AW.mjs +29 -0
- package/dist/token-goat-chunk-5V7DAC7V.mjs +123 -0
- package/dist/token-goat-chunk-6DLVZDB6.mjs +34 -0
- package/dist/token-goat-chunk-7ZYK25AO.mjs +24 -0
- package/dist/token-goat-chunk-A4JYKD5H.mjs +144 -0
- package/dist/token-goat-chunk-AH6QILZM.mjs +26 -0
- package/dist/token-goat-chunk-ASYEPR3S.mjs +212 -0
- package/dist/{token-goat-chunk-RDITECDL.mjs → token-goat-chunk-ATIFTMRC.mjs} +31 -13
- package/dist/{token-goat-chunk-ZZI3IDQZ.mjs → token-goat-chunk-BL5LNGBG.mjs} +3935 -11949
- package/dist/{token-goat-chunk-4NXUKV7D.mjs → token-goat-chunk-C5JIO6HK.mjs} +8 -4
- package/dist/{token-goat-chunk-FZU7GMUS.mjs → token-goat-chunk-DQ4J5AFF.mjs} +50 -18
- package/dist/token-goat-chunk-ERTXEKB6.mjs +228 -0
- package/dist/{token-goat-chunk-6B44WLIF.mjs → token-goat-chunk-GIIHUSZX.mjs} +142 -21
- package/dist/token-goat-chunk-GMOUBOX4.mjs +386 -0
- package/dist/{token-goat-chunk-3ESRORNM.mjs → token-goat-chunk-GMQQA7E4.mjs} +12576 -12326
- package/dist/token-goat-chunk-IVUQLQWN.mjs +2046 -0
- package/dist/token-goat-chunk-K7F2BFIK.mjs +2430 -0
- package/dist/{token-goat-chunk-U7X6LQD2.mjs → token-goat-chunk-LCZBPOIN.mjs} +10197 -9717
- package/dist/token-goat-chunk-LMFO66YD.mjs +331 -0
- package/dist/token-goat-chunk-LT7JRU6K.mjs +22 -0
- package/dist/token-goat-chunk-MZDIJJ3R.mjs +420 -0
- package/dist/token-goat-chunk-NDPO7GAH.mjs +177 -0
- package/dist/token-goat-chunk-NDRP4KJQ.mjs +4371 -0
- package/dist/token-goat-chunk-NEI4NC54.mjs +424 -0
- package/dist/token-goat-chunk-NU7TLMQK.mjs +585 -0
- package/dist/token-goat-chunk-OSUFN2FV.mjs +326 -0
- package/dist/token-goat-chunk-OUGNPMDA.mjs +959 -0
- package/dist/token-goat-chunk-PM76YS22.mjs +1341 -0
- package/dist/token-goat-chunk-S4XRY446.mjs +2637 -0
- package/dist/{token-goat-chunk-YOA4N6WA.mjs → token-goat-chunk-SFAS46RE.mjs} +5 -3
- package/dist/{token-goat-chunk-QWSUZWFP.mjs → token-goat-chunk-SZWYESBS.mjs} +793 -650
- package/dist/{token-goat-chunk-B3CTCQTH.mjs → token-goat-chunk-XEPXYDPI.mjs} +3 -2
- package/dist/token-goat-chunk-XTQAOTSO.mjs +89 -0
- package/dist/token-goat-chunk-Y4AFKTHK.mjs +22 -0
- package/dist/token-goat-chunk-YKG35VHC.mjs +228 -0
- package/dist/{token-goat-chunk-JOXLE672.mjs → token-goat-chunk-YQ7WI2CO.mjs} +990 -106
- package/dist/{token-goat-chunk-2WC4ZUXN.mjs → token-goat-chunk-YZX7EFG4.mjs} +1 -1
- package/dist/token-goat-chunk-Z6UXPYJA.mjs +62 -0
- package/dist/token-goat-hook.mjs +16 -8
- package/dist/token-goat.core.mjs +27 -10
- package/docs/cli.md +9 -6
- package/package.json +4 -2
- package/dist/token-goat-chunk-FQCNJV4V.mjs +0 -693
- package/dist/token-goat-chunk-JVNPCQB7.mjs +0 -31
- package/dist/token-goat-chunk-P2PU4CR5.mjs +0 -26
- package/dist/token-goat-chunk-QKXBGBQR.mjs +0 -3653
- package/dist/token-goat-chunk-UM47DRD3.mjs +0 -242
- package/dist/token-goat-chunk-UMXJN7DI.mjs +0 -5521
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
|
+
sanitizeFtsQuery
|
|
5
|
+
} from "./token-goat-chunk-2X2EBBC6.mjs";
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_MAX_AGE_MS,
|
|
8
|
+
blobPath,
|
|
4
9
|
countRedactionPlaceholders,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
parse,
|
|
10
|
+
isBlobStale,
|
|
11
|
+
loadBlob,
|
|
8
12
|
recordStat,
|
|
9
13
|
recordUnmappedTool,
|
|
10
14
|
redactSecrets,
|
|
11
15
|
savedTokensFromBytes,
|
|
12
|
-
|
|
16
|
+
storeBlob,
|
|
13
17
|
stripAnsiEscapes
|
|
14
|
-
} from "./token-goat-chunk-
|
|
18
|
+
} from "./token-goat-chunk-IVUQLQWN.mjs";
|
|
19
|
+
import {
|
|
20
|
+
getDb
|
|
21
|
+
} from "./token-goat-chunk-K7F2BFIK.mjs";
|
|
22
|
+
import {
|
|
23
|
+
detectHarness,
|
|
24
|
+
loadConfig,
|
|
25
|
+
parse,
|
|
26
|
+
shortFingerprint,
|
|
27
|
+
stringify
|
|
28
|
+
} from "./token-goat-chunk-S4XRY446.mjs";
|
|
15
29
|
import {
|
|
16
30
|
registerReset
|
|
17
31
|
} from "./token-goat-chunk-EEIDFMEM.mjs";
|
|
@@ -20,20 +34,18 @@ import {
|
|
|
20
34
|
assertWriteInScope,
|
|
21
35
|
atomicWriteText,
|
|
22
36
|
backupFile,
|
|
23
|
-
displaySafeText,
|
|
24
37
|
ensureDirSync,
|
|
25
38
|
escapeRegExp,
|
|
26
39
|
extractErrorMessage,
|
|
40
|
+
globalDbPath,
|
|
27
41
|
hookCommandFor,
|
|
28
42
|
hookPowershellCommand,
|
|
29
|
-
neutralizeOutsideFences,
|
|
30
|
-
normalizeDarwinSystemAlias,
|
|
31
43
|
projectPathIsConsultable,
|
|
32
44
|
projectScopeRoot,
|
|
33
45
|
recordCreatedConfig,
|
|
34
46
|
removeCreatedBackups,
|
|
35
47
|
removeFileInScope,
|
|
36
|
-
|
|
48
|
+
runGit,
|
|
37
49
|
safeSlice,
|
|
38
50
|
stripDelimitedBlock,
|
|
39
51
|
stripOwnHooksFromMap,
|
|
@@ -43,7 +55,14 @@ import {
|
|
|
43
55
|
withInstallScope,
|
|
44
56
|
writeIfDifferent,
|
|
45
57
|
writeJsonSettings
|
|
46
|
-
} from "./token-goat-chunk-
|
|
58
|
+
} from "./token-goat-chunk-PM76YS22.mjs";
|
|
59
|
+
import {
|
|
60
|
+
displaySafeText,
|
|
61
|
+
neutralizeOutsideFences,
|
|
62
|
+
normalizeDarwinSystemAlias,
|
|
63
|
+
normalizePath,
|
|
64
|
+
resolveIndexPath
|
|
65
|
+
} from "./token-goat-chunk-ERTXEKB6.mjs";
|
|
47
66
|
import {
|
|
48
67
|
init_define_import_meta_env
|
|
49
68
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -513,6 +532,27 @@ const TOOL_TO_TG = {
|
|
|
513
532
|
exit_plan_mode: 'ExitPlanMode',
|
|
514
533
|
}
|
|
515
534
|
|
|
535
|
+
function foldToolName(name) {
|
|
536
|
+
return typeof name === 'string' ? name.toLowerCase().replace(/[_-]/g, '') : ''
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const FOLDED_TOOL_TO_TG = {}
|
|
540
|
+
for (const [k, v] of Object.entries(TOOL_TO_TG)) {
|
|
541
|
+
FOLDED_TOOL_TO_TG[foldToolName(k)] = v
|
|
542
|
+
}
|
|
543
|
+
// Also register folded entries for canonical TG tool names directly (e.g. WebSearch -> websearch)
|
|
544
|
+
for (const v of Object.values(TOOL_TO_TG)) {
|
|
545
|
+
FOLDED_TOOL_TO_TG[foldToolName(v)] = v
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function resolveCanonicalToolName(name) {
|
|
549
|
+
if (!name || typeof name !== 'string') return name
|
|
550
|
+
const direct = TOOL_TO_TG[name]
|
|
551
|
+
if (direct !== undefined) return direct
|
|
552
|
+
const folded = foldToolName(name)
|
|
553
|
+
return FOLDED_TOOL_TO_TG[folded] || name
|
|
554
|
+
}
|
|
555
|
+
|
|
516
556
|
// Confirmed via github/copilot-cli#3349 (open, unresolved as of writing): some
|
|
517
557
|
// real Copilot CLI invocations send toolArgs as a JSON-*encoded string*
|
|
518
558
|
// rather than a parsed object, contradicting the documented schema. Left
|
|
@@ -583,11 +623,12 @@ function remapToolInput(copilotToolName, input) {
|
|
|
583
623
|
let out = input
|
|
584
624
|
// Add the canonical key alongside the original rather than renaming it, so nothing that
|
|
585
625
|
// might read the original 'path'/'shellId' key elsewhere (e.g. a future handler) loses it.
|
|
586
|
-
const
|
|
626
|
+
const folded = foldToolName(copilotToolName)
|
|
627
|
+
const pathKey = FILE_PATH_ARG_KEY[copilotToolName] || (folded === 'view' || folded === 'edit' || folded === 'create' ? 'path' : undefined)
|
|
587
628
|
if (pathKey !== undefined && pathKey in out) {
|
|
588
629
|
out = Object.assign({}, out, { file_path: out[pathKey] })
|
|
589
630
|
}
|
|
590
|
-
const idKey = POLL_ID_ARG_KEY[copilotToolName]
|
|
631
|
+
const idKey = POLL_ID_ARG_KEY[copilotToolName] || (folded === 'readbash' || folded === 'readpowershell' ? 'shellId' : undefined)
|
|
591
632
|
if (idKey !== undefined && idKey in out) {
|
|
592
633
|
out = Object.assign({}, out, { bash_id: out[idKey] })
|
|
593
634
|
}
|
|
@@ -752,7 +793,7 @@ async function main() {
|
|
|
752
793
|
let originalToolArgs = {}
|
|
753
794
|
if (toolName) {
|
|
754
795
|
originalToolArgs = parseMaybeJsonObject(payload && payload.toolArgs)
|
|
755
|
-
canonical.tool_name =
|
|
796
|
+
canonical.tool_name = resolveCanonicalToolName(toolName)
|
|
756
797
|
canonical.tool_input = remapToolInput(toolName, originalToolArgs)
|
|
757
798
|
}
|
|
758
799
|
|
|
@@ -842,12 +883,12 @@ async function main() {
|
|
|
842
883
|
return
|
|
843
884
|
}
|
|
844
885
|
|
|
845
|
-
process.stdout.write(JSON.stringify(translate(copilotEvent, resp, toolName, originalToolArgs)))
|
|
886
|
+
process.stdout.write(JSON.stringify(translate(copilotEvent, resp, toolName, originalToolArgs, payload)))
|
|
846
887
|
}
|
|
847
888
|
|
|
848
889
|
${MATERIALIZE_SHRUNK_IMAGE_JS}
|
|
849
890
|
|
|
850
|
-
function translate(copilotEvent, resp, toolName, originalToolArgs) {
|
|
891
|
+
function translate(copilotEvent, resp, toolName, originalToolArgs, payload) {
|
|
851
892
|
if (copilotEvent === 'preToolUse') {
|
|
852
893
|
const hso = resp && resp.hookSpecificOutput
|
|
853
894
|
const denied = resp && (resp.decision === 'block' || (hso && hso.permissionDecision === 'deny'))
|
|
@@ -861,7 +902,7 @@ function translate(copilotEvent, resp, toolName, originalToolArgs) {
|
|
|
861
902
|
return { modifiedArgs: updated }
|
|
862
903
|
}
|
|
863
904
|
// Image shrink has no context channel on this event (Copilot's preToolUse output schema carries no additionalContext; docs/hook-channel-matrix.md footnote 7) -- translate it into a rewritten view path pointing at a materialized shrunk copy instead, via the same modifiedArgs channel the updatedInput branch above already uses. modifiedArgs REPLACES the tool call's args wholesale (ESr in the 1.0.80 bundle, see this module's header docblock), so the full original toolArgs are spread and only Copilot's own path key is swapped.
|
|
864
|
-
if (toolName === 'view') {
|
|
905
|
+
if (toolName === 'view' || foldToolName(toolName) === 'view') {
|
|
865
906
|
const shrunkPath = materializeShrunkImage(extractContext(resp))
|
|
866
907
|
if (shrunkPath) return { modifiedArgs: Object.assign({}, originalToolArgs, { path: shrunkPath }) }
|
|
867
908
|
}
|
|
@@ -869,52 +910,20 @@ function translate(copilotEvent, resp, toolName, originalToolArgs) {
|
|
|
869
910
|
}
|
|
870
911
|
|
|
871
912
|
if (copilotEvent === 'postToolUse') {
|
|
872
|
-
//
|
|
873
|
-
//
|
|
874
|
-
// toolResult.resultType === "success", then does n.toolResultJson && CSr(e.toolResult,
|
|
875
|
-
// n.toolResultJson), where CSr (offset 2032350) is an in-place Object.assign; the mutated
|
|
876
|
-
// object is re-serialized back to native in the postTool callback at offset 1793926. So
|
|
877
|
-
// modifiedResult IS honored on this event, and token-goat's rewriteOutput producers
|
|
878
|
-
// (compression, injection fencing, image shrink) really do reach the model. resultType is
|
|
879
|
-
// hardcoded 'success' because success is the only branch this event ever runs on.
|
|
880
|
-
//
|
|
881
|
-
// additionalContext on THIS event is dropped on the JS path. grep -abo "onAdditionalContext:"
|
|
882
|
-
// app.js returns nothing, so the callback is never supplied, and the two "onAdditionalContext?"
|
|
883
|
-
// call sites (offsets 2041896 and 2043300) are both no-ops. preToolsExecution (offset 2041832)
|
|
884
|
-
// additionally pushes each context into an array that IS drained into the native return
|
|
885
|
-
// payload (additional_contexts, offset 1791950); postToolExecution has no such push and its
|
|
886
|
-
// native return payload (offset 1793926) has no additional_contexts key. Residual, stated
|
|
887
|
-
// honestly: the native hookProcessorPostToolUse might fold additionalContext into the
|
|
888
|
-
// toolResultJson it returns, and that was NOT verified. The evidence leans against it, since
|
|
889
|
-
// the failure sibling path appends its context explicitly in JS via
|
|
890
|
-
// hookAppendPostToolUseFailureContext and there is no success-path counterpart. The
|
|
891
|
-
// out.additionalContext below is kept as cheap best-effort, not as a channel anything should
|
|
892
|
-
// depend on -- see src/pending_context.ts, whose whole design depended on it.
|
|
893
|
-
//
|
|
894
|
-
// Failed tool calls never reach this event. postToolUse and postToolUseFailure are two
|
|
895
|
-
// distinct hook events (both listed in the runtime.node hook-event enum at offset 101618150),
|
|
896
|
-
// and the shipped copilot-sdk/types.d.ts says onPostToolUse "does not fire for non-success
|
|
897
|
-
// results". The failure event cannot carry a fence either: PostToolUseFailureHookInput carries
|
|
898
|
-
// only a stringified error message, not the tool result, and PostToolUseFailureHookOutput
|
|
899
|
-
// consumes only additionalContext -- "modifiedResult or suppressOutput are not honored for
|
|
900
|
-
// failure hooks". rejected/denied/timeout results trigger no post hook at all. So on Copilot,
|
|
901
|
-
// the output of a failed tool call reaches the model unfenced, uncompressed and unshrunk, and
|
|
902
|
-
// no response shape this shim could emit changes that. That gap is real and still open. What
|
|
903
|
-
// is now wired is the narrower thing that IS possible there: the postToolUseFailure branch
|
|
904
|
-
// below carries advisory text alongside the failure, and never rewrites it.
|
|
905
|
-
//
|
|
906
|
-
// Emitted camelCase-only. The inbound side around line 247 also tolerates a snake_case "VS
|
|
907
|
-
// Code compatible" shape, and this comment used to justify the camelCase choice by claiming
|
|
908
|
-
// PascalCase event registration selects the snake_case response format. That reasoning is
|
|
909
|
-
// UNVERIFIED in 1.0.80: every hook-response field in the native string tables is camelCase,
|
|
910
|
-
// and the snake_case hits in the bundle are unrelated internal Rust identifiers. camelCase
|
|
911
|
-
// stays because it is what the bundle reads; the old justification is no longer asserted.
|
|
913
|
+
// modifiedResult is honored on success: rewriteOutput (compression, fencing, image shrink) reaches LLM.
|
|
914
|
+
// additionalContext is dropped on Copilot CLI JS path; we fold context into modifiedResult.textResultForLlm.
|
|
912
915
|
const hso = resp && resp.hookSpecificOutput
|
|
913
916
|
const updatedToolOutput = hso && hso.updatedToolOutput
|
|
914
917
|
const context = extractContext(resp)
|
|
915
918
|
const out = {}
|
|
916
919
|
if (typeof updatedToolOutput === 'string') {
|
|
917
920
|
out.modifiedResult = { resultType: 'success', textResultForLlm: updatedToolOutput }
|
|
921
|
+
} else if (context) {
|
|
922
|
+
const rawResult = payload && payload.toolResult
|
|
923
|
+
const originalText = rawResult && (typeof rawResult.textResultForLlm === 'string' ? rawResult.textResultForLlm : typeof rawResult.text_result_for_llm === 'string' ? rawResult.text_result_for_llm : undefined)
|
|
924
|
+
if (typeof originalText === 'string') {
|
|
925
|
+
out.modifiedResult = { resultType: 'success', textResultForLlm: originalText + '\\n\\n[token-goat: ' + context + ']' }
|
|
926
|
+
}
|
|
918
927
|
}
|
|
919
928
|
if (context) out.additionalContext = context
|
|
920
929
|
return out
|
|
@@ -1035,6 +1044,29 @@ main()
|
|
|
1035
1044
|
process.exitCode = 0
|
|
1036
1045
|
})
|
|
1037
1046
|
`;
|
|
1047
|
+
function foldToolName(name) {
|
|
1048
|
+
return typeof name === "string" ? name.toLowerCase().replace(/[_-]/g, "") : "";
|
|
1049
|
+
}
|
|
1050
|
+
var TOOL_TO_TG_MAPPING = {
|
|
1051
|
+
bash: "Bash",
|
|
1052
|
+
powershell: "Bash",
|
|
1053
|
+
read_bash: "BashOutput",
|
|
1054
|
+
read_powershell: "BashOutput",
|
|
1055
|
+
view: "Read",
|
|
1056
|
+
create: "Write",
|
|
1057
|
+
edit: "Edit",
|
|
1058
|
+
web_fetch: "WebFetch",
|
|
1059
|
+
web_search: "WebSearch",
|
|
1060
|
+
grep: "Grep",
|
|
1061
|
+
glob: "Glob",
|
|
1062
|
+
skill: "Skill",
|
|
1063
|
+
exit_plan_mode: "ExitPlanMode"
|
|
1064
|
+
};
|
|
1065
|
+
var FOLDED_MAPPING = {};
|
|
1066
|
+
for (const [k, v] of Object.entries(TOOL_TO_TG_MAPPING)) {
|
|
1067
|
+
FOLDED_MAPPING[foldToolName(k)] = v;
|
|
1068
|
+
FOLDED_MAPPING[foldToolName(v)] = v;
|
|
1069
|
+
}
|
|
1038
1070
|
|
|
1039
1071
|
// src/bridges/copilot_cli_install.ts
|
|
1040
1072
|
var COPILOT_CLI_HOOK_EVENTS = [
|
|
@@ -1747,6 +1779,10 @@ function getCwd(event) {
|
|
|
1747
1779
|
const value = event.raw && typeof event.raw === "object" ? event.raw["cwd"] : void 0;
|
|
1748
1780
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
1749
1781
|
}
|
|
1782
|
+
function getTranscriptPath(event) {
|
|
1783
|
+
const value = event.raw && typeof event.raw === "object" ? event.raw["transcript_path"] : void 0;
|
|
1784
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
1785
|
+
}
|
|
1750
1786
|
function extractToolResponseField(raw, keys) {
|
|
1751
1787
|
const resp = raw["tool_response"];
|
|
1752
1788
|
if (typeof resp === "string") return resp;
|
|
@@ -2047,7 +2083,7 @@ function toolMatcherFor(eventName) {
|
|
|
2047
2083
|
if (parts.length === 0) return null;
|
|
2048
2084
|
return parts.join("|");
|
|
2049
2085
|
}
|
|
2050
|
-
function
|
|
2086
|
+
function foldToolName2(name) {
|
|
2051
2087
|
return name.toLowerCase().replace(/[_-]/g, "");
|
|
2052
2088
|
}
|
|
2053
2089
|
function noteUnrecognizedTool(event, list) {
|
|
@@ -2060,8 +2096,8 @@ function noteUnrecognizedTool(event, list) {
|
|
|
2060
2096
|
named.push(want);
|
|
2061
2097
|
}
|
|
2062
2098
|
if (named.length === 0) return;
|
|
2063
|
-
const folded =
|
|
2064
|
-
const nearMiss = named.find((n) =>
|
|
2099
|
+
const folded = foldToolName2(toolName);
|
|
2100
|
+
const nearMiss = named.find((n) => foldToolName2(n) === folded) ?? null;
|
|
2065
2101
|
recordUnmappedTool(toolName, event.eventName, nearMiss);
|
|
2066
2102
|
}
|
|
2067
2103
|
async function runHook(event) {
|
|
@@ -2954,9 +2990,800 @@ function byteLength(s) {
|
|
|
2954
2990
|
return utf8Len(s);
|
|
2955
2991
|
}
|
|
2956
2992
|
|
|
2957
|
-
// src/
|
|
2993
|
+
// src/bash_output_cache.ts
|
|
2994
|
+
init_define_import_meta_env();
|
|
2995
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
2996
|
+
import { resolve as resolve4 } from "path";
|
|
2997
|
+
|
|
2998
|
+
// src/hints/lang_patterns.ts
|
|
2999
|
+
init_define_import_meta_env();
|
|
3000
|
+
var LOCK_FILE_NAMES = /* @__PURE__ */ new Set([
|
|
3001
|
+
"package-lock.json",
|
|
3002
|
+
"yarn.lock",
|
|
3003
|
+
"pnpm-lock.yaml",
|
|
3004
|
+
"cargo.lock",
|
|
3005
|
+
"poetry.lock",
|
|
3006
|
+
"pipfile.lock",
|
|
3007
|
+
"uv.lock",
|
|
3008
|
+
"gemfile.lock",
|
|
3009
|
+
"go.sum",
|
|
3010
|
+
"composer.lock",
|
|
3011
|
+
"mix.lock",
|
|
3012
|
+
"pubspec.lock",
|
|
3013
|
+
"package.resolved"
|
|
3014
|
+
]);
|
|
3015
|
+
var MANIFEST_FILE_NAMES = /* @__PURE__ */ new Set([
|
|
3016
|
+
"package.json",
|
|
3017
|
+
"pyproject.toml",
|
|
3018
|
+
"cargo.toml",
|
|
3019
|
+
"go.mod",
|
|
3020
|
+
"go.work",
|
|
3021
|
+
"pom.xml",
|
|
3022
|
+
"build.gradle",
|
|
3023
|
+
"build.gradle.kts",
|
|
3024
|
+
"settings.gradle",
|
|
3025
|
+
"composer.json",
|
|
3026
|
+
"gemfile",
|
|
3027
|
+
"mix.exs",
|
|
3028
|
+
"pubspec.yaml",
|
|
3029
|
+
"cmakelists.txt",
|
|
3030
|
+
"makefile",
|
|
3031
|
+
"project.clj",
|
|
3032
|
+
// TypeScript / JavaScript project configs
|
|
3033
|
+
"tsconfig.json",
|
|
3034
|
+
"jsconfig.json",
|
|
3035
|
+
// Bundler / framework configs
|
|
3036
|
+
"vite.config.ts",
|
|
3037
|
+
"vite.config.js",
|
|
3038
|
+
"webpack.config.js",
|
|
3039
|
+
"webpack.config.ts",
|
|
3040
|
+
"rollup.config.js",
|
|
3041
|
+
"rollup.config.ts",
|
|
3042
|
+
"esbuild.config.js",
|
|
3043
|
+
"next.config.js",
|
|
3044
|
+
"next.config.ts",
|
|
3045
|
+
"nuxt.config.ts"
|
|
3046
|
+
]);
|
|
3047
|
+
var MANIFEST_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
3048
|
+
".cabal"
|
|
3049
|
+
]);
|
|
3050
|
+
var MANIFEST_BASENAME_PATTERNS = [
|
|
3051
|
+
/^tsconfig(\..+)?\.json$/i
|
|
3052
|
+
];
|
|
3053
|
+
var BUILD_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
3054
|
+
"dist",
|
|
3055
|
+
"target",
|
|
3056
|
+
"build",
|
|
3057
|
+
"out",
|
|
3058
|
+
"__pycache__",
|
|
3059
|
+
".next",
|
|
3060
|
+
".nuxt",
|
|
3061
|
+
".output",
|
|
3062
|
+
".gradle",
|
|
3063
|
+
"_build",
|
|
3064
|
+
".build",
|
|
3065
|
+
"pkg",
|
|
3066
|
+
"obj"
|
|
3067
|
+
]);
|
|
3068
|
+
var ALWAYS_GENERATED_EXTS = /* @__PURE__ */ new Set([
|
|
3069
|
+
".pyc",
|
|
3070
|
+
".pyo",
|
|
3071
|
+
".pyd",
|
|
3072
|
+
".class",
|
|
3073
|
+
".o",
|
|
3074
|
+
".a",
|
|
3075
|
+
".so",
|
|
3076
|
+
".dylib",
|
|
3077
|
+
".dll",
|
|
3078
|
+
".tsbuildinfo"
|
|
3079
|
+
]);
|
|
3080
|
+
var CONDITIONALLY_GENERATED_EXTS = /* @__PURE__ */ new Set([
|
|
3081
|
+
".map",
|
|
3082
|
+
".d.ts"
|
|
3083
|
+
]);
|
|
3084
|
+
var BUILD_COMMAND_PATTERNS = [
|
|
3085
|
+
// Rust / Cargo
|
|
3086
|
+
/^\s*cargo\s+(build|test|run|check|clippy)\b/i,
|
|
3087
|
+
// Go
|
|
3088
|
+
/^\s*go\s+(build|test|run|vet)\b/i,
|
|
3089
|
+
// Maven
|
|
3090
|
+
/^\s*mvn\b/i,
|
|
3091
|
+
// Gradle (direct or wrapper)
|
|
3092
|
+
/^\s*(?:gradle|\.\/gradlew|gradlew)\b/i,
|
|
3093
|
+
// Python / pip
|
|
3094
|
+
/^\s*pip\s+(install|freeze)\b/i,
|
|
3095
|
+
// npm audit / npm outdated — read-only reporting commands with no side effects, safe to cache/recall. Each resolves a `lockfile`-scoped fingerprint via bash_output_cache.ts's isNpmAuditCommand/isNpmOutdatedCommand, but that fingerprinting never actually ran outside their own unit tests until this entry, since neither subcommand was recognized by isBuildCommand (the generic cache-reachability gate in hooks_bash.ts). `npm install`/`npm ci`/`npm list`/`npm ls` are deliberately left out — see the negative-case test in lang_patterns.test.ts.
|
|
3096
|
+
/^\s*npm\s+(audit|outdated)\b/i,
|
|
3097
|
+
// Poetry
|
|
3098
|
+
/^\s*poetry\s+(install|update)\b/i,
|
|
3099
|
+
// uv
|
|
3100
|
+
/^\s*uv\s+(sync|pip\s+install)\b/i,
|
|
3101
|
+
// Bundler (Ruby)
|
|
3102
|
+
/^\s*bundle\s+(install|update)\b/i,
|
|
3103
|
+
// Mix (Elixir)
|
|
3104
|
+
/^\s*mix\s+(deps\.get|compile|test)\b/i,
|
|
3105
|
+
// dotnet
|
|
3106
|
+
/^\s*dotnet\s+(build|test|restore)\b/i,
|
|
3107
|
+
// Make
|
|
3108
|
+
/^\s*make\b/i,
|
|
3109
|
+
// CMake build
|
|
3110
|
+
/^\s*cmake\s+--build\b/i,
|
|
3111
|
+
// Rake (Ruby)
|
|
3112
|
+
/^\s*rake\b/i,
|
|
3113
|
+
// TypeScript compiler (direct and via npx)
|
|
3114
|
+
/^\s*tsc(?:\s|$)/i,
|
|
3115
|
+
/^\s*npx\s+tsc(?:\s|$)/i,
|
|
3116
|
+
// Vite
|
|
3117
|
+
/^\s*vite\s+(build|dev|preview)\b/i,
|
|
3118
|
+
// Next.js
|
|
3119
|
+
/^\s*next\s+(build|dev|start)\b/i,
|
|
3120
|
+
// Nuxt
|
|
3121
|
+
/^\s*nuxt\s+(build|dev)\b/i,
|
|
3122
|
+
// Webpack
|
|
3123
|
+
/^\s*webpack\b/i,
|
|
3124
|
+
// esbuild
|
|
3125
|
+
/^\s*esbuild\b/i,
|
|
3126
|
+
// Rollup
|
|
3127
|
+
/^\s*rollup\b/i,
|
|
3128
|
+
// Turbo
|
|
3129
|
+
/^\s*turbo\s+(build|dev)\b/i
|
|
3130
|
+
];
|
|
3131
|
+
var LOCK_FILE_COUNT = LOCK_FILE_NAMES.size;
|
|
3132
|
+
var MANIFEST_FILE_COUNT = MANIFEST_FILE_NAMES.size + MANIFEST_EXTENSIONS.size + MANIFEST_BASENAME_PATTERNS.length;
|
|
3133
|
+
var BUILD_DIR_COUNT = BUILD_DIR_NAMES.size;
|
|
3134
|
+
var GENERATED_EXT_COUNT = ALWAYS_GENERATED_EXTS.size + CONDITIONALLY_GENERATED_EXTS.size;
|
|
3135
|
+
function isLockFile(basename2) {
|
|
3136
|
+
return LOCK_FILE_NAMES.has(basename2.toLowerCase());
|
|
3137
|
+
}
|
|
3138
|
+
function isManifestFile(basename2) {
|
|
3139
|
+
const lower = basename2.toLowerCase();
|
|
3140
|
+
if (MANIFEST_FILE_NAMES.has(lower)) return true;
|
|
3141
|
+
const dot = lower.lastIndexOf(".");
|
|
3142
|
+
if (dot !== -1 && MANIFEST_EXTENSIONS.has(lower.slice(dot))) return true;
|
|
3143
|
+
if (MANIFEST_BASENAME_PATTERNS.some((re) => re.test(basename2))) return true;
|
|
3144
|
+
return false;
|
|
3145
|
+
}
|
|
3146
|
+
function pathSegments(filePath) {
|
|
3147
|
+
return filePath.split(/[/\\]/).filter((s) => s.length > 0);
|
|
3148
|
+
}
|
|
3149
|
+
function isInBuildDir(filePath) {
|
|
3150
|
+
const segments = pathSegments(filePath);
|
|
3151
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
3152
|
+
const seg = segments[i];
|
|
3153
|
+
if (seg !== void 0 && BUILD_DIR_NAMES.has(seg.toLowerCase())) return true;
|
|
3154
|
+
}
|
|
3155
|
+
return false;
|
|
3156
|
+
}
|
|
3157
|
+
function isGeneratedFile(filePath) {
|
|
3158
|
+
const lower = filePath.toLowerCase();
|
|
3159
|
+
for (const ext of ALWAYS_GENERATED_EXTS) {
|
|
3160
|
+
if (lower.endsWith(ext)) return true;
|
|
3161
|
+
}
|
|
3162
|
+
for (const ext of CONDITIONALLY_GENERATED_EXTS) {
|
|
3163
|
+
if (lower.endsWith(ext) && isInBuildDir(filePath)) return true;
|
|
3164
|
+
}
|
|
3165
|
+
return false;
|
|
3166
|
+
}
|
|
3167
|
+
function isBuildCommand(cmd) {
|
|
3168
|
+
return BUILD_COMMAND_PATTERNS.some((re) => re.test(cmd));
|
|
3169
|
+
}
|
|
3170
|
+
var TEST_RUNNER_COMMAND_PATTERNS = [
|
|
3171
|
+
// pytest (Python)
|
|
3172
|
+
/^pytest(?:\s|$)/i,
|
|
3173
|
+
// Jest / Vitest (direct or via npx)
|
|
3174
|
+
/^(?:npx\s+)?jest(?:\s|$)/i,
|
|
3175
|
+
/^(?:npx\s+)?vitest(?:\s+run|\s+watch)?(?:\s|$)/i,
|
|
3176
|
+
// Go
|
|
3177
|
+
/^go test(?:\s|$)/i,
|
|
3178
|
+
// Rust / Cargo
|
|
3179
|
+
/^cargo test(?:\s|$)/i,
|
|
3180
|
+
// npm test / npm run test
|
|
3181
|
+
/^npm (?:test|run test)(?:\s|$)/i,
|
|
3182
|
+
// yarn test / yarn run test / pnpm test / pnpm run test
|
|
3183
|
+
/^(?:yarn|pnpm)(?:\s+run)? test(?:\s|$)/i
|
|
3184
|
+
];
|
|
3185
|
+
function isTestRunnerCommand(cmd) {
|
|
3186
|
+
return TEST_RUNNER_COMMAND_PATTERNS.some((re) => re.test(cmd));
|
|
3187
|
+
}
|
|
3188
|
+
var MONITORING_COMMAND_PATTERNS = [
|
|
3189
|
+
// GitHub CI
|
|
3190
|
+
{ pattern: /^gh run (?:watch|view|list)/, recallHint: '--grep "fail|error|pass|\u2713|\u2717|conclusion"' },
|
|
3191
|
+
{ pattern: /^gh run view.*--log/, recallHint: '--tail 100 --grep "Error|FAIL|error"' },
|
|
3192
|
+
{ pattern: /^gh pr checks/, recallHint: '--grep "fail|error|pass|pending"' },
|
|
3193
|
+
{ pattern: /^gh pr view/, recallHint: '--grep "state|title|mergeable|review|OPEN|CLOSED|MERGED"' },
|
|
3194
|
+
{ pattern: /^gh pr list/, recallHint: '--grep "OPEN|DRAFT|MERGED|#"' },
|
|
3195
|
+
{ pattern: /^gh issue view/, recallHint: '--grep "state|title|label|OPEN|CLOSED"' },
|
|
3196
|
+
{ pattern: /^gh workflow (?:run|list|view)/, recallHint: '--grep "completed|failed|in_progress"' },
|
|
3197
|
+
// Dev servers (Next, Vite, Nuxt, Remix, Astro)
|
|
3198
|
+
{ pattern: /^(?:npx\s+)?next dev/, recallHint: '--tail 30 --grep "error|warn|ready|compiled"' },
|
|
3199
|
+
{ pattern: /^(?:npx\s+)?next build/, recallHint: '--grep "error|warn|Failed|\u2713"' },
|
|
3200
|
+
{ pattern: /^(?:npx\s+)?vite\b(?:\s+dev|\s+build|\s+preview)?/, recallHint: '--tail 20 --grep "error|warn|ready"' },
|
|
3201
|
+
{ pattern: /^(?:npx\s+)?nuxt dev/, recallHint: '--tail 30 --grep "error|warn|ready"' },
|
|
3202
|
+
{ pattern: /^(?:npx\s+)?remix dev/, recallHint: '--tail 20 --grep "error|warn|ready"' },
|
|
3203
|
+
{ pattern: /^(?:npx\s+)?astro dev/, recallHint: '--tail 20 --grep "error|warn|ready"' },
|
|
3204
|
+
// Test watchers
|
|
3205
|
+
{ pattern: /^(?:npx\s+)?vitest(?:\s+run|\s+watch)?/, recallHint: '--grep "FAIL|PASS|Error|\u2713|\u2717"' },
|
|
3206
|
+
{ pattern: /^(?:npx\s+)?jest(?:\s+--watch)?/, recallHint: '--grep "FAIL|PASS|Error|Tests:"' },
|
|
3207
|
+
{ pattern: /^pytest(?:\s|$)/, recallHint: '--grep "FAILED|PASSED|ERROR|passed|failed"' },
|
|
3208
|
+
{ pattern: /^(?:cargo\s+test|cargo\s+watch)/, recallHint: '--grep "FAILED|ok|error\\["' },
|
|
3209
|
+
{ pattern: /^go test/, recallHint: '--grep "FAIL|ok|---"' },
|
|
3210
|
+
// Docker / compose
|
|
3211
|
+
{ pattern: /^docker(?:\s+compose)?\s+logs/, recallHint: '--tail 50 --grep "error|warn|Error|WARN"' },
|
|
3212
|
+
{ pattern: /^docker-compose\s+logs/, recallHint: '--tail 50 --grep "error|warn|Error|WARN"' },
|
|
3213
|
+
// File watchers / hot-reload
|
|
3214
|
+
{ pattern: /^nodemon/, recallHint: '--tail 20 --grep "error|crash|restart"' },
|
|
3215
|
+
{ pattern: /^air(?:\s|$)/, recallHint: '--tail 20 --grep "error|build failed"' },
|
|
3216
|
+
{ pattern: /^cargo watch/, recallHint: '--tail 20 --grep "error\\[|warning\\["' },
|
|
3217
|
+
{ pattern: /^watchexec/, recallHint: '--tail 20 --grep "error|warn"' },
|
|
3218
|
+
// Linters / formatters run repeatedly
|
|
3219
|
+
{ pattern: /^(?:npx\s+)?eslint(?:\s|$)/, recallHint: '--grep "error|warning|\u2716|problems"' },
|
|
3220
|
+
{ pattern: /^(?:npx\s+)?prettier(?:\s|$)/, recallHint: '--grep "unchanged|reformatted|error"' },
|
|
3221
|
+
{ pattern: /^npx\s+tsc(?:\s|$)/, recallHint: '--grep "error TS|Cannot find|Type "' },
|
|
3222
|
+
{ pattern: /^ruff(?:\s|$)/, recallHint: '--grep "error|warning|Found"' },
|
|
3223
|
+
{ pattern: /^(?:cargo\s+)?clippy/, recallHint: '--grep "error\\[|warning\\["' },
|
|
3224
|
+
// git diff (full diff output — can be very large; excludes --stat which is small)
|
|
3225
|
+
{ pattern: /^git diff(?!\s+--stat)(?:\s+HEAD)?(?:\s|$)/, recallHint: '--grep "@@|\\+\\+\\+|---|diff --git"' },
|
|
3226
|
+
{ pattern: /^git diff\s+--cached(?!\s+--stat)/, recallHint: '--grep "@@|\\+\\+\\+|---|diff --git"' },
|
|
3227
|
+
// git push — output is dominated by the pre-push hook (full test suite, often 10K+); cache it so failures/rejections are recallable without re-pushing
|
|
3228
|
+
{ pattern: /^git push(?:\s|$)/, recallHint: '--tail 100 --grep "error|fail|FAIL|rejected|To "' },
|
|
3229
|
+
// npm run * wrappers (npm test is excluded — too generic; npm run test is explicit)
|
|
3230
|
+
{ pattern: /^npm run (?:test|spec)(?:\s|$)/, recallHint: '--grep "FAIL|PASS|Error|Tests:|\u2713|\u2717"' },
|
|
3231
|
+
{ pattern: /^npm run build(?:\s|$)/, recallHint: '--grep "error|Built|Failed|\u2713|\u2717"' },
|
|
3232
|
+
{ pattern: /^npm run (?:lint|typecheck|check|type-check)(?:\s|$)/, recallHint: '--grep "error|warning|\u2716|problems"' },
|
|
3233
|
+
// node scripts (migration runners, seed generators, etc. run repeatedly)
|
|
3234
|
+
{ pattern: /^node\s+(?:scripts|src\/scripts)\/\S+\.m?js\b/, recallHint: '--tail 50 --grep "error|Error|done|complete|inserted|migrated"' },
|
|
3235
|
+
// External AI peer-review CLI tools (produce large outputs, run repeatedly per session)
|
|
3236
|
+
{ pattern: /^codex(?:\s|$)/, recallHint: '--tail 100 --grep "error|suggestion|verdict|conclusion"' },
|
|
3237
|
+
{ pattern: /^(?:~\/\.claude\/bin\/|\.claude\/bin\/)?glm\.sh(?:\s|$)/, recallHint: '--tail 100 --grep "error|verdict|conclusion|suggestion"' },
|
|
3238
|
+
// cat of a single source file — output is the full file; pre-bash emits a token-goat read suggestion
|
|
3239
|
+
{ pattern: /^cat\s+\S+\.(java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj)\s*$/, recallHint: "--tail 50" },
|
|
3240
|
+
// PowerShell read-only system-state queries (stable over 60-120s)
|
|
3241
|
+
{
|
|
3242
|
+
pattern: /^(?:powershell(?:\.exe)?|pwsh(?:\.exe)?)\s+(?:-\w+\s+)*-Command\s+["']?Get-(?:CimInstance|Process|Counter|Service|PSDrive|WmiObject)\b/i,
|
|
3243
|
+
recallHint: "--tail 50"
|
|
3244
|
+
},
|
|
3245
|
+
// token-goat section/outline/symbol repeat calls — output is stable until the file changes
|
|
3246
|
+
{ pattern: /^(?:token-goat|tg)\s+section\s+["'][^"']+["']/, recallHint: "" },
|
|
3247
|
+
{ pattern: /^(?:token-goat|tg)\s+outline\s+\S+/, recallHint: "" },
|
|
3248
|
+
{ pattern: /^(?:token-goat|tg)\s+symbol\s+\S+/, recallHint: "" }
|
|
3249
|
+
];
|
|
3250
|
+
function isPsMultilineSystemQuery(cmd) {
|
|
3251
|
+
if (!/^(?:powershell(?:\.exe)?|pwsh(?:\.exe)?)\s+/i.test(cmd)) return false;
|
|
3252
|
+
const cmdIdx = cmd.search(/-Command\b/i);
|
|
3253
|
+
if (cmdIdx === -1) return false;
|
|
3254
|
+
const afterCmd = cmd.slice(cmdIdx + "-Command".length).trimStart();
|
|
3255
|
+
if (!/^["']/.test(afterCmd)) return false;
|
|
3256
|
+
const body = afterCmd.slice(1);
|
|
3257
|
+
if (!body.includes("\n")) return false;
|
|
3258
|
+
if (!/\bGet-(?:CimInstance|Process|Counter|Service|PSDrive|WmiObject)\b/i.test(body)) return false;
|
|
3259
|
+
if (/\b(?:Remove|Set|New|Restart|Install|Uninstall|Enable|Disable|Grant|Revoke|Register|Unregister)-/i.test(body)) return false;
|
|
3260
|
+
if (/\b(?:Stop-(?:Process|Service|Computer)|Invoke-(?:Expression|Command)|Clear-(?:Content|EventLog|Item))\b/i.test(body)) return false;
|
|
3261
|
+
return true;
|
|
3262
|
+
}
|
|
3263
|
+
function getMonitoringRecallHint(cmd) {
|
|
3264
|
+
const trimmed = cmd.trim();
|
|
3265
|
+
if (hasUnquotedOperator(trimmed, ["&&", "||", ";"])) return null;
|
|
3266
|
+
for (const { pattern, recallHint } of MONITORING_COMMAND_PATTERNS) {
|
|
3267
|
+
if (pattern.test(trimmed)) return recallHint;
|
|
3268
|
+
}
|
|
3269
|
+
if (isPsMultilineSystemQuery(trimmed)) return "--tail 50";
|
|
3270
|
+
return null;
|
|
3271
|
+
}
|
|
3272
|
+
|
|
3273
|
+
// src/recall_index.ts
|
|
2958
3274
|
init_define_import_meta_env();
|
|
2959
3275
|
import * as fs7 from "node:fs";
|
|
3276
|
+
function subdirForCacheType(cacheType) {
|
|
3277
|
+
return cacheType === "web" ? "web_outputs" : "bash_outputs";
|
|
3278
|
+
}
|
|
3279
|
+
function blobStillExists(cacheType, id) {
|
|
3280
|
+
try {
|
|
3281
|
+
const p = blobPath(subdirForCacheType(cacheType), id);
|
|
3282
|
+
return p !== null && fs7.existsSync(p);
|
|
3283
|
+
} catch {
|
|
3284
|
+
return false;
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
var VALID_TYPES = ["bash", "web", "mcp"];
|
|
3288
|
+
var RECALL_DEFAULT_LIMIT = 10;
|
|
3289
|
+
function isRecallCacheType(value) {
|
|
3290
|
+
return VALID_TYPES.includes(value);
|
|
3291
|
+
}
|
|
3292
|
+
function indexRecallEntry(cacheType, id, label, content, storedAt) {
|
|
3293
|
+
try {
|
|
3294
|
+
const db = getDb(globalDbPath());
|
|
3295
|
+
db.prepare(
|
|
3296
|
+
`INSERT INTO cache_recall (cache_type, entry_id, label, content, stored_at)
|
|
3297
|
+
VALUES (@cacheType, @id, @label, @content, @storedAt)
|
|
3298
|
+
ON CONFLICT(cache_type, entry_id) DO UPDATE SET
|
|
3299
|
+
label = excluded.label,
|
|
3300
|
+
content = excluded.content,
|
|
3301
|
+
stored_at = excluded.stored_at`
|
|
3302
|
+
).run({ cacheType, id, label, content, storedAt });
|
|
3303
|
+
} catch {
|
|
3304
|
+
}
|
|
3305
|
+
pruneCacheRecallRows();
|
|
3306
|
+
}
|
|
3307
|
+
function pruneCacheRecallRows(maxAgeMs = DEFAULT_MAX_AGE_MS) {
|
|
3308
|
+
try {
|
|
3309
|
+
const db = getDb(globalDbPath());
|
|
3310
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
3311
|
+
db.prepare(`DELETE FROM cache_recall WHERE stored_at < ?`).run(cutoff);
|
|
3312
|
+
} catch {
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
var _ftsAvailable = null;
|
|
3316
|
+
function hasFtsTable() {
|
|
3317
|
+
if (_ftsAvailable !== null) return _ftsAvailable;
|
|
3318
|
+
try {
|
|
3319
|
+
const db = getDb(globalDbPath());
|
|
3320
|
+
const row = db.prepare(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'cache_recall_fts'`).get();
|
|
3321
|
+
_ftsAvailable = row !== void 0;
|
|
3322
|
+
} catch {
|
|
3323
|
+
_ftsAvailable = false;
|
|
3324
|
+
}
|
|
3325
|
+
return _ftsAvailable;
|
|
3326
|
+
}
|
|
3327
|
+
function buildSnippet(content, query, maxLen = 160) {
|
|
3328
|
+
const flat = content.replace(/\s+/g, " ").trim();
|
|
3329
|
+
const tokens = query.split(/\s+/).filter(Boolean);
|
|
3330
|
+
let idx = -1;
|
|
3331
|
+
const lowerFlat = flat.toLowerCase();
|
|
3332
|
+
for (const token of tokens) {
|
|
3333
|
+
const at = lowerFlat.indexOf(token.toLowerCase());
|
|
3334
|
+
if (at !== -1) {
|
|
3335
|
+
idx = at;
|
|
3336
|
+
break;
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
if (idx === -1) {
|
|
3340
|
+
return flat.length > maxLen ? flat.slice(0, maxLen) + "..." : flat;
|
|
3341
|
+
}
|
|
3342
|
+
const start = Math.max(0, idx - Math.floor(maxLen / 3));
|
|
3343
|
+
const end = Math.min(flat.length, start + maxLen);
|
|
3344
|
+
const prefix = start > 0 ? "..." : "";
|
|
3345
|
+
const suffix = end < flat.length ? "..." : "";
|
|
3346
|
+
return prefix + flat.slice(start, end) + suffix;
|
|
3347
|
+
}
|
|
3348
|
+
function toFtsMatchExpr(query) {
|
|
3349
|
+
if (query.trim().length === 0) return null;
|
|
3350
|
+
return sanitizeFtsQuery(query);
|
|
3351
|
+
}
|
|
3352
|
+
function mapRowsToHits(rows, snippetQuery) {
|
|
3353
|
+
return rows.filter((r) => isRecallCacheType(r.cacheType)).map((r) => ({
|
|
3354
|
+
id: r.id,
|
|
3355
|
+
cacheType: r.cacheType,
|
|
3356
|
+
label: r.label ?? "",
|
|
3357
|
+
snippet: buildSnippet(r.content ?? "", snippetQuery),
|
|
3358
|
+
storedAt: r.storedAt ?? 0,
|
|
3359
|
+
blobPresent: blobStillExists(r.cacheType, r.id)
|
|
3360
|
+
}));
|
|
3361
|
+
}
|
|
3362
|
+
function ftsSearch(query, type, limit) {
|
|
3363
|
+
const matchExpr = toFtsMatchExpr(query);
|
|
3364
|
+
if (matchExpr === null) return [];
|
|
3365
|
+
const db = getDb(globalDbPath());
|
|
3366
|
+
const rows = type !== void 0 ? db.prepare(
|
|
3367
|
+
`SELECT c.entry_id AS id, c.cache_type AS cacheType, c.label AS label, c.content AS content, c.stored_at AS storedAt
|
|
3368
|
+
FROM cache_recall_fts
|
|
3369
|
+
JOIN cache_recall c ON c.row_id = cache_recall_fts.rowid
|
|
3370
|
+
WHERE cache_recall_fts MATCH ? AND c.cache_type = ?
|
|
3371
|
+
ORDER BY bm25(cache_recall_fts)
|
|
3372
|
+
LIMIT ?`
|
|
3373
|
+
).all(matchExpr, type, limit) : db.prepare(
|
|
3374
|
+
`SELECT c.entry_id AS id, c.cache_type AS cacheType, c.label AS label, c.content AS content, c.stored_at AS storedAt
|
|
3375
|
+
FROM cache_recall_fts
|
|
3376
|
+
JOIN cache_recall c ON c.row_id = cache_recall_fts.rowid
|
|
3377
|
+
WHERE cache_recall_fts MATCH ?
|
|
3378
|
+
ORDER BY bm25(cache_recall_fts)
|
|
3379
|
+
LIMIT ?`
|
|
3380
|
+
).all(matchExpr, limit);
|
|
3381
|
+
return mapRowsToHits(rows, query);
|
|
3382
|
+
}
|
|
3383
|
+
function likeSearch(query, type, limit) {
|
|
3384
|
+
const trimmed = query.trim();
|
|
3385
|
+
if (!trimmed) return [];
|
|
3386
|
+
const db = getDb(globalDbPath());
|
|
3387
|
+
const needle = `%${trimmed.replace(/\\/g, "\\\\").replace(/[%_]/g, (c) => `\\${c}`)}%`;
|
|
3388
|
+
const rows = type !== void 0 ? db.prepare(
|
|
3389
|
+
`SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt
|
|
3390
|
+
FROM cache_recall
|
|
3391
|
+
WHERE cache_type = ? AND (label LIKE ? ESCAPE '\\' OR content LIKE ? ESCAPE '\\')
|
|
3392
|
+
ORDER BY stored_at DESC
|
|
3393
|
+
LIMIT ?`
|
|
3394
|
+
).all(type, needle, needle, limit) : db.prepare(
|
|
3395
|
+
`SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt
|
|
3396
|
+
FROM cache_recall
|
|
3397
|
+
WHERE label LIKE ? ESCAPE '\\' OR content LIKE ? ESCAPE '\\'
|
|
3398
|
+
ORDER BY stored_at DESC
|
|
3399
|
+
LIMIT ?`
|
|
3400
|
+
).all(needle, needle, limit);
|
|
3401
|
+
return mapRowsToHits(rows, trimmed);
|
|
3402
|
+
}
|
|
3403
|
+
function listRecentRecall(opts = {}) {
|
|
3404
|
+
const limit = opts.limit ?? RECALL_DEFAULT_LIMIT;
|
|
3405
|
+
const columns = `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt FROM cache_recall`;
|
|
3406
|
+
try {
|
|
3407
|
+
const db = getDb(globalDbPath());
|
|
3408
|
+
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);
|
|
3409
|
+
return mapRowsToHits(rows, "");
|
|
3410
|
+
} catch {
|
|
3411
|
+
return [];
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
function searchRecall(query, opts = {}) {
|
|
3415
|
+
const limit = opts.limit ?? RECALL_DEFAULT_LIMIT;
|
|
3416
|
+
if (query.trim() === "") return [];
|
|
3417
|
+
try {
|
|
3418
|
+
if (hasFtsTable()) {
|
|
3419
|
+
return ftsSearch(query, opts.type, limit);
|
|
3420
|
+
}
|
|
3421
|
+
return likeSearch(query, opts.type, limit);
|
|
3422
|
+
} catch {
|
|
3423
|
+
try {
|
|
3424
|
+
return likeSearch(query, opts.type, limit);
|
|
3425
|
+
} catch {
|
|
3426
|
+
return [];
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3431
|
+
// src/bash_output_cache.ts
|
|
3432
|
+
var BASH_OUTPUT_SUBDIR = "bash_outputs";
|
|
3433
|
+
var _byId = /* @__PURE__ */ new Map();
|
|
3434
|
+
var COMMAND_PATTERNS = {
|
|
3435
|
+
gitMutable: /^\s*git\s+(diff|status)\b/i,
|
|
3436
|
+
gitImmutable: /^\s*git\s+show\s+[0-9a-f]{40}\b/i,
|
|
3437
|
+
gitDiffScoped: /\s--\s+\S/,
|
|
3438
|
+
dirListing: /^\s*(?:ls|eza|exa|dir|Get-ChildItem|gci)\b/i,
|
|
3439
|
+
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,
|
|
3440
|
+
npmInstall: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:install|ci)\b/i,
|
|
3441
|
+
npmAudit: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*audit\b(?!.*(?:--fix|fix)\b)/i,
|
|
3442
|
+
npmOutdated: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*outdated\b/i,
|
|
3443
|
+
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,
|
|
3444
|
+
npx: /^(?!.*\b(?:install|add|remove|uninstall|i|rm|update|upgrade|set|get|publish|link|ci|audit|shrinkwrap|dedupe|prune|rebuild)\b)\s*npx\s+(?:--?yes\s+)?/i,
|
|
3445
|
+
gitPush: /^\s*git\s+push\b/i,
|
|
3446
|
+
testRunner: /^\s*(?:npx\s+)?(?:pytest|vitest|jest|go\s+test)\b/i,
|
|
3447
|
+
lintCommand: /^\s*(?:(?:npx\s+)?eslint|(?:uv\s+run\s+)?ruff)\b/i,
|
|
3448
|
+
npmRunScript: /^\s*npm\s+run(?:-script)?\b/i,
|
|
3449
|
+
catCommand: /^\s*cat\b/i
|
|
3450
|
+
};
|
|
3451
|
+
var DEP_LOCKFILES = {
|
|
3452
|
+
npm: ["package-lock.json", "yarn.lock"],
|
|
3453
|
+
pip: ["requirements.txt"],
|
|
3454
|
+
uv: ["uv.lock", "requirements.txt"],
|
|
3455
|
+
pnpm: ["pnpm-lock.yaml"],
|
|
3456
|
+
yarn: ["yarn.lock"],
|
|
3457
|
+
cargo: ["Cargo.lock"],
|
|
3458
|
+
bundle: ["Gemfile.lock"],
|
|
3459
|
+
composer: ["composer.lock"]
|
|
3460
|
+
};
|
|
3461
|
+
function isCommandOfType(cmd, type) {
|
|
3462
|
+
const pattern = COMMAND_PATTERNS[type];
|
|
3463
|
+
return pattern?.test(cmd) ?? false;
|
|
3464
|
+
}
|
|
3465
|
+
var isGitMutableCommand = (cmd) => isCommandOfType(cmd, "gitMutable");
|
|
3466
|
+
var isDirListingCommand = (cmd) => isCommandOfType(cmd, "dirListing");
|
|
3467
|
+
var isDepListCommand = (cmd) => isCommandOfType(cmd, "depList");
|
|
3468
|
+
var isNpmInstallCommand = (cmd) => isCommandOfType(cmd, "npmInstall");
|
|
3469
|
+
var isNpmAuditCommand = (cmd) => isCommandOfType(cmd, "npmAudit");
|
|
3470
|
+
var isNpmOutdatedCommand = (cmd) => isCommandOfType(cmd, "npmOutdated");
|
|
3471
|
+
var isGitPushCommand = (cmd) => isCommandOfType(cmd, "gitPush");
|
|
3472
|
+
var isTestRunnerCommand2 = (cmd) => isCommandOfType(cmd, "testRunner");
|
|
3473
|
+
var isLintCommand = (cmd) => isCommandOfType(cmd, "lintCommand");
|
|
3474
|
+
var isNpmRunScriptCommand = (cmd) => isCommandOfType(cmd, "npmRunScript");
|
|
3475
|
+
var isCatCommand = (cmd) => isCommandOfType(cmd, "catCommand");
|
|
3476
|
+
function isScopedGitStatusOrDiffStatCommand(cmd) {
|
|
3477
|
+
if (!isCommandOfType(cmd, "gitMutable")) return false;
|
|
3478
|
+
if (!isCommandOfType(cmd, "gitDiffScoped")) return false;
|
|
3479
|
+
if (/^\s*git\s+diff\b/i.test(cmd) && !/--stat\b/.test(cmd)) return false;
|
|
3480
|
+
return true;
|
|
3481
|
+
}
|
|
3482
|
+
function gitStateFingerprintSync(cwd) {
|
|
3483
|
+
try {
|
|
3484
|
+
const headResult = runGit(["rev-parse", "HEAD"], { cwd });
|
|
3485
|
+
if (headResult.exitCode !== 0) return null;
|
|
3486
|
+
const headSha = headResult.stdout.trim();
|
|
3487
|
+
let statusHash = "";
|
|
3488
|
+
const statusResult = runGit(["status", "--porcelain"], { cwd });
|
|
3489
|
+
if (statusResult.exitCode === 0) {
|
|
3490
|
+
statusHash = shortFingerprint(statusResult.stdout);
|
|
3491
|
+
}
|
|
3492
|
+
const key = `${headSha}\0${statusHash}`;
|
|
3493
|
+
return shortFingerprint(key);
|
|
3494
|
+
} catch {
|
|
3495
|
+
return null;
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
var DIR_FINGERPRINT_LISTING_CAP_ENTRIES = 1e4;
|
|
3499
|
+
function dirStateFingerprintSync(path12) {
|
|
3500
|
+
try {
|
|
3501
|
+
const stat = statSync3(path12);
|
|
3502
|
+
if (!stat.isDirectory()) return null;
|
|
3503
|
+
const entries = readdirSync2(path12);
|
|
3504
|
+
if (entries.length <= DIR_FINGERPRINT_LISTING_CAP_ENTRIES) {
|
|
3505
|
+
return shortFingerprint(entries.slice().sort().join("\0"));
|
|
3506
|
+
}
|
|
3507
|
+
return shortFingerprint(stat.mtimeMs.toString());
|
|
3508
|
+
} catch {
|
|
3509
|
+
return null;
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
var FILE_FINGERPRINT_CONTENT_CAP_BYTES = 2 * 1024 * 1024;
|
|
3513
|
+
function fileStateFingerprintSync(path12) {
|
|
3514
|
+
try {
|
|
3515
|
+
const stat = statSync3(path12);
|
|
3516
|
+
if (!stat.isFile()) return null;
|
|
3517
|
+
if (stat.size <= FILE_FINGERPRINT_CONTENT_CAP_BYTES) {
|
|
3518
|
+
return shortFingerprint(readFileSync5(path12));
|
|
3519
|
+
}
|
|
3520
|
+
return shortFingerprint(`${stat.mtimeMs}\0${stat.size}`);
|
|
3521
|
+
} catch {
|
|
3522
|
+
return null;
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
function depLockfileFingerprintSync(cmd, cwd) {
|
|
3526
|
+
if (!cwd) return null;
|
|
3527
|
+
const stripped = cmd.trim();
|
|
3528
|
+
const firstToken = stripped.split(/\s+/)[0]?.toLowerCase() || "";
|
|
3529
|
+
if (!firstToken) return null;
|
|
3530
|
+
const candidates = firstToken === "uv" ? DEP_LOCKFILES["uv"] : DEP_LOCKFILES[firstToken];
|
|
3531
|
+
if (!candidates) return null;
|
|
3532
|
+
for (const lockfile of candidates) {
|
|
3533
|
+
try {
|
|
3534
|
+
const content = readFileSync5(resolve4(cwd, lockfile));
|
|
3535
|
+
return shortFingerprint(content);
|
|
3536
|
+
} catch {
|
|
3537
|
+
continue;
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
return null;
|
|
3541
|
+
}
|
|
3542
|
+
function normalizeCommandForCacheKey(cmd) {
|
|
3543
|
+
const trimmed = cmd.trim();
|
|
3544
|
+
const tokens = [];
|
|
3545
|
+
let current = "";
|
|
3546
|
+
let quoteChar = null;
|
|
3547
|
+
const flush = () => {
|
|
3548
|
+
if (current.length > 0) {
|
|
3549
|
+
tokens.push(current);
|
|
3550
|
+
current = "";
|
|
3551
|
+
}
|
|
3552
|
+
};
|
|
3553
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
3554
|
+
const ch = trimmed[i];
|
|
3555
|
+
if (quoteChar !== null) {
|
|
3556
|
+
current += ch;
|
|
3557
|
+
if (ch === quoteChar) quoteChar = null;
|
|
3558
|
+
continue;
|
|
3559
|
+
}
|
|
3560
|
+
if (ch === '"' || ch === "'") {
|
|
3561
|
+
quoteChar = ch;
|
|
3562
|
+
current += ch;
|
|
3563
|
+
continue;
|
|
3564
|
+
}
|
|
3565
|
+
if (/\s/.test(ch)) {
|
|
3566
|
+
flush();
|
|
3567
|
+
continue;
|
|
3568
|
+
}
|
|
3569
|
+
current += ch === "\\" ? "/" : ch;
|
|
3570
|
+
}
|
|
3571
|
+
flush();
|
|
3572
|
+
const normalized_tokens = tokens.map((token) => {
|
|
3573
|
+
if (token.startsWith("-") || ["&&", "||", "|", ">", ">>", ";", "&"].includes(token)) {
|
|
3574
|
+
return token;
|
|
3575
|
+
}
|
|
3576
|
+
if (token.startsWith("./") && !token.startsWith("../")) {
|
|
3577
|
+
token = token.slice(2);
|
|
3578
|
+
}
|
|
3579
|
+
if (token.endsWith("/") && token !== "/") {
|
|
3580
|
+
token = token.slice(0, -1);
|
|
3581
|
+
}
|
|
3582
|
+
return token || ".";
|
|
3583
|
+
});
|
|
3584
|
+
return normalized_tokens.join(" ");
|
|
3585
|
+
}
|
|
3586
|
+
function commandHashSync(command, cwd = null) {
|
|
3587
|
+
const normalized = normalizeCommandForCacheKey(command);
|
|
3588
|
+
let key = cwd ? `${normalizePath(cwd)}\0${normalized}` : normalized;
|
|
3589
|
+
if (cwd && isGitMutableCommand(command)) {
|
|
3590
|
+
const fp = gitStateFingerprintSync(cwd);
|
|
3591
|
+
if (fp) key = `${key}\0git:${fp}`;
|
|
3592
|
+
}
|
|
3593
|
+
if (cwd && isDirListingCommand(command)) {
|
|
3594
|
+
const target = extractFirstPathArg(command, cwd, cwd);
|
|
3595
|
+
if (target) {
|
|
3596
|
+
const fp = dirStateFingerprintSync(target);
|
|
3597
|
+
if (fp) key = `${key}\0dir:${fp}`;
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
if (isDepListCommand(command)) {
|
|
3601
|
+
const fp = depLockfileFingerprintSync(command, cwd);
|
|
3602
|
+
if (fp) key = `${key}\0lockfile:${fp}`;
|
|
3603
|
+
}
|
|
3604
|
+
if (cwd && (isNpmInstallCommand(command) || isNpmAuditCommand(command) || isNpmOutdatedCommand(command))) {
|
|
3605
|
+
const fp = depLockfileFingerprintSync(command, cwd);
|
|
3606
|
+
if (fp) key = `${key}\0npm-install:${fp}`;
|
|
3607
|
+
}
|
|
3608
|
+
return shortFingerprint(key);
|
|
3609
|
+
}
|
|
3610
|
+
function commandHash(command, cwd = null) {
|
|
3611
|
+
return Promise.resolve(commandHashSync(command, cwd));
|
|
3612
|
+
}
|
|
3613
|
+
function computeBashFingerprints(command, cwd) {
|
|
3614
|
+
const fingerprints = {};
|
|
3615
|
+
if (cwd && (isGitMutableCommand(command) || isGitPushCommand(command) || isTestRunnerCommand2(command) || isLintCommand(command) || isBuildCommand(command) || isNpmRunScriptCommand(command))) {
|
|
3616
|
+
const fp = gitStateFingerprintSync(cwd);
|
|
3617
|
+
if (fp) fingerprints.git = fp;
|
|
3618
|
+
}
|
|
3619
|
+
if (cwd && isDirListingCommand(command)) {
|
|
3620
|
+
const target = extractFirstPathArg(command, cwd, cwd);
|
|
3621
|
+
if (target) {
|
|
3622
|
+
const fp = dirStateFingerprintSync(target);
|
|
3623
|
+
if (fp) fingerprints.dir = fp;
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
if (isDepListCommand(command) || cwd && (isNpmInstallCommand(command) || isNpmAuditCommand(command) || isNpmOutdatedCommand(command))) {
|
|
3627
|
+
const fp = depLockfileFingerprintSync(command, cwd);
|
|
3628
|
+
if (fp) fingerprints.lockfile = fp;
|
|
3629
|
+
}
|
|
3630
|
+
if (cwd && isCatCommand(command)) {
|
|
3631
|
+
const target = extractFirstPathArg(command, cwd, null);
|
|
3632
|
+
if (target) {
|
|
3633
|
+
const fp = fileStateFingerprintSync(target);
|
|
3634
|
+
if (fp) fingerprints.file = fp;
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
return Object.keys(fingerprints).length > 0 ? fingerprints : void 0;
|
|
3638
|
+
}
|
|
3639
|
+
function isBashEntryStale(entry, command, cwd) {
|
|
3640
|
+
const stored = entry.fingerprints;
|
|
3641
|
+
if (!stored) return false;
|
|
3642
|
+
const current = computeBashFingerprints(command, cwd);
|
|
3643
|
+
if (stored.git !== void 0 && stored.git !== current?.git) return true;
|
|
3644
|
+
if (stored.dir !== void 0 && stored.dir !== current?.dir) return true;
|
|
3645
|
+
if (stored.lockfile !== void 0 && stored.lockfile !== current?.lockfile) return true;
|
|
3646
|
+
if (stored.file !== void 0 && stored.file !== current?.file) return true;
|
|
3647
|
+
return false;
|
|
3648
|
+
}
|
|
3649
|
+
function tokenizeShellArgs(cmd) {
|
|
3650
|
+
const trimmed = cmd.trim();
|
|
3651
|
+
const tokens = [];
|
|
3652
|
+
let current = "";
|
|
3653
|
+
let quoteChar = null;
|
|
3654
|
+
let hasToken = false;
|
|
3655
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
3656
|
+
const ch = trimmed[i];
|
|
3657
|
+
if (quoteChar !== null) {
|
|
3658
|
+
if (ch === quoteChar) {
|
|
3659
|
+
quoteChar = null;
|
|
3660
|
+
} else {
|
|
3661
|
+
current += ch;
|
|
3662
|
+
}
|
|
3663
|
+
continue;
|
|
3664
|
+
}
|
|
3665
|
+
if (ch === '"' || ch === "'") {
|
|
3666
|
+
quoteChar = ch;
|
|
3667
|
+
hasToken = true;
|
|
3668
|
+
continue;
|
|
3669
|
+
}
|
|
3670
|
+
if (/\s/.test(ch)) {
|
|
3671
|
+
if (hasToken) {
|
|
3672
|
+
tokens.push(current);
|
|
3673
|
+
current = "";
|
|
3674
|
+
hasToken = false;
|
|
3675
|
+
}
|
|
3676
|
+
continue;
|
|
3677
|
+
}
|
|
3678
|
+
current += ch;
|
|
3679
|
+
hasToken = true;
|
|
3680
|
+
}
|
|
3681
|
+
if (hasToken) tokens.push(current);
|
|
3682
|
+
return tokens;
|
|
3683
|
+
}
|
|
3684
|
+
function extractFirstPathArg(cmd, cwd, fallback) {
|
|
3685
|
+
const tokens = tokenizeShellArgs(cmd);
|
|
3686
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
3687
|
+
const token = tokens[i];
|
|
3688
|
+
if (!token.startsWith("-")) {
|
|
3689
|
+
if (!token.startsWith("/")) {
|
|
3690
|
+
return resolve4(cwd, token);
|
|
3691
|
+
}
|
|
3692
|
+
return token;
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
return fallback;
|
|
3696
|
+
}
|
|
3697
|
+
var ISSUE_LINE_PATTERN = /\b(?:error|fail(?:ed|ure)?|warning)\b/i;
|
|
3698
|
+
function summarizeOutputDelta(oldOutput, newOutput) {
|
|
3699
|
+
if (oldOutput === newOutput) return null;
|
|
3700
|
+
const oldLines = oldOutput.split("\n");
|
|
3701
|
+
const newLines = newOutput.split("\n");
|
|
3702
|
+
const oldIssueLines = oldLines.filter((l) => ISSUE_LINE_PATTERN.test(l));
|
|
3703
|
+
if (oldIssueLines.length > 0) {
|
|
3704
|
+
const newIssueLines = newLines.filter((l) => ISSUE_LINE_PATTERN.test(l));
|
|
3705
|
+
const priorTotal = oldIssueLines.length;
|
|
3706
|
+
const availableCounts = /* @__PURE__ */ new Map();
|
|
3707
|
+
for (const l of newIssueLines) availableCounts.set(l, (availableCounts.get(l) ?? 0) + 1);
|
|
3708
|
+
let resolved = 0;
|
|
3709
|
+
for (const l of oldIssueLines) {
|
|
3710
|
+
const avail = availableCounts.get(l) ?? 0;
|
|
3711
|
+
if (avail > 0) {
|
|
3712
|
+
availableCounts.set(l, avail - 1);
|
|
3713
|
+
} else {
|
|
3714
|
+
resolved++;
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
const remaining = newIssueLines.length;
|
|
3718
|
+
return `[token-goat: delta] ${resolved} of ${priorTotal} prior issues resolved; remaining: ${remaining}`;
|
|
3719
|
+
}
|
|
3720
|
+
return `[token-goat: delta] output changed: ${oldLines.length} -> ${newLines.length} lines`;
|
|
3721
|
+
}
|
|
3722
|
+
function storeBashOutputSync(command, output, exitCode, cwd = null) {
|
|
3723
|
+
const id = commandHashSync(command, cwd);
|
|
3724
|
+
const fingerprints = computeBashFingerprints(command, cwd);
|
|
3725
|
+
const redactedOutput = redactSecrets(output).text;
|
|
3726
|
+
const redactedCommand = redactSecrets(command).text;
|
|
3727
|
+
const entry = {
|
|
3728
|
+
id,
|
|
3729
|
+
command: redactedCommand,
|
|
3730
|
+
output: redactedOutput,
|
|
3731
|
+
exitCode,
|
|
3732
|
+
storedAt: Date.now(),
|
|
3733
|
+
sizeBytes: Buffer.byteLength(redactedOutput, "utf-8"),
|
|
3734
|
+
...fingerprints ? { fingerprints } : {}
|
|
3735
|
+
};
|
|
3736
|
+
_byId.set(id, entry);
|
|
3737
|
+
storeBlob(BASH_OUTPUT_SUBDIR, id, entry);
|
|
3738
|
+
indexRecallEntry("bash", id, redactedCommand, `${redactedCommand}
|
|
3739
|
+
${redactedOutput}`, entry.storedAt);
|
|
3740
|
+
return id;
|
|
3741
|
+
}
|
|
3742
|
+
function storeBashOutput(command, output, exitCode, cwd = null) {
|
|
3743
|
+
return Promise.resolve(storeBashOutputSync(command, output, exitCode, cwd));
|
|
3744
|
+
}
|
|
3745
|
+
function coerceBashEntry(raw) {
|
|
3746
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
3747
|
+
const o = raw;
|
|
3748
|
+
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") {
|
|
3749
|
+
return null;
|
|
3750
|
+
}
|
|
3751
|
+
const entry = {
|
|
3752
|
+
id: o["id"],
|
|
3753
|
+
command: o["command"],
|
|
3754
|
+
output: o["output"],
|
|
3755
|
+
exitCode: o["exitCode"],
|
|
3756
|
+
storedAt: o["storedAt"],
|
|
3757
|
+
sizeBytes: o["sizeBytes"]
|
|
3758
|
+
};
|
|
3759
|
+
const rawFingerprints = o["fingerprints"];
|
|
3760
|
+
if (rawFingerprints !== null && typeof rawFingerprints === "object") {
|
|
3761
|
+
const f = rawFingerprints;
|
|
3762
|
+
const fingerprints = {};
|
|
3763
|
+
if (typeof f["git"] === "string") fingerprints.git = f["git"];
|
|
3764
|
+
if (typeof f["dir"] === "string") fingerprints.dir = f["dir"];
|
|
3765
|
+
if (typeof f["lockfile"] === "string") fingerprints.lockfile = f["lockfile"];
|
|
3766
|
+
if (typeof f["file"] === "string") fingerprints.file = f["file"];
|
|
3767
|
+
if (Object.keys(fingerprints).length > 0) return { ...entry, fingerprints };
|
|
3768
|
+
}
|
|
3769
|
+
return entry;
|
|
3770
|
+
}
|
|
3771
|
+
function getBashOutput(id) {
|
|
3772
|
+
const hit = _byId.get(id);
|
|
3773
|
+
if (hit !== void 0) return hit;
|
|
3774
|
+
if (isBlobStale(BASH_OUTPUT_SUBDIR, id)) return null;
|
|
3775
|
+
const entry = coerceBashEntry(loadBlob(BASH_OUTPUT_SUBDIR, id));
|
|
3776
|
+
if (entry === null) return null;
|
|
3777
|
+
_byId.set(id, entry);
|
|
3778
|
+
return entry;
|
|
3779
|
+
}
|
|
3780
|
+
registerReset(() => {
|
|
3781
|
+
_byId = /* @__PURE__ */ new Map();
|
|
3782
|
+
});
|
|
3783
|
+
|
|
3784
|
+
// src/install.ts
|
|
3785
|
+
init_define_import_meta_env();
|
|
3786
|
+
import * as fs8 from "node:fs";
|
|
2960
3787
|
import * as os4 from "node:os";
|
|
2961
3788
|
import * as path7 from "node:path";
|
|
2962
3789
|
|
|
@@ -3140,7 +3967,7 @@ var SettingsParseError = class extends Error {
|
|
|
3140
3967
|
function readSettings(p, opts = {}) {
|
|
3141
3968
|
let raw;
|
|
3142
3969
|
try {
|
|
3143
|
-
raw =
|
|
3970
|
+
raw = fs8.readFileSync(p, "utf8");
|
|
3144
3971
|
} catch {
|
|
3145
3972
|
return {};
|
|
3146
3973
|
}
|
|
@@ -3272,7 +4099,7 @@ function isInstalled(scope = "user") {
|
|
|
3272
4099
|
const hooks = settings.hooks;
|
|
3273
4100
|
if (hooks === void 0) return false;
|
|
3274
4101
|
const scriptPath = claudeHookScriptPath();
|
|
3275
|
-
if (!
|
|
4102
|
+
if (!fs8.existsSync(scriptPath)) return false;
|
|
3276
4103
|
for (const [eventKey, eventArg] of HOOK_EVENT_MAP) {
|
|
3277
4104
|
const expectedCommand = hookCommandFor(scriptPath, eventArg);
|
|
3278
4105
|
if (!groupHasTokenGoat(hooks[eventKey], (c) => c === expectedCommand)) return false;
|
|
@@ -3310,7 +4137,7 @@ function findStrayClaudeMdBlocks(searchRoot) {
|
|
|
3310
4137
|
const root = searchRoot ?? path7.join(os4.homedir(), ".claude");
|
|
3311
4138
|
const canonical = path7.resolve(claudeMdPath());
|
|
3312
4139
|
const found = [];
|
|
3313
|
-
if (!
|
|
4140
|
+
if (!fs8.existsSync(root)) return found;
|
|
3314
4141
|
const SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git"]);
|
|
3315
4142
|
const MAX_DEPTH = 6;
|
|
3316
4143
|
const hasRealBlock = (text) => {
|
|
@@ -3326,7 +4153,7 @@ function findStrayClaudeMdBlocks(searchRoot) {
|
|
|
3326
4153
|
if (depth > MAX_DEPTH) return;
|
|
3327
4154
|
let entries;
|
|
3328
4155
|
try {
|
|
3329
|
-
entries =
|
|
4156
|
+
entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
3330
4157
|
} catch {
|
|
3331
4158
|
return;
|
|
3332
4159
|
}
|
|
@@ -3340,7 +4167,7 @@ function findStrayClaudeMdBlocks(searchRoot) {
|
|
|
3340
4167
|
if (path7.resolve(full) === canonical) continue;
|
|
3341
4168
|
let text;
|
|
3342
4169
|
try {
|
|
3343
|
-
text =
|
|
4170
|
+
text = fs8.readFileSync(full, "utf8");
|
|
3344
4171
|
} catch {
|
|
3345
4172
|
continue;
|
|
3346
4173
|
}
|
|
@@ -3378,7 +4205,7 @@ function installSkill() {
|
|
|
3378
4205
|
const p = skillPath();
|
|
3379
4206
|
let existing = null;
|
|
3380
4207
|
try {
|
|
3381
|
-
existing =
|
|
4208
|
+
existing = fs8.readFileSync(p, "utf8");
|
|
3382
4209
|
} catch {
|
|
3383
4210
|
}
|
|
3384
4211
|
const skillContent = skillMdContent();
|
|
@@ -3390,17 +4217,17 @@ function installSkill() {
|
|
|
3390
4217
|
}
|
|
3391
4218
|
function uninstallSkill() {
|
|
3392
4219
|
const dir = skillDir();
|
|
3393
|
-
if (!
|
|
4220
|
+
if (!fs8.existsSync(dir)) return false;
|
|
3394
4221
|
removeCreatedBackups(skillPath());
|
|
3395
4222
|
assertWriteInScope(dir);
|
|
3396
|
-
|
|
4223
|
+
fs8.rmSync(dir, { recursive: true, force: true });
|
|
3397
4224
|
return true;
|
|
3398
4225
|
}
|
|
3399
4226
|
|
|
3400
4227
|
// src/bridges/codex_install.ts
|
|
3401
4228
|
init_define_import_meta_env();
|
|
3402
4229
|
import * as crypto2 from "node:crypto";
|
|
3403
|
-
import * as
|
|
4230
|
+
import * as fs9 from "node:fs";
|
|
3404
4231
|
import * as os5 from "node:os";
|
|
3405
4232
|
import * as path8 from "node:path";
|
|
3406
4233
|
|
|
@@ -3567,7 +4394,7 @@ function codexHookScriptPath() {
|
|
|
3567
4394
|
function readCodexConfig(p, opts = {}) {
|
|
3568
4395
|
let raw;
|
|
3569
4396
|
try {
|
|
3570
|
-
raw =
|
|
4397
|
+
raw = fs9.readFileSync(p, "utf8");
|
|
3571
4398
|
} catch {
|
|
3572
4399
|
return {};
|
|
3573
4400
|
}
|
|
@@ -3773,7 +4600,7 @@ function uninstallCodex() {
|
|
|
3773
4600
|
removedAny = true;
|
|
3774
4601
|
}
|
|
3775
4602
|
try {
|
|
3776
|
-
|
|
4603
|
+
fs9.unlinkSync(scriptPath);
|
|
3777
4604
|
removedAny = true;
|
|
3778
4605
|
} catch {
|
|
3779
4606
|
}
|
|
@@ -3809,10 +4636,10 @@ function isCodexInstalled() {
|
|
|
3809
4636
|
const expectedHash = computeCodexHookHash(eventArg, expectedCommand);
|
|
3810
4637
|
if (hooksState[stateKey]?.trusted_hash !== expectedHash) return false;
|
|
3811
4638
|
}
|
|
3812
|
-
if (!
|
|
4639
|
+
if (!fs9.existsSync(scriptPath)) return false;
|
|
3813
4640
|
let agents;
|
|
3814
4641
|
try {
|
|
3815
|
-
agents =
|
|
4642
|
+
agents = fs9.readFileSync(codexAgentsPath(), "utf8");
|
|
3816
4643
|
} catch {
|
|
3817
4644
|
return false;
|
|
3818
4645
|
}
|
|
@@ -3837,7 +4664,7 @@ function stripAgentsBlock(p) {
|
|
|
3837
4664
|
|
|
3838
4665
|
// src/bridges/kimi_install.ts
|
|
3839
4666
|
init_define_import_meta_env();
|
|
3840
|
-
import * as
|
|
4667
|
+
import * as fs10 from "node:fs";
|
|
3841
4668
|
import * as os6 from "node:os";
|
|
3842
4669
|
import * as path9 from "node:path";
|
|
3843
4670
|
|
|
@@ -3967,7 +4794,7 @@ function kimiSkillPath() {
|
|
|
3967
4794
|
function readKimiConfig(p, opts = {}) {
|
|
3968
4795
|
let raw;
|
|
3969
4796
|
try {
|
|
3970
|
-
raw =
|
|
4797
|
+
raw = fs10.readFileSync(p, "utf8");
|
|
3971
4798
|
} catch {
|
|
3972
4799
|
return {};
|
|
3973
4800
|
}
|
|
@@ -4070,16 +4897,16 @@ function uninstallKimi() {
|
|
|
4070
4897
|
}
|
|
4071
4898
|
if (stripKimiAgentsBlock(kimiAgentsPath())) removed = true;
|
|
4072
4899
|
try {
|
|
4073
|
-
if (
|
|
4900
|
+
if (fs10.existsSync(kimiSkillDir())) {
|
|
4074
4901
|
removeCreatedBackups(kimiSkillPath());
|
|
4075
|
-
|
|
4902
|
+
fs10.rmSync(kimiSkillDir(), { recursive: true, force: true });
|
|
4076
4903
|
removed = true;
|
|
4077
4904
|
}
|
|
4078
4905
|
} catch {
|
|
4079
4906
|
}
|
|
4080
4907
|
try {
|
|
4081
|
-
if (
|
|
4082
|
-
|
|
4908
|
+
if (fs10.existsSync(kimiHookScriptPath())) {
|
|
4909
|
+
fs10.rmSync(kimiHookScriptPath(), { force: true });
|
|
4083
4910
|
removed = true;
|
|
4084
4911
|
}
|
|
4085
4912
|
} catch {
|
|
@@ -4100,7 +4927,7 @@ function isKimiInstalled() {
|
|
|
4100
4927
|
|
|
4101
4928
|
// src/bridges/grok_install.ts
|
|
4102
4929
|
init_define_import_meta_env();
|
|
4103
|
-
import * as
|
|
4930
|
+
import * as fs11 from "node:fs";
|
|
4104
4931
|
import * as os7 from "node:os";
|
|
4105
4932
|
import * as path10 from "node:path";
|
|
4106
4933
|
|
|
@@ -4244,12 +5071,12 @@ function uninstallGrok() {
|
|
|
4244
5071
|
const scriptPath = grokHookScriptPath();
|
|
4245
5072
|
let removedAny = false;
|
|
4246
5073
|
try {
|
|
4247
|
-
|
|
5074
|
+
fs11.unlinkSync(configPath);
|
|
4248
5075
|
removedAny = true;
|
|
4249
5076
|
} catch {
|
|
4250
5077
|
}
|
|
4251
5078
|
try {
|
|
4252
|
-
|
|
5079
|
+
fs11.unlinkSync(scriptPath);
|
|
4253
5080
|
removedAny = true;
|
|
4254
5081
|
} catch {
|
|
4255
5082
|
}
|
|
@@ -4257,12 +5084,12 @@ function uninstallGrok() {
|
|
|
4257
5084
|
return removedAny;
|
|
4258
5085
|
}
|
|
4259
5086
|
function isGrokInstalled() {
|
|
4260
|
-
return
|
|
5087
|
+
return fs11.existsSync(grokConfigPath()) && fs11.existsSync(grokHookScriptPath());
|
|
4261
5088
|
}
|
|
4262
5089
|
|
|
4263
5090
|
// src/bridges/vscode_install.ts
|
|
4264
5091
|
init_define_import_meta_env();
|
|
4265
|
-
import * as
|
|
5092
|
+
import * as fs12 from "node:fs";
|
|
4266
5093
|
import * as os8 from "node:os";
|
|
4267
5094
|
import * as path11 from "node:path";
|
|
4268
5095
|
var VSCODE_GUIDANCE_BEGIN = "<!-- token-goat-vscode-begin -->";
|
|
@@ -4311,7 +5138,7 @@ function vscodeUserSettingsPath() {
|
|
|
4311
5138
|
function vscodeUsesClaudeHooks(settingsPath2 = vscodeUserSettingsPath()) {
|
|
4312
5139
|
let text;
|
|
4313
5140
|
try {
|
|
4314
|
-
text =
|
|
5141
|
+
text = fs12.readFileSync(settingsPath2, "utf8");
|
|
4315
5142
|
} catch {
|
|
4316
5143
|
return false;
|
|
4317
5144
|
}
|
|
@@ -4326,7 +5153,7 @@ var updateConfig = setTokenGoatServer;
|
|
|
4326
5153
|
function otherScopeHasManagedServer(opts) {
|
|
4327
5154
|
const otherPath = otherScopeMcpPath2(opts);
|
|
4328
5155
|
if (opts.project !== true && !projectPathIsConsultable(otherPath, path11.resolve(opts.projectRoot ?? process.cwd()))) return false;
|
|
4329
|
-
if (!
|
|
5156
|
+
if (!fs12.existsSync(otherPath)) return false;
|
|
4330
5157
|
try {
|
|
4331
5158
|
const config = readConfig(otherPath);
|
|
4332
5159
|
const servers = config.value["servers"];
|
|
@@ -4343,7 +5170,7 @@ function vscodeDecoderConfigured(opts = {}) {
|
|
|
4343
5170
|
if (projectPathIsConsultable(projectPath, path11.resolve(opts.projectRoot))) checkedPaths.push(projectPath);
|
|
4344
5171
|
}
|
|
4345
5172
|
for (const candidate of checkedPaths) {
|
|
4346
|
-
if (!
|
|
5173
|
+
if (!fs12.existsSync(candidate)) continue;
|
|
4347
5174
|
try {
|
|
4348
5175
|
const config = readConfig(candidate);
|
|
4349
5176
|
const servers = config.value["servers"];
|
|
@@ -4358,7 +5185,7 @@ function vscodeDecoderConfigured(opts = {}) {
|
|
|
4358
5185
|
}
|
|
4359
5186
|
function writeGuidance2(filePath, userScope) {
|
|
4360
5187
|
let created = false;
|
|
4361
|
-
if (userScope && !
|
|
5188
|
+
if (userScope && !fs12.existsSync(filePath)) {
|
|
4362
5189
|
ensureDirSync(path11.dirname(filePath));
|
|
4363
5190
|
atomicWriteText(filePath, USER_INSTRUCTIONS_FRONTMATTER);
|
|
4364
5191
|
created = true;
|
|
@@ -4409,7 +5236,7 @@ function installVscodeScoped(opts) {
|
|
|
4409
5236
|
throw new Error(`VS Code MCP JSON at ${mcpPath} already has a non-token-goat-managed server named "token-goat"`);
|
|
4410
5237
|
}
|
|
4411
5238
|
const next = updateConfig(config.text, managedServer());
|
|
4412
|
-
const mcpExisted =
|
|
5239
|
+
const mcpExisted = fs12.existsSync(mcpPath);
|
|
4413
5240
|
ensureDirSync(path11.dirname(mcpPath));
|
|
4414
5241
|
if (config.text !== next) {
|
|
4415
5242
|
backupFile(mcpPath);
|
|
@@ -4435,7 +5262,7 @@ function uninstallVscodeScoped(opts) {
|
|
|
4435
5262
|
assertProjectTargetsAreInTheProject(opts);
|
|
4436
5263
|
const mcpPath = vscodeMcpPath(opts);
|
|
4437
5264
|
let removed = false;
|
|
4438
|
-
if (
|
|
5265
|
+
if (fs12.existsSync(mcpPath)) {
|
|
4439
5266
|
const config = readConfig(mcpPath);
|
|
4440
5267
|
const servers = config.value["servers"];
|
|
4441
5268
|
if (servers !== void 0 && (servers === null || typeof servers !== "object" || Array.isArray(servers))) {
|
|
@@ -4455,7 +5282,7 @@ function uninstallVscodeScoped(opts) {
|
|
|
4455
5282
|
const instructionsPath = vscodeInstructionsPath(opts);
|
|
4456
5283
|
if (stripDelimitedBlock(instructionsPath, BEGIN, END, opts.keepBackups === true)) {
|
|
4457
5284
|
removed = true;
|
|
4458
|
-
if (opts.project !== true &&
|
|
5285
|
+
if (opts.project !== true && fs12.readFileSync(instructionsPath, "utf8").trim() === USER_INSTRUCTIONS_FRONTMATTER.trim()) removeFileInScope(instructionsPath);
|
|
4459
5286
|
}
|
|
4460
5287
|
if (opts.project === true) syncVisualStudioProjectGuidance(instructionsPath);
|
|
4461
5288
|
if (releaseCopilotHooksFile(vscodeHooksDir(opts), "vscode", opts.keepBackups === true)) removed = true;
|
|
@@ -5318,25 +6145,25 @@ function makeLanguageFilter(cfg) {
|
|
|
5318
6145
|
// src/tool_filters/ai_clis.ts
|
|
5319
6146
|
var _GH_COPILOT_SPINNER_RE = /^\s*(?:Asking GitHub Copilot|Generating|Thinking|Fetching)\s*(?:\.{1,3}\s*)?$/i;
|
|
5320
6147
|
var _GH_COPILOT_BANNER_RE = /^\s*(?:Welcome to GitHub Copilot|Using GitHub Copilot|Authenticated as|GitHub Copilot\s+v\d+)/i;
|
|
5321
|
-
var _GH_COPILOT_DISCLAIMER_RE = /^\s*(?:Disclaimer:|This response was|GitHub Copilot|The commands?\s+(?:above|below)|Please review|Always review|Remember to
|
|
6148
|
+
var _GH_COPILOT_DISCLAIMER_RE = /^\s*(?:Disclaimer:|This response was|GitHub Copilot|The commands?\s+(?:above|below)|Please review|Always review|Remember to)/i;
|
|
5322
6149
|
var _AIDER_APPLYING_RE = /^\s*(?:Applying\s+edits?(?:\s+to\s+\S+)?|Applied\s+edit\s+to\s+\S+)\s*(?:\.{1,3}\s*)?$/i;
|
|
5323
6150
|
var _AIDER_TOKENS_RE = /^\s*Tokens:\s+\d[\d,]*\s+sent,\s+\d[\d,]*\s+received/i;
|
|
5324
6151
|
var _AIDER_COST_RE = /^\s*Cost:\s+\$[\d.]+\s+message,\s+\$[\d.]+\s+session/i;
|
|
5325
6152
|
var _AIDER_REPOMAP_RE = /^\s*(?:Repo-map:|Added\s+\S+\s+to\s+the\s+chat|Removed\s+\S+\s+from\s+the\s+chat|Loading\s+repo\s+map|Updating\s+repo\s+map|Scanning\s+repo\s+contents|Using\s+\d+\s+tokens\s+of\s+repo\s+map)/i;
|
|
5326
6153
|
var _AIDER_BANNER_RE = /^\s*aider\s+v\d+\.\d+/i;
|
|
5327
|
-
var _AIDER_FOOTER_NOISE_RE = /^\s*(?:Use\s+ctrl-c|Run\s+with\s+--help|You\s+can\s+skip\s+this
|
|
6154
|
+
var _AIDER_FOOTER_NOISE_RE = /^\s*(?:Use\s+ctrl-c|Run\s+with\s+--help|You\s+can\s+skip\s+this)/i;
|
|
5328
6155
|
var _COPILOT_WORKSPACE_NOISE_RE = /^\s*(?:Starting\s+Copilot\s+workspace|Loading\s+model:|Copilot\s+workspace\s+(?:starting|loaded|ready)|Streaming\.\.\.|▌\s*$|Turn\s+\d+\s*:)/i;
|
|
5329
6156
|
var _COPILOT_COMPLETION_STATS_RE = /^\s*(?:Completion\s+tokens:|Prompt\s+tokens:|Total\s+tokens:|Input\s+tokens:|Output\s+tokens:)\s*\d/i;
|
|
5330
6157
|
var _GEMINI_STARTUP_RE = /^\s*(?:[✓✗►]|>)\s*(?:Model:|Theme:|Tools:|Sandbox:|Checkpointing:|Context(?:\s+limit)?:|Version:|Authenticated(?:\s+as)?:|Connecting)/i;
|
|
5331
6158
|
var _GEMINI_BANNER_RE = /^\s*Gemini\s+CLI\s+v\d+/i;
|
|
5332
6159
|
var _GEMINI_TOKEN_METER_RE = /^\s*(?:Token\s+usage|Context|Tokens):\s+[\d,]+\s*\/\s*[\d,]+/i;
|
|
5333
6160
|
var _GEMINI_TOOL_SPINNER_RE = /^\s*[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏✓✗►✦]\s+(?:Call(?:ing|ed)|Execut(?:ing|ed)|Running)\s+\S+/;
|
|
5334
|
-
var _GEMINI_FOOTER_RE = /^\s*(?:Type\s+\/help|Press\s+Ctrl|Use\s+Ctrl
|
|
6161
|
+
var _GEMINI_FOOTER_RE = /^\s*(?:Type\s+\/help|Press\s+Ctrl|Use\s+Ctrl)/i;
|
|
5335
6162
|
var _GEMINI_THINKING_RE = /^\s*(?:Thinking|Generating|Processing)\s*(?:\.{1,3}\s*)?$/i;
|
|
5336
6163
|
var _CLAUDE_CLI_MODEL_HDR_RE = /^\s*[◆◇►✦]\s+claude-/i;
|
|
5337
6164
|
var _CLAUDE_CLI_STATS_RE = /^\s*[↑↓⇑⇓]\s*\d[\d,]*(?:(?:\s*[↑↓⇑⇓]\s*|\s+)\d[\d,]*)?\s*tokens/i;
|
|
5338
6165
|
var _CLAUDE_CLI_CONTEXT_RE = /^\s*(?:Context(?:\s+window)?|Token\s+limit):\s+[\d,]+\s*\/\s*[\d,]+/i;
|
|
5339
|
-
var _CLAUDE_CLI_FOOTER_RE = /^\s*(?:Press\s+Ctrl|Enter\s+\/|Type\s+\/|Use\s+Ctrl
|
|
6166
|
+
var _CLAUDE_CLI_FOOTER_RE = /^\s*(?:Press\s+Ctrl|Enter\s+\/|Type\s+\/|Use\s+Ctrl)/i;
|
|
5340
6167
|
var _CLAUDE_CLI_SPINNER_RE = /^\s*[◎⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]\s+(?:Thinking|Generating|Processing|Running)\s*(?:\.{1,3}\s*)?$/;
|
|
5341
6168
|
var _CLAUDE_CLI_TOOL_LOG_RE = /^\s*(?:>\s+Using\s+tool:|✓\s+Tool\s+result:|◎\s+Tool:)/i;
|
|
5342
6169
|
var _CLAUDE_CLI_SKIP_SUBCMDS = /* @__PURE__ */ new Set(["install", "update", "doctor", "config", "login", "logout"]);
|
|
@@ -8315,7 +9142,8 @@ ${stderr.replace(/\s+$/, "")}` : stderr;
|
|
|
8315
9142
|
var awsFilter = new AwsFilter();
|
|
8316
9143
|
var _AWS_UPLOAD_RE = /^upload:\s+\S+\s+to\s+s3:\/\//i;
|
|
8317
9144
|
var _AWS_DOWNLOAD_RE = /^download:\s+s3:\/\//i;
|
|
8318
|
-
var
|
|
9145
|
+
var _AWS_S3_TRANSFER_FAILED_RE = /^(?:upload|download|copy|delete|move)\s+failed:/i;
|
|
9146
|
+
var _AWS_S3_PROGRESS_RE = /^(?:Completed\s+\d|\d+(?:\.\d+)?\s*(?:KiB|MiB|GiB|B)\/s|Calculating)/i;
|
|
8319
9147
|
var AWS_GLOBAL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
8320
9148
|
"--profile",
|
|
8321
9149
|
"--region",
|
|
@@ -8337,7 +9165,8 @@ var AwsCliFilter = class extends ToolFilter {
|
|
|
8337
9165
|
_JSON_ARRAY_KEEP = 3;
|
|
8338
9166
|
compressBody(stdout, stderr, _exitCode, argv) {
|
|
8339
9167
|
const positionals = positionalArgs(argv.slice(1), AWS_GLOBAL_VALUE_FLAGS);
|
|
8340
|
-
const isS3Transfer = positionals.length >= 2 && positionals[0] === "s3" &&
|
|
9168
|
+
const isS3Transfer = positionals.length >= 2 && positionals[0] === "s3" && // `rm` belongs here for the failure path, not the volume one: `aws s3 rm --recursive` reports `delete failed:` per object, and routing it anywhere else meant those lines were never counted. Its `delete:` success lines are not folded into a count -- only `upload:`/`download:` are -- so adding it drops nothing that used to survive.
|
|
9169
|
+
(positionals[1] === "cp" || positionals[1] === "sync" || positionals[1] === "mv" || positionals[1] === "rm");
|
|
8341
9170
|
const isCfnEvents = positionals.length >= 2 && positionals[0] === "cloudformation" && positionals[1] === "describe-stack-events";
|
|
8342
9171
|
let text = stdout;
|
|
8343
9172
|
if (isS3Transfer) {
|
|
@@ -8369,6 +9198,7 @@ ${stderr.replace(/\s+$/, "")}` : stderr;
|
|
|
8369
9198
|
const kept = [];
|
|
8370
9199
|
let uploadCount = 0;
|
|
8371
9200
|
let downloadCount = 0;
|
|
9201
|
+
let failedCount = 0;
|
|
8372
9202
|
let progressDropped = 0;
|
|
8373
9203
|
for (const line of lines) {
|
|
8374
9204
|
if (_AWS_UPLOAD_RE.test(line)) {
|
|
@@ -8379,6 +9209,11 @@ ${stderr.replace(/\s+$/, "")}` : stderr;
|
|
|
8379
9209
|
downloadCount++;
|
|
8380
9210
|
continue;
|
|
8381
9211
|
}
|
|
9212
|
+
if (_AWS_S3_TRANSFER_FAILED_RE.test(line)) {
|
|
9213
|
+
failedCount++;
|
|
9214
|
+
kept.push(line);
|
|
9215
|
+
continue;
|
|
9216
|
+
}
|
|
8382
9217
|
if (_AWS_S3_PROGRESS_RE.test(line)) {
|
|
8383
9218
|
progressDropped++;
|
|
8384
9219
|
continue;
|
|
@@ -8388,6 +9223,7 @@ ${stderr.replace(/\s+$/, "")}` : stderr;
|
|
|
8388
9223
|
const notes = [];
|
|
8389
9224
|
maybeNote(notes, uploadCount, `uploaded ${uploadCount} file(s)`);
|
|
8390
9225
|
maybeNote(notes, downloadCount, `downloaded ${downloadCount} file(s)`);
|
|
9226
|
+
maybeNote(notes, failedCount, `${failedCount} transfer(s) failed`);
|
|
8391
9227
|
maybeNote(notes, progressDropped, `dropped ${progressDropped} progress line(s)`);
|
|
8392
9228
|
this.emitNotes(kept, notes);
|
|
8393
9229
|
return this.finalize(kept);
|
|
@@ -9488,8 +10324,9 @@ var RgFilter = class _RgFilter extends ToolFilter {
|
|
|
9488
10324
|
name = "rg";
|
|
9489
10325
|
binaries = /* @__PURE__ */ new Set(["rg", "grep"]);
|
|
9490
10326
|
static _SEP = "--";
|
|
9491
|
-
|
|
9492
|
-
static
|
|
10327
|
+
// The path prefix is optional because ripgrep only prints one when the search covers more than one file: `rg -n -C2 pat one.ts` emits `12-text` and `14:text`, not `one.ts-12-text` and `one.ts:14:text`. Requiring the prefix meant neither regex matched anything on a single-file search -- the ordinary shape of the command -- so no context line was ever suppressed there, and the only thing removed was the cosmetic `--` separator, under a note announcing a compression that had not happened.
|
|
10328
|
+
static _CTX_LINE_RE = /^(?:.+-)?\d+-/;
|
|
10329
|
+
static _MATCH_LINE_RE = /^(?:.+:)?\d+:/;
|
|
9493
10330
|
static _parseContextDepth(argv) {
|
|
9494
10331
|
let depth = 0;
|
|
9495
10332
|
const longFlags = /* @__PURE__ */ new Set(["--after-context", "--before-context", "--context"]);
|
|
@@ -9529,8 +10366,14 @@ var RgFilter = class _RgFilter extends ToolFilter {
|
|
|
9529
10366
|
// RgFilter only handles context-block output (-A/-B/-C/--context); a plain
|
|
9530
10367
|
// grep/rg with no context flags falls through to GrepFilter's per-file
|
|
9531
10368
|
// match-count summarizer, which produces dramatically smaller output.
|
|
10369
|
+
// Both line regexes hard-code ripgrep's default field separators (`:` after a match's line number, `-` after a context line's). `--field-match-separator=-` makes a real match read `12-text`, which is exactly the shape this filter drops, so a search that overrides either separator is released to the generic path rather than compressed against an assumption it broke.
|
|
10370
|
+
static _hasCustomFieldSeparator(argv) {
|
|
10371
|
+
const flags = ["--field-match-separator", "--field-context-separator"];
|
|
10372
|
+
return argv.some((a) => flags.some((f) => a === f || a.startsWith(f + "=")));
|
|
10373
|
+
}
|
|
9532
10374
|
matches(argv) {
|
|
9533
10375
|
if (!super.matches(argv)) return false;
|
|
10376
|
+
if (_RgFilter._hasCustomFieldSeparator(argv)) return false;
|
|
9534
10377
|
return _RgFilter._hasContextFlags(argv);
|
|
9535
10378
|
}
|
|
9536
10379
|
_compressGroups(groups) {
|
|
@@ -9563,7 +10406,8 @@ var RgFilter = class _RgFilter extends ToolFilter {
|
|
|
9563
10406
|
const kept = [];
|
|
9564
10407
|
let suppressed = 0;
|
|
9565
10408
|
for (const ln of lines) {
|
|
9566
|
-
if (ln === _RgFilter._SEP
|
|
10409
|
+
if (ln === _RgFilter._SEP) continue;
|
|
10410
|
+
if (_RgFilter._CTX_LINE_RE.test(ln) && !_RgFilter._MATCH_LINE_RE.test(ln)) {
|
|
9567
10411
|
suppressed++;
|
|
9568
10412
|
} else {
|
|
9569
10413
|
kept.push(ln);
|
|
@@ -13231,6 +14075,8 @@ var _DOCKER_OLD_STEP_ERROR_RE = /error|returned a non-zero code/i;
|
|
|
13231
14075
|
var DockerFilter = class extends ToolFilter {
|
|
13232
14076
|
name = "docker";
|
|
13233
14077
|
binaries = /* @__PURE__ */ new Set(["docker", "buildah", "podman", "nerdctl"]);
|
|
14078
|
+
// Every rule below is about image transfer and build noise -- layer digests, pull/push progress, cached step lines, step bodies -- so the filter is declared for exactly the subcommands that emit them, including buildah's `bud` spelling of build, `create` and `plugin install`/`plugin upgrade`, which pull layers and emit the same per-layer status lines as `pull`. The base matcher scans the first three positionals, so the management-command spellings (`docker image pull`, `docker container create`) are covered by the same entries. With no subcommand set it claimed every `docker …` invocation and removed a single blank line from the ones it has no rules for, which matters because dispatch stops at the first match: `docker logs` on a service repeating a heartbeat went 8702 bytes in, 8701 out, where the generic compressor it was displacing returns 6987. On output with nothing repeated (`docker images` here: 15912 bytes) neither saves anything, so the gain is confined to the repetitive case -- but a filter that claims a command it cannot compress blocks whatever could.
|
|
14079
|
+
subcommands = /* @__PURE__ */ new Set(["build", "bud", "buildx", "pull", "push", "run", "load", "create", "install", "upgrade"]);
|
|
13234
14080
|
// Docker writes progress/errors to stderr; only stdout carries build bodies. Merge with stderr first so errors appear before raw build output.
|
|
13235
14081
|
compress(stdout, stderr, _exitCode, _argv) {
|
|
13236
14082
|
const merged = this.combineOutput(stderr, stdout);
|
|
@@ -13457,6 +14303,7 @@ var _KEY_PREFIXES = [
|
|
|
13457
14303
|
"Namespace:",
|
|
13458
14304
|
"Status:",
|
|
13459
14305
|
"State:",
|
|
14306
|
+
"Last State:",
|
|
13460
14307
|
"Node:",
|
|
13461
14308
|
"IP:",
|
|
13462
14309
|
"PodIP:",
|
|
@@ -13464,6 +14311,7 @@ var _KEY_PREFIXES = [
|
|
|
13464
14311
|
"QoS Class:",
|
|
13465
14312
|
"Priority:",
|
|
13466
14313
|
"Image:",
|
|
14314
|
+
"Container ID:",
|
|
13467
14315
|
"Ready:",
|
|
13468
14316
|
"Restart Count:",
|
|
13469
14317
|
"Started:",
|
|
@@ -14269,8 +15117,8 @@ function _capHunksByDensity(hunks, maxHunksPerFile) {
|
|
|
14269
15117
|
};
|
|
14270
15118
|
}
|
|
14271
15119
|
function _compressGitDiffBody(stdout, stderr, maxHunksPerFile = 10) {
|
|
14272
|
-
const MAX_HUNK_CHANGED =
|
|
14273
|
-
const HUNK_HEAD_KEEP =
|
|
15120
|
+
const MAX_HUNK_CHANGED = 25;
|
|
15121
|
+
const HUNK_HEAD_KEEP = 15;
|
|
14274
15122
|
const HUNK_TAIL_KEEP = 5;
|
|
14275
15123
|
const fileBlocks = splitBlocks(stdout, _GIT_DIFF_FILE_RE);
|
|
14276
15124
|
if (!fileBlocks.length) return stdout;
|
|
@@ -14401,12 +15249,18 @@ function _compressGitStatusVerbose(stdout, stderr, argv = null) {
|
|
|
14401
15249
|
const kept = [];
|
|
14402
15250
|
let section = null;
|
|
14403
15251
|
let counts = {};
|
|
15252
|
+
let fileLines = [];
|
|
14404
15253
|
function flush() {
|
|
14405
|
-
if (section !== null && section !== "unmerged" &&
|
|
14406
|
-
|
|
14407
|
-
|
|
15254
|
+
if (section !== null && section !== "unmerged" && fileLines.length) {
|
|
15255
|
+
if (fileLines.length > _DIFF_STAT_DIR_ROLLUP_THRESHOLD) {
|
|
15256
|
+
const parts = Object.entries(counts).map(([label, n]) => `${n} ${label}`);
|
|
15257
|
+
kept.push(" " + parts.join(", "));
|
|
15258
|
+
} else {
|
|
15259
|
+
kept.push(...fileLines);
|
|
15260
|
+
}
|
|
14408
15261
|
}
|
|
14409
15262
|
counts = {};
|
|
15263
|
+
fileLines = [];
|
|
14410
15264
|
}
|
|
14411
15265
|
for (const line of lines) {
|
|
14412
15266
|
const headerMatch = _GIT_STATUS_SECTION_RE.exec(line);
|
|
@@ -14423,6 +15277,7 @@ function _compressGitStatusVerbose(stdout, stderr, argv = null) {
|
|
|
14423
15277
|
} else {
|
|
14424
15278
|
const label = _gitStatusFileLabel(line, section);
|
|
14425
15279
|
counts[label] = (counts[label] ?? 0) + 1;
|
|
15280
|
+
fileLines.push(line);
|
|
14426
15281
|
}
|
|
14427
15282
|
continue;
|
|
14428
15283
|
}
|
|
@@ -15909,6 +16764,7 @@ var _PHPSTAN_SEP_RE = /^\s*-{3,}/;
|
|
|
15909
16764
|
var _PHPSTAN_FILE_HEADER_RE = /^\s+Line\s+(\S.*\.php)\s*$/;
|
|
15910
16765
|
var _PHPSTAN_ROW_RE = /^\s+(\d+)\s+(.+)$/;
|
|
15911
16766
|
var _PHPSTAN_SUMMARY_RE = /^\s*\[(ERROR|OK|WARNING|NOTE)\]/i;
|
|
16767
|
+
var _PHPSTAN_ROW_CONTINUATION_RE = /^\s{2,}\S/;
|
|
15912
16768
|
var _PSALM_ERROR_RE = /^(ERROR|INFO|FATAL): \w+ - .+\.php:\d+/i;
|
|
15913
16769
|
var _PSALM_PROGRESS_RE = /^(Scanning|Analyzing|Checking|Parsing|Caching|Target PHP|Psalm|PHP version|Running Psalm|Checked \d)/i;
|
|
15914
16770
|
var _PHPSTAN_INFO_RE = /^(Note: |Loading config|Found cached|Autoload|Bootstrapping|PHPStan - PHP Static|Psalm is running)/i;
|
|
@@ -15928,6 +16784,7 @@ var PhpStanFilter = class extends ToolFilter {
|
|
|
15928
16784
|
let droppedSep = 0;
|
|
15929
16785
|
let droppedInfo = 0;
|
|
15930
16786
|
let currentFile = "";
|
|
16787
|
+
let lastRowKept = true;
|
|
15931
16788
|
const fileMsgs = /* @__PURE__ */ new Map();
|
|
15932
16789
|
const flushFileDedup = (file) => {
|
|
15933
16790
|
const msgs = fileMsgs.get(file);
|
|
@@ -15945,11 +16802,13 @@ var PhpStanFilter = class extends ToolFilter {
|
|
|
15945
16802
|
flushFileDedup(currentFile);
|
|
15946
16803
|
currentFile = "";
|
|
15947
16804
|
}
|
|
16805
|
+
lastRowKept = true;
|
|
15948
16806
|
kept.push(line);
|
|
15949
16807
|
continue;
|
|
15950
16808
|
}
|
|
15951
16809
|
if (_PHPSTAN_SEP_RE.test(line) && !_PHPSTAN_ROW_RE.test(line)) {
|
|
15952
16810
|
droppedSep++;
|
|
16811
|
+
lastRowKept = true;
|
|
15953
16812
|
continue;
|
|
15954
16813
|
}
|
|
15955
16814
|
if (_PHPSTAN_FILE_HEADER_RE.test(line)) {
|
|
@@ -15957,6 +16816,7 @@ var PhpStanFilter = class extends ToolFilter {
|
|
|
15957
16816
|
const headerMatch = _PHPSTAN_FILE_HEADER_RE.exec(line);
|
|
15958
16817
|
currentFile = headerMatch?.[1] ? headerMatch[1].trim() : line.trim();
|
|
15959
16818
|
if (!fileMsgs.has(currentFile)) fileMsgs.set(currentFile, /* @__PURE__ */ new Map());
|
|
16819
|
+
lastRowKept = true;
|
|
15960
16820
|
kept.push(line);
|
|
15961
16821
|
continue;
|
|
15962
16822
|
}
|
|
@@ -15966,9 +16826,11 @@ var PhpStanFilter = class extends ToolFilter {
|
|
|
15966
16826
|
const counts = fileMsgs.get(currentFile);
|
|
15967
16827
|
const count = (counts.get(msg) ?? 0) + 1;
|
|
15968
16828
|
counts.set(msg, count);
|
|
15969
|
-
|
|
16829
|
+
lastRowKept = count <= 3;
|
|
16830
|
+
if (lastRowKept) kept.push(line);
|
|
15970
16831
|
continue;
|
|
15971
16832
|
}
|
|
16833
|
+
if (currentFile && !lastRowKept && _PHPSTAN_ROW_CONTINUATION_RE.test(line)) continue;
|
|
15972
16834
|
kept.push(line);
|
|
15973
16835
|
}
|
|
15974
16836
|
if (currentFile) flushFileDedup(currentFile);
|
|
@@ -17730,6 +18592,7 @@ export {
|
|
|
17730
18592
|
getToolInput,
|
|
17731
18593
|
getFilePath,
|
|
17732
18594
|
getCwd,
|
|
18595
|
+
getTranscriptPath,
|
|
17733
18596
|
extractToolResponseField,
|
|
17734
18597
|
OUTPUT_FIRST_TOOL_RESPONSE_KEYS,
|
|
17735
18598
|
BODY_FIRST_TOOL_RESPONSE_KEYS,
|
|
@@ -17793,6 +18656,27 @@ export {
|
|
|
17793
18656
|
capTokens,
|
|
17794
18657
|
shlexSplit,
|
|
17795
18658
|
clipLongMatchLine,
|
|
18659
|
+
isLockFile,
|
|
18660
|
+
isManifestFile,
|
|
18661
|
+
isInBuildDir,
|
|
18662
|
+
isGeneratedFile,
|
|
18663
|
+
isBuildCommand,
|
|
18664
|
+
isTestRunnerCommand,
|
|
18665
|
+
getMonitoringRecallHint,
|
|
18666
|
+
RECALL_DEFAULT_LIMIT,
|
|
18667
|
+
isRecallCacheType,
|
|
18668
|
+
indexRecallEntry,
|
|
18669
|
+
listRecentRecall,
|
|
18670
|
+
searchRecall,
|
|
18671
|
+
BASH_OUTPUT_SUBDIR,
|
|
18672
|
+
isScopedGitStatusOrDiffStatCommand,
|
|
18673
|
+
normalizeCommandForCacheKey,
|
|
18674
|
+
commandHash,
|
|
18675
|
+
isBashEntryStale,
|
|
18676
|
+
summarizeOutputDelta,
|
|
18677
|
+
storeBashOutputSync,
|
|
18678
|
+
storeBashOutput,
|
|
18679
|
+
getBashOutput,
|
|
17796
18680
|
anchoredMarkerPattern,
|
|
17797
18681
|
settingsPath,
|
|
17798
18682
|
installHooks,
|