token-goat 2.9.18 → 2.9.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{token-goat-chunk-V3ZWBEHM.mjs → token-goat-chunk-2GTB5RBZ.mjs} +365 -26
- package/dist/{token-goat-chunk-ARNRFR6M.mjs → token-goat-chunk-3W5P673V.mjs} +1 -1
- package/dist/{token-goat-chunk-YQZE5NS6.mjs → token-goat-chunk-62VJ47WN.mjs} +6 -6
- package/dist/{token-goat-chunk-CTOYJ232.mjs → token-goat-chunk-7DGTVBWP.mjs} +45 -8
- package/dist/{token-goat-chunk-5MMJAJF3.mjs → token-goat-chunk-AVRD7SBO.mjs} +60 -8
- package/dist/{token-goat-chunk-7VLWNQDQ.mjs → token-goat-chunk-BTKDZW7F.mjs} +1 -1
- package/dist/{token-goat-chunk-LMFO66YD.mjs → token-goat-chunk-DA43OE7L.mjs} +1 -1
- package/dist/{token-goat-chunk-6LGVCCMY.mjs → token-goat-chunk-DA6TIDLI.mjs} +21 -16
- package/dist/{token-goat-chunk-UENEL6KX.mjs → token-goat-chunk-DHIXE6GV.mjs} +2 -2
- package/dist/{token-goat-chunk-W3E65QOW.mjs → token-goat-chunk-EP7TCWHM.mjs} +304 -46
- package/dist/{token-goat-chunk-HIZXQVNU.mjs → token-goat-chunk-EXLLLIC3.mjs} +4 -4
- package/dist/{token-goat-chunk-H3FONFBO.mjs → token-goat-chunk-F24NE3FO.mjs} +3 -3
- package/dist/{token-goat-chunk-LA3O4JGD.mjs → token-goat-chunk-FJZ5MZK7.mjs} +3 -3
- package/dist/{token-goat-chunk-BFEWOMA3.mjs → token-goat-chunk-FQYMZASI.mjs} +632 -352
- package/dist/{token-goat-chunk-OWSJJY5R.mjs → token-goat-chunk-FX5DHKGF.mjs} +5 -5
- package/dist/{token-goat-chunk-K4DG6EEF.mjs → token-goat-chunk-GYC34EGE.mjs} +7 -8
- package/dist/{token-goat-chunk-WFX2V3OF.mjs → token-goat-chunk-HPMMLV37.mjs} +358 -116
- package/dist/{token-goat-chunk-I67CPTDD.mjs → token-goat-chunk-IBTFGZBX.mjs} +19 -14
- package/dist/{token-goat-chunk-L7VCX33I.mjs → token-goat-chunk-JNIFSVWM.mjs} +36 -10
- package/dist/{token-goat-chunk-EPXNOKIV.mjs → token-goat-chunk-KWM6KBPA.mjs} +2 -2
- package/dist/{token-goat-chunk-BWCRC23L.mjs → token-goat-chunk-L2UOJBV4.mjs} +21 -20
- package/dist/{token-goat-chunk-52IMXJ2L.mjs → token-goat-chunk-LACH3WKS.mjs} +4 -4
- package/dist/token-goat-chunk-MU6I4IDI.mjs +337 -0
- package/dist/{token-goat-chunk-GRI6HWX3.mjs → token-goat-chunk-NJKNAC37.mjs} +1 -1
- package/dist/{token-goat-chunk-ERTXEKB6.mjs → token-goat-chunk-NMTKNYGF.mjs} +2 -0
- package/dist/{token-goat-chunk-34OUJ2IE.mjs → token-goat-chunk-NYFYIRG3.mjs} +12 -12
- package/dist/{token-goat-chunk-4EXFN2AW.mjs → token-goat-chunk-OZRSREKR.mjs} +2 -2
- package/dist/token-goat-chunk-P46RTOAQ.mjs +27 -0
- package/dist/{token-goat-chunk-QML7ITHJ.mjs → token-goat-chunk-PIAESKVU.mjs} +139 -23
- package/dist/{token-goat-chunk-HPOFR6QX.mjs → token-goat-chunk-S7RFIX4Y.mjs} +180 -50
- package/dist/{token-goat-chunk-CA6V36KW.mjs → token-goat-chunk-SOKAPOOC.mjs} +23 -4
- package/dist/token-goat-chunk-VJQBYSXG.mjs +20 -0
- package/dist/{token-goat-chunk-LTYWLKAG.mjs → token-goat-chunk-WKO5TVNA.mjs} +922 -611
- package/dist/{token-goat-chunk-QIDLJGXZ.mjs → token-goat-chunk-ZGQXRYKE.mjs} +29 -5
- package/dist/token-goat-hook.mjs +12 -12
- package/dist/token-goat.core.mjs +21 -20
- package/docs/cli.md +2 -1
- package/docs/security.md +1 -1
- package/package.json +1 -1
- package/dist/token-goat-chunk-2KTZ6J7T.mjs +0 -173
- package/dist/token-goat-chunk-RHHFEXKF.mjs +0 -93
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
lowercaseDriveLetter,
|
|
9
9
|
normalizeDarwinSystemAlias,
|
|
10
10
|
normalizePath
|
|
11
|
-
} from "./token-goat-chunk-
|
|
11
|
+
} from "./token-goat-chunk-NMTKNYGF.mjs";
|
|
12
12
|
import {
|
|
13
13
|
compileGuardedRegex
|
|
14
14
|
} from "./token-goat-chunk-GMOUBOX4.mjs";
|
|
@@ -21,7 +21,7 @@ init_define_import_meta_env();
|
|
|
21
21
|
import { createRequire } from "node:module";
|
|
22
22
|
function resolveVersion() {
|
|
23
23
|
if (true) {
|
|
24
|
-
return "2.9.
|
|
24
|
+
return "2.9.19";
|
|
25
25
|
}
|
|
26
26
|
const require2 = createRequire(import.meta.url);
|
|
27
27
|
const pkg = require2("../package.json");
|
|
@@ -222,6 +222,10 @@ function isCaseInsensitiveFs() {
|
|
|
222
222
|
function foldPath(p) {
|
|
223
223
|
return isCaseInsensitiveFs() ? foldCase(p) : p;
|
|
224
224
|
}
|
|
225
|
+
function isNodeModulesPath(p) {
|
|
226
|
+
const check = foldPath(p);
|
|
227
|
+
return check.includes("/node_modules/") || check.includes("\\node_modules\\");
|
|
228
|
+
}
|
|
225
229
|
function foldCase(s) {
|
|
226
230
|
return s.toLowerCase();
|
|
227
231
|
}
|
|
@@ -599,7 +603,7 @@ function encodeUtf32(text, littleEndian) {
|
|
|
599
603
|
// src/util.ts
|
|
600
604
|
init_define_import_meta_env();
|
|
601
605
|
import { spawn } from "node:child_process";
|
|
602
|
-
import { chmodSync as chmodSync2, closeSync, constants as fsConstants, copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync3, renameSync, statSync as statSync3, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
606
|
+
import { chmodSync as chmodSync2, closeSync, constants as fsConstants, copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2, openSync, readdirSync, readFileSync as readFileSync3, renameSync, rmdirSync, statSync as statSync3, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
603
607
|
import * as path7 from "node:path";
|
|
604
608
|
|
|
605
609
|
// src/bridges/created_configs.ts
|
|
@@ -734,7 +738,7 @@ function stripOwnHooksFromMap(hooks, isOurs) {
|
|
|
734
738
|
const kept = [];
|
|
735
739
|
for (const group of groups) {
|
|
736
740
|
const keptHooks = (group.hooks ?? []).filter((h) => {
|
|
737
|
-
const isOur = isOurs(h.command);
|
|
741
|
+
const isOur = isOurs(h.command, h.args);
|
|
738
742
|
if (isOur) removed = true;
|
|
739
743
|
return !isOur;
|
|
740
744
|
});
|
|
@@ -760,7 +764,7 @@ function stripStaleGroupHooks(groups, isOurs, matcherFilter) {
|
|
|
760
764
|
next.push(group);
|
|
761
765
|
continue;
|
|
762
766
|
}
|
|
763
|
-
const keptHooks = (group.hooks ?? []).filter((h) => !isOurs(h.command));
|
|
767
|
+
const keptHooks = (group.hooks ?? []).filter((h) => !isOurs(h.command, h.args));
|
|
764
768
|
if (keptHooks.length > 0) {
|
|
765
769
|
next.push({ ...group, hooks: keptHooks });
|
|
766
770
|
} else if ((group.hooks ?? []).length === 0) {
|
|
@@ -982,6 +986,19 @@ function removeFileInScope(p) {
|
|
|
982
986
|
return false;
|
|
983
987
|
}
|
|
984
988
|
}
|
|
989
|
+
function removeEmptyDirInScope(dirPath) {
|
|
990
|
+
assertWriteInScope(dirPath);
|
|
991
|
+
try {
|
|
992
|
+
const entries = readdirSync(dirPath);
|
|
993
|
+
if (entries.length === 0) {
|
|
994
|
+
rmdirSync(dirPath);
|
|
995
|
+
return true;
|
|
996
|
+
}
|
|
997
|
+
return false;
|
|
998
|
+
} catch {
|
|
999
|
+
return false;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
985
1002
|
function uninstallSingleFilePlugin(filePath, sidecarPath) {
|
|
986
1003
|
removeFileInScope(sidecarPath);
|
|
987
1004
|
return removeFileInScope(filePath);
|
|
@@ -1243,6 +1260,10 @@ function hookCommandFor(scriptPath, event) {
|
|
|
1243
1260
|
const entryArg = entryPath ? ` ${quoteShellPath(entryPath)}` : "";
|
|
1244
1261
|
return `${quoteShellPath(process.execPath)} ${quoteShellPath(scriptPath)} ${event}${entryArg}`;
|
|
1245
1262
|
}
|
|
1263
|
+
function hookExecPartsFor(scriptPath, event) {
|
|
1264
|
+
const entryPath = process.argv[1];
|
|
1265
|
+
return { command: process.execPath, args: entryPath ? [scriptPath, event, entryPath] : [scriptPath, event] };
|
|
1266
|
+
}
|
|
1246
1267
|
function hookPowershellCommand(scriptPath, event) {
|
|
1247
1268
|
const entryPath = process.argv[1];
|
|
1248
1269
|
const entryArg = entryPath ? ` ${quotePowershellPath(entryPath)}` : "";
|
|
@@ -1269,6 +1290,7 @@ export {
|
|
|
1269
1290
|
removeCreatedBackups,
|
|
1270
1291
|
isCaseInsensitiveFs,
|
|
1271
1292
|
foldPath,
|
|
1293
|
+
isNodeModulesPath,
|
|
1272
1294
|
foldCase,
|
|
1273
1295
|
foldCaseForContainment,
|
|
1274
1296
|
foldPathForContainment,
|
|
@@ -1304,6 +1326,7 @@ export {
|
|
|
1304
1326
|
atomicWriteBytes,
|
|
1305
1327
|
installSingleFilePlugin,
|
|
1306
1328
|
removeFileInScope,
|
|
1329
|
+
removeEmptyDirInScope,
|
|
1307
1330
|
uninstallSingleFilePlugin,
|
|
1308
1331
|
backupFile,
|
|
1309
1332
|
PER_FILE_COUNTERFACTUAL_CEILING,
|
|
@@ -1337,5 +1360,6 @@ export {
|
|
|
1337
1360
|
isUnderBlockedRoot,
|
|
1338
1361
|
isWithinQuietHours,
|
|
1339
1362
|
hookCommandFor,
|
|
1363
|
+
hookExecPartsFor,
|
|
1340
1364
|
hookPowershellCommand
|
|
1341
1365
|
};
|
package/dist/token-goat-hook.mjs
CHANGED
|
@@ -2,21 +2,21 @@ import { createRequire as __cjsRequire } from 'node:module';
|
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
relayInProcess
|
|
5
|
-
} from "./token-goat-chunk-
|
|
6
|
-
import "./token-goat-chunk-
|
|
7
|
-
import "./token-goat-chunk-
|
|
8
|
-
import "./token-goat-chunk-
|
|
9
|
-
import "./token-goat-chunk-
|
|
10
|
-
import "./token-goat-chunk-
|
|
5
|
+
} from "./token-goat-chunk-HPMMLV37.mjs";
|
|
6
|
+
import "./token-goat-chunk-MU6I4IDI.mjs";
|
|
7
|
+
import "./token-goat-chunk-2GTB5RBZ.mjs";
|
|
8
|
+
import "./token-goat-chunk-FQYMZASI.mjs";
|
|
9
|
+
import "./token-goat-chunk-7DGTVBWP.mjs";
|
|
10
|
+
import "./token-goat-chunk-S7RFIX4Y.mjs";
|
|
11
11
|
import "./token-goat-chunk-3BTK54F3.mjs";
|
|
12
|
-
import "./token-goat-chunk-
|
|
13
|
-
import "./token-goat-chunk-
|
|
14
|
-
import "./token-goat-chunk-
|
|
15
|
-
import "./token-goat-chunk-
|
|
12
|
+
import "./token-goat-chunk-EP7TCWHM.mjs";
|
|
13
|
+
import "./token-goat-chunk-IBTFGZBX.mjs";
|
|
14
|
+
import "./token-goat-chunk-JNIFSVWM.mjs";
|
|
15
|
+
import "./token-goat-chunk-SOKAPOOC.mjs";
|
|
16
16
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
17
17
|
import "./token-goat-chunk-OSUFN2FV.mjs";
|
|
18
|
-
import "./token-goat-chunk-
|
|
19
|
-
import "./token-goat-chunk-
|
|
18
|
+
import "./token-goat-chunk-ZGQXRYKE.mjs";
|
|
19
|
+
import "./token-goat-chunk-NMTKNYGF.mjs";
|
|
20
20
|
import "./token-goat-chunk-Y4AFKTHK.mjs";
|
|
21
21
|
import "./token-goat-chunk-GMOUBOX4.mjs";
|
|
22
22
|
import "./token-goat-chunk-RRNZMM3A.mjs";
|
package/dist/token-goat.core.mjs
CHANGED
|
@@ -2,34 +2,35 @@ import { createRequire as __cjsRequire } from 'node:module';
|
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
run
|
|
5
|
-
} from "./token-goat-chunk-
|
|
6
|
-
import "./token-goat-chunk-
|
|
7
|
-
import "./token-goat-chunk-
|
|
8
|
-
import "./token-goat-chunk-
|
|
9
|
-
import "./token-goat-chunk-
|
|
10
|
-
import "./token-goat-chunk-
|
|
11
|
-
import "./token-goat-chunk-
|
|
12
|
-
import "./token-goat-chunk-
|
|
5
|
+
} from "./token-goat-chunk-WKO5TVNA.mjs";
|
|
6
|
+
import "./token-goat-chunk-GYC34EGE.mjs";
|
|
7
|
+
import "./token-goat-chunk-PIAESKVU.mjs";
|
|
8
|
+
import "./token-goat-chunk-F24NE3FO.mjs";
|
|
9
|
+
import "./token-goat-chunk-DA43OE7L.mjs";
|
|
10
|
+
import "./token-goat-chunk-2GTB5RBZ.mjs";
|
|
11
|
+
import "./token-goat-chunk-FQYMZASI.mjs";
|
|
12
|
+
import "./token-goat-chunk-7DGTVBWP.mjs";
|
|
13
|
+
import "./token-goat-chunk-S7RFIX4Y.mjs";
|
|
13
14
|
import "./token-goat-chunk-LKSXAMJB.mjs";
|
|
14
|
-
import "./token-goat-chunk-
|
|
15
|
-
import "./token-goat-chunk-
|
|
16
|
-
import "./token-goat-chunk-
|
|
17
|
-
import "./token-goat-chunk-
|
|
15
|
+
import "./token-goat-chunk-DHIXE6GV.mjs";
|
|
16
|
+
import "./token-goat-chunk-3W5P673V.mjs";
|
|
17
|
+
import "./token-goat-chunk-FJZ5MZK7.mjs";
|
|
18
|
+
import "./token-goat-chunk-KWM6KBPA.mjs";
|
|
18
19
|
import "./token-goat-chunk-3BTK54F3.mjs";
|
|
19
|
-
import "./token-goat-chunk-
|
|
20
|
+
import "./token-goat-chunk-BTKDZW7F.mjs";
|
|
20
21
|
import "./token-goat-chunk-XTQAOTSO.mjs";
|
|
21
22
|
import "./token-goat-chunk-AH6QILZM.mjs";
|
|
22
|
-
import "./token-goat-chunk-
|
|
23
|
-
import "./token-goat-chunk-
|
|
24
|
-
import "./token-goat-chunk-
|
|
25
|
-
import "./token-goat-chunk-
|
|
26
|
-
import "./token-goat-chunk-
|
|
23
|
+
import "./token-goat-chunk-NJKNAC37.mjs";
|
|
24
|
+
import "./token-goat-chunk-EP7TCWHM.mjs";
|
|
25
|
+
import "./token-goat-chunk-IBTFGZBX.mjs";
|
|
26
|
+
import "./token-goat-chunk-JNIFSVWM.mjs";
|
|
27
|
+
import "./token-goat-chunk-SOKAPOOC.mjs";
|
|
27
28
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
28
29
|
import "./token-goat-chunk-OSUFN2FV.mjs";
|
|
29
30
|
import {
|
|
30
31
|
installEpipeGuard
|
|
31
|
-
} from "./token-goat-chunk-
|
|
32
|
-
import "./token-goat-chunk-
|
|
32
|
+
} from "./token-goat-chunk-ZGQXRYKE.mjs";
|
|
33
|
+
import "./token-goat-chunk-NMTKNYGF.mjs";
|
|
33
34
|
import "./token-goat-chunk-Y4AFKTHK.mjs";
|
|
34
35
|
import "./token-goat-chunk-GMOUBOX4.mjs";
|
|
35
36
|
import "./token-goat-chunk-RRNZMM3A.mjs";
|
package/docs/cli.md
CHANGED
|
@@ -58,6 +58,7 @@ token-goat pdf-extract manual.pdf --pages 12-15 --layout --head 120
|
|
|
58
58
|
| `token-goat call-chain <symbol>` | Trace every caller layer from a symbol back to the entry points — one step deeper than `callers`. Use when you need to know what reaches a function across the whole call graph, not only who invokes it directly. Pairs with `impact` for the downstream direction. Accepts `file::symbol` to disambiguate WHICH same-named definition the chain starts from — the file only narrows which definition, callers can still be found in any file. `--exclude-tests` prunes callers whose call site is a test file BEFORE they're admitted to the traversal, so nothing walks through a test node either (opt-in — omitted, output is unchanged); when every caller was a test, the "no callers" line names how many were hidden instead of reading as genuinely unreferenced. `Symbol not found: <symbol>` for an unindexed name (bare or `file::symbol`) now carries a `Did you mean:` suggestion when a near-name candidate is indexed. `--grep <pattern>` keeps only completed chains containing a symbol name matching this regex (literal substring if it is not valid regex) — the BFS still walks the full graph, this only narrows which finished chains are reported, so a chain passing through a matching symbol on its way to an unrelated root still surfaces; when it matches none of the chains that do exist, the output names how many were filtered out rather than reading as genuinely caller-less. A chain the walk abandoned because it ran out of `--depth` ends in a `(depth-limit)` marker, so a truncated chain is never mistaken for one that reached a real entry point. |
|
|
59
59
|
| `token-goat impact <symbol>` | Walk the call-reference graph forward (breadth-first) and list every function that depends on a symbol, with hop depth; module-scope callers are surfaced as `(module scope) <file>` entries. Run before a refactor to size up the blast radius without starting a build. Accepts `file::symbol` to disambiguate WHICH same-named definition the walk starts from — the file only narrows which definition, callers can still be found in any file. `--exclude-tests` prunes callers (including module-scope entries) whose call site is a test file BEFORE they're enqueued for further traversal, so nothing walks through a test node either (opt-in — omitted, output is unchanged); when every caller was a test, the "no callers found" error names how many were hidden instead of reading as genuinely unreferenced. A bare name that isn't indexed at all reports `Symbol not found: <name>` (with a `Did you mean:` suggestion when a near-name candidate is indexed) instead of the misleading "no callers found", which is reserved for a real, indexed symbol that genuinely has zero impact. `--grep <pattern>` only shows impacted entries whose symbol name (or `(module scope) <file>` key) matches this regex (literal substring if it is not valid regex), the same filter `call-chain --grep`/`dead --grep` apply to their own results — applied BEFORE the `--top` slice, so it selects from the whole impacted set rather than an already-capped page; when it matches none of the impacted entries that do exist, the output names how many were filtered out instead of reading as genuinely impact-free. |
|
|
60
60
|
| `token-goat context-for <task>` | Takes a natural-language task description, runs semantic search across the indexed codebase, and emits a prioritized list of `token-goat read` commands trimmed to a token budget. Fetches only the relevant slices instead of loading entire files. `--budget N` sets the token ceiling; `--top N` limits the file count; `--json` for structured output. Every emitted command carries the `file::symbol@LINE` anchor, so a suggestion still runs when the same symbol name has more than one definition in its file; `--json` entries carry the matching `line` field. |
|
|
61
|
+
| `token-goat answer "<question>"` | Deterministic question router: classifies a plain-English question to one of six intents (where a symbol is, who calls it, what tests cover it, what a file exports, what a file imports, what breaks if a symbol changes), resolves the subject against the index, and delegates in-process to the command that already answers it. No model call, no inference, no fallback to a fuzzy match. Because the subject is looked up before anything runs, the router picks the right ARGUMENT FORM too: `answer "what tests cover foldPath"` resolves `foldPath` to `src/path_containment.ts` and runs `test-for` against the file, where passing a symbol to `test-for` directly fails. A bare basename resolves when exactly one file in the project carries it, and lists the candidates when several do. Every answer is preceded by `via: token-goat <command> <argument>` so the delegate can be verified or re-run with its own flags. Anything else refuses: `cannot answer deterministically: <why>; try: <command>`, exit 1. Questions of judgement, intent or runtime behaviour (`why does X...`, `is X correct`, `should I...`) refuse even when they name a real symbol. |
|
|
61
62
|
| `token-goat ask "<question>"` *(experimental)* | Retrieves relevant slices via full-text (BM25) search over the symbol index — not semantic/embedding search — and lists them as pointer-citations plus `token-goat read` commands. Set `TOKEN_GOAT_ASK_BACKEND=claude` or `TOKEN_GOAT_ASK_BACKEND=codex` to synthesize a short answer via that CLI (whatever model it defaults to; token-goat does not force Haiku or any particular tier); with the env var unset, or the named CLI missing from PATH, `ask` degrades to printing the retrieved pointers with no network call. `--top N` caps the number of FTS hits (default 8); `--json` for structured output. Answers are not cached — each call re-retrieves and re-synthesizes from scratch. Every emitted command carries the `file::symbol@LINE` anchor, so a suggestion still runs when the same symbol name has more than one definition in its file; `--json` entries carry the matching `line` field. |
|
|
62
63
|
| `token-goat changed [<ref>]` | List files (or `--symbol` for symbols) changed since a git ref, without reading the full diff. `<ref>` and `--since <ref>` are equivalent (default `HEAD~5`); `--since` wins if both are given. `--json` for structured output. `--grep <pattern>` only lists changed files whose path matches this regex (literal substring if it is not valid regex) — applied to the file list even in `--symbol` mode, before any downstream slicing; when it matches none of the files that did change, the output names the active filter instead of reading like nothing changed. `--exclude-tests` hides changed files that live in a test file (opt-in — omitted, output is unchanged), completing the flag family already on `refs`/`callers`/`dead`/`call-chain`/`impact`/`semantic`/`symbol`. `--grep` can only ever *select* a path, so there was no reliable way to ask for the non-test half of a diff: the negative-lookahead regex that expresses "not a test" silently degrades to a literal substring match whenever the regex-compile fallback fires. Test files are a large share of a typical diff — measured against this repo, 35–54% of changed files across the last 5, 10 and 20 commits. Like `--grep` it filters the file path, so it applies in `--symbol` mode too, and it prunes before the per-file index lookup rather than after, so a test file is never queried at all. Composable with `--grep` (a file must satisfy both; when both are active and `--grep` is what emptied the list, the `--grep` notice takes priority, and when `--grep` left only test files so that `--exclude-tests` emptied it, the message names both filters rather than claiming no non-test file changed). When it hides every changed file there was, the output names how many were hidden and exits 0, rather than a bare "No files changed." that would read as a clean diff. Every zero-row path emits the shared `{items, truncated, totalCount}` envelope under `--json`. |
|
|
63
64
|
| `token-goat diff "file::symbol" [range]` | Show only the git diff hunk(s) that fall within one symbol's line range, e.g. `token-goat diff "file.ts::myFn" HEAD~3..HEAD`, instead of the whole file's diff. Also accepts `read`'s `symbol@LINE` anchor to pick out an otherwise-ambiguous candidate. |
|
|
@@ -177,7 +178,7 @@ token-goat pdf-extract manual.pdf --pages 12-15 --layout --head 120
|
|
|
177
178
|
| `token-goat project prune [--dry-run]` | Remove blocked/excluded roots that no longer exist on disk, and index rows for files under the OS temp dir. `--dry-run` previews removals without touching the config file. Useful after deleting or moving projects. |
|
|
178
179
|
| `token-goat install` | Wire up hooks (and, with the harness flags below, other AI tool integrations). No `--dry-run` or `--verify` flag — run `token-goat doctor` after install to audit the result. |
|
|
179
180
|
| `token-goat upgrade` | Check for updates or upgrade token-goat to the latest version via npm. Pass `--check` to report status without installing. `--json` emits machine-readable version status. |
|
|
180
|
-
| `token-goat doctor` | Confirm everything is wired correctly. Surfaces install state, cold-import timing, cache hit rates, compaction-budget telemetry, opt-in flag status, and canonical-root sanity. A **Parser freshness** check reports how much of the index for this project was written by a different build of the extractor. The stamp only refreshes when something touches a file, so after an upgrade a project goes on answering `symbol`, `read`, `outline` and `skeleton` from the previous extractor with nothing saying so. It warns past a quarter and names the fix: a plain `token-goat index` in that project, which is enough on its own, since a parser mismatch reparses without `--force`. A **Tool names** check reports any tool name a harness sent that reached no handler wanting it, and calls out the ones that differ from a handled name only by capitalisation or punctuation — the signature of a bridge that forgot to rename something, which is otherwise invisible. A **Security** section reports the posture in one place: whether offline mode is on, whether injection scanning is on, whether the Google Drive integration is enabled, whether fetching runs against an allow list or a deny list, whether MCP reads are confined to the project root, and whether the data directory is readable by other local users. It only warns when a protection that ships on has been switched off, so a default install stays quiet. It read-only audits `~/.copilot/mcp-config.json` for globally configured Chrome DevTools or Playwright `npx` launchers, recommending project scope or removal when inactive; it never prints server configuration or secrets. On Windows, it also reports duplicate Chrome DevTools/Playwright MCP launchers and orphaned Node processes without terminating anything. Pass `--context` to show the **Context footprint** section: a fill bar with severity (ok / warn / high / URGENT), per-component breakdown (skills catalog, loaded skill bodies, CLAUDE.md+MEMORY.md, conversation estimate), session-to-session growth trend with sessions-to-URGENT projection, and tiered compaction recommendations (Tier 0–4) naming the exact commands to run. Auto-shown when fill > 40 % or any loaded skill > 2 K tokens lacks a compact. `--json` emits the check results (one entry per check, with `ok`/`warn`/`fail` status) as JSON instead of text. |
|
|
181
|
+
| `token-goat doctor` | Confirm everything is wired correctly. Surfaces install state, cold-import timing, cache hit rates, compaction-budget telemetry, opt-in flag status, and canonical-root sanity. A **Parser freshness** check reports how much of the index for this project was written by a different build of the extractor. The stamp only refreshes when something touches a file, so after an upgrade a project goes on answering `symbol`, `read`, `outline` and `skeleton` from the previous extractor with nothing saying so. It warns past a quarter and names the fix: a plain `token-goat index` in that project, which is enough on its own, since a parser mismatch reparses without `--force`. A **Tool names** check reports any tool name a harness sent that reached no handler wanting it, and calls out the ones that differ from a handled name only by capitalisation or punctuation — the signature of a bridge that forgot to rename something, which is otherwise invisible. When `global.db` passes 1 GB, the warning now also breaks the total down by category (symbol bodies, refs, chunk text, embedding vectors, stats detail) with each one's measured byte share and the exact command that shrinks it -- `reclaim-index --rebuild` for the derived-row categories, disabling `indexing.embeddings_enabled` first for vectors so they don't regrow, and nothing manual for stats detail, which ages out on its own. A **Security** section reports the posture in one place: whether offline mode is on, whether injection scanning is on, whether the Google Drive integration is enabled, whether fetching runs against an allow list or a deny list, whether MCP reads are confined to the project root, and whether the data directory is readable by other local users. It only warns when a protection that ships on has been switched off, so a default install stays quiet. It read-only audits `~/.copilot/mcp-config.json` for globally configured Chrome DevTools or Playwright `npx` launchers, recommending project scope or removal when inactive; it never prints server configuration or secrets. On Windows, it also reports duplicate Chrome DevTools/Playwright MCP launchers and orphaned Node processes without terminating anything. Pass `--context` to show the **Context footprint** section: a fill bar with severity (ok / warn / high / URGENT), per-component breakdown (skills catalog, loaded skill bodies, CLAUDE.md+MEMORY.md, conversation estimate), session-to-session growth trend with sessions-to-URGENT projection, and tiered compaction recommendations (Tier 0–4) naming the exact commands to run. Auto-shown when fill > 40 % or any loaded skill > 2 K tokens lacks a compact. `--json` emits the check results (one entry per check, with `ok`/`warn`/`fail` status) as JSON instead of text. |
|
|
181
182
|
| `token-goat capabilities` | List every capability that can send data off this machine or leave data on it, with whether it is currently on, the config key that decides that, and the exact `file::symbol` where the decision is made — so a reviewer can open the code rather than take the list's word for it. `--json` emits the same thing for a pipeline to assert on, which is the point: the answer comes from the binary installed on your machine, not from documentation that may describe a different build. A test in the suite fails the build when a module that can open a network connection is missing from this list, and equally when the list names one that no longer connects anywhere. |
|
|
182
183
|
| `token-goat baseline` | Emit a project map: file count, per-language file counts, the top indexed symbols (by name/kind/location), and the most recently modified files. `--subagent` emits a terser variant (fewer symbols, fewer recent files) for context handed to a freshly spawned subagent; `--json` for the machine-readable form. |
|
|
183
184
|
| `token-goat compact-doc <path>` | Build an extractive compact sidecar for a large reference doc (`.md`/`.markdown`). The compact is stored in the token-goat data dir as a SHA-keyed sidecar; `pre_read` serves it in place of the full file when it exists and is fresh, typically saving 60–95% of the tokens the full read would cost (measured across this repo's own 44 docs: median 67%, range 5–99%, depending on how much of the doc is prose under headings). Use `--force` to rebuild, `--sentences N` to control lines per section (default 2), `--show` to print the result. The sidecar is automatically marked stale when you edit the source file. Config: `[hints] stable_doc_compacts = true` (default on). |
|
package/docs/security.md
CHANGED
|
@@ -20,7 +20,7 @@ Outbound network is reserved to these explicit cases:
|
|
|
20
20
|
|
|
21
21
|
**One switch for all of it.** Set `network.offline = true` (env `TOKEN_GOAT_OFFLINE`) and every one of the paths above refuses instead of connecting, saying so rather than failing quietly. Anything already cached keeps working: a machine that has the embedding model still runs `semantic`, and one that has the language data still reads text out of images. This is one of the settings a per-project config file may not touch, so cloning a repository cannot switch it back off.
|
|
22
22
|
|
|
23
|
-
**A repository cannot reconfigure the security controls.** A project-root `.token-goat.toml` layers on top of your global config, which is what it is for: hint thresholds, indexing settings, compression tuning. But that file arrives with the repository, so whoever wrote the repository wrote it. Seven whole sections are therefore off limits to it, and come from your global config or the environment only: `injection` (prompt-injection fencing), `webfetch` (the fetch allow and deny lists), `gdrive` (the Google Drive integration), `mcp` (root confinement and the allowed-roots list), `network` (offline mode), `redaction` (the secret-redaction rules), and `screenshot` (the headless browser).
|
|
23
|
+
**A repository cannot reconfigure the security controls.** A project-root `.token-goat.toml` layers on top of your global config, which is what it is for: hint thresholds, indexing settings, compression tuning. But that file arrives with the repository, so whoever wrote the repository wrote it. Seven whole sections are therefore off limits to it, and come from your global config or the environment only: `injection` (prompt-injection fencing), `webfetch` (the fetch allow and deny lists), `gdrive` (the Google Drive integration), `mcp` (root confinement and the allowed-roots list), `network` (offline mode), `redaction` (the secret-redaction rules), and `screenshot` (the headless browser). Fourteen individual keys inside otherwise-overridable sections are locked the same way. Six decide what gets indexed at all: `indexing.cross_project_symbols`, `indexing.skip_dirs`, `indexing.skip_files`, `indexing.large_file_skip_kb`, `indexing.large_file_symbol_only_kb` and `indexing.max_chunks_per_file`, which matter because an unindexed file answers `symbol`, `read` and `semantic` in the same words a name that never existed does. Five more are the switches that decide whether a large file arrives folded rather than whole: `hints.fold_code_bodies`, `hints.fold_comment_blocks`, `hints.fold_prose_paragraphs`, `hints.outline_large_documents` and `hints.skeleton_large_sources`. The last three are `image_shrink.max_image_pixels`, the decompression-bomb cap, `worker.blocked_roots`, the folders you have kept out of the index, and `compact_assist.summary_budget_chars`, the length target token-goat hands to whoever writes your compaction summary, which a repository must not be able to set low enough to erase the session. A project file that sets one of them is ignored, and token-goat prints a line naming what it dropped. Everything else stays project-overridable.
|
|
24
24
|
|
|
25
25
|
**The lock covers the config file, not the environment.** These settings still read a `TOKEN_GOAT_*` environment variable, and a repository has ways to set one: a `.envrc` for direnv, a `terminal.integrated.env.*` block in a committed `.vscode/settings.json`, a `containerEnv` entry in a devcontainer. Refusing environment overrides would break the operator who exports a variable in their own shell, which is the legitimate case and the common one, so token-goat reports instead of refusing: `token-goat doctor` prints a `Security config overrides` line naming every locked security setting the environment is currently deciding, and the variable to unset. It covers all twenty locked keys that read an environment variable, and it derives that set from the same two tables that define what a project config may not write, rather than from a list kept alongside them. Settings with a safe side (booleans) are reported when the environment holds them open; settings without one (lists, sizes) are reported whenever the environment supplies a value at all, since there is nothing to compare against. A default install, where nothing is set, prints a single ok line.
|
|
26
26
|
|
package/package.json
CHANGED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
|
-
const require = __cjsRequire(import.meta.url);
|
|
3
|
-
import {
|
|
4
|
-
deliveredOutputBytes,
|
|
5
|
-
wrappedShell
|
|
6
|
-
} from "./token-goat-chunk-RHHFEXKF.mjs";
|
|
7
|
-
import {
|
|
8
|
-
ToolFilter,
|
|
9
|
-
capTokens,
|
|
10
|
-
compressedTokensSaved,
|
|
11
|
-
deliverCompressed,
|
|
12
|
-
dispatchArgv,
|
|
13
|
-
filterByName,
|
|
14
|
-
selectFilter,
|
|
15
|
-
shlexSplit,
|
|
16
|
-
storeBashOutputSync
|
|
17
|
-
} from "./token-goat-chunk-CTOYJ232.mjs";
|
|
18
|
-
import {
|
|
19
|
-
recordStat
|
|
20
|
-
} from "./token-goat-chunk-HPOFR6QX.mjs";
|
|
21
|
-
import "./token-goat-chunk-I67CPTDD.mjs";
|
|
22
|
-
import "./token-goat-chunk-L7VCX33I.mjs";
|
|
23
|
-
import {
|
|
24
|
-
loadConfig
|
|
25
|
-
} from "./token-goat-chunk-CA6V36KW.mjs";
|
|
26
|
-
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
27
|
-
import "./token-goat-chunk-OSUFN2FV.mjs";
|
|
28
|
-
import "./token-goat-chunk-QIDLJGXZ.mjs";
|
|
29
|
-
import "./token-goat-chunk-ERTXEKB6.mjs";
|
|
30
|
-
import "./token-goat-chunk-GMOUBOX4.mjs";
|
|
31
|
-
import {
|
|
32
|
-
init_define_import_meta_env
|
|
33
|
-
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
34
|
-
|
|
35
|
-
// src/bash_runner.ts
|
|
36
|
-
init_define_import_meta_env();
|
|
37
|
-
import { spawnSync } from "node:child_process";
|
|
38
|
-
import * as os from "node:os";
|
|
39
|
-
var IdentityFilter = class extends ToolFilter {
|
|
40
|
-
name = "passthrough";
|
|
41
|
-
};
|
|
42
|
-
var DEFAULT_TIMEOUT_SECONDS = 600;
|
|
43
|
-
var MAX_CAPTURE_BYTES = 32 * 1024 * 1024;
|
|
44
|
-
var MIN_RECORD_STAT_BYTES = 32;
|
|
45
|
-
function resolveFilter(command, filterName, cwd) {
|
|
46
|
-
let split;
|
|
47
|
-
try {
|
|
48
|
-
split = shlexSplit(command);
|
|
49
|
-
} catch {
|
|
50
|
-
split = null;
|
|
51
|
-
}
|
|
52
|
-
const argv = split === null ? [command] : dispatchArgv(split, cwd).argv;
|
|
53
|
-
if (filterName) {
|
|
54
|
-
const named = filterByName(filterName);
|
|
55
|
-
if (named !== null) return { filter: named, argv };
|
|
56
|
-
}
|
|
57
|
-
if (split === null) return { filter: null, argv };
|
|
58
|
-
return { filter: selectFilter(split, cwd), argv };
|
|
59
|
-
}
|
|
60
|
-
function baseSpawnOptions(timeout, cwd, env, nativeShell) {
|
|
61
|
-
return { shell: nativeShell ? true : wrappedShell(), timeout: timeout * 1e3, cwd, env };
|
|
62
|
-
}
|
|
63
|
-
function signalExitCode(signal) {
|
|
64
|
-
const num = os.constants.signals[signal];
|
|
65
|
-
return 128 + (num ?? 0);
|
|
66
|
-
}
|
|
67
|
-
function resolveProfile(explicit) {
|
|
68
|
-
if (explicit) return explicit;
|
|
69
|
-
try {
|
|
70
|
-
const p = loadConfig().compression.profile;
|
|
71
|
-
return p && p !== "auto" ? p : "balanced";
|
|
72
|
-
} catch {
|
|
73
|
-
return "balanced";
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
function resolveCompressLimits() {
|
|
77
|
-
try {
|
|
78
|
-
const bc = loadConfig().bash_compress;
|
|
79
|
-
return { maxLines: bc.max_lines, maxBytes: bc.max_bytes };
|
|
80
|
-
} catch {
|
|
81
|
-
return { maxLines: 1e3, maxBytes: 64 * 1024 };
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
function run(command, opts = {}) {
|
|
85
|
-
const timeout = opts.timeout ?? DEFAULT_TIMEOUT_SECONDS;
|
|
86
|
-
const { filter, argv } = resolveFilter(command, opts.filterName, opts.cwd);
|
|
87
|
-
if (filter === null) {
|
|
88
|
-
if ((opts.maxTokens ?? 0) > 0 || opts.quietSuccess) {
|
|
89
|
-
return wrapAndCompress(command, argv, new IdentityFilter(), timeout, resolveProfile(opts.compressionProfile), opts);
|
|
90
|
-
}
|
|
91
|
-
return passthrough(command, timeout, opts.cwd, opts.env, opts.nativeShell);
|
|
92
|
-
}
|
|
93
|
-
return wrapAndCompress(command, argv, filter, timeout, resolveProfile(opts.compressionProfile), opts);
|
|
94
|
-
}
|
|
95
|
-
function runRaw(command, timeout = DEFAULT_TIMEOUT_SECONDS) {
|
|
96
|
-
return passthrough(command, timeout, void 0, void 0);
|
|
97
|
-
}
|
|
98
|
-
function passthrough(command, timeout, cwd, env, nativeShell) {
|
|
99
|
-
const result = spawnSync(command, { ...baseSpawnOptions(timeout, cwd, env, nativeShell), stdio: "inherit" });
|
|
100
|
-
if (isTimeout(result.error)) return 124;
|
|
101
|
-
if (result.status !== null) return result.status;
|
|
102
|
-
if (result.signal) return signalExitCode(result.signal);
|
|
103
|
-
return 0;
|
|
104
|
-
}
|
|
105
|
-
function isTimeout(error) {
|
|
106
|
-
return !!error && error.code === "ETIMEDOUT";
|
|
107
|
-
}
|
|
108
|
-
function decode(buf) {
|
|
109
|
-
if (buf == null) return "";
|
|
110
|
-
return typeof buf === "string" ? buf : buf.toString("utf8");
|
|
111
|
-
}
|
|
112
|
-
function wrapAndCompress(command, argv, filter, timeout, profile, opts) {
|
|
113
|
-
const writeStdout = opts.writeStdout ?? ((s) => process.stdout.write(s));
|
|
114
|
-
const startTime = Date.now();
|
|
115
|
-
const result = spawnSync(command, {
|
|
116
|
-
...baseSpawnOptions(timeout, opts.cwd, opts.env, opts.nativeShell),
|
|
117
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
118
|
-
maxBuffer: MAX_CAPTURE_BYTES
|
|
119
|
-
});
|
|
120
|
-
const timedOut = isTimeout(result.error);
|
|
121
|
-
const overflowed = !!result.error && result.error.code === "ENOBUFS";
|
|
122
|
-
let stdoutText = decode(result.stdout);
|
|
123
|
-
let stderrText = decode(result.stderr);
|
|
124
|
-
if (overflowed) {
|
|
125
|
-
stdoutText += `
|
|
126
|
-
[token-goat: capture capped at ${MAX_CAPTURE_BYTES / (1024 * 1024)} MiB]`;
|
|
127
|
-
}
|
|
128
|
-
let exitCode;
|
|
129
|
-
if (timedOut) {
|
|
130
|
-
exitCode = 124;
|
|
131
|
-
stderrText = (stderrText ? stderrText + "\n" : "") + `[token-goat: command exceeded ${timeout}s timeout and was killed]`;
|
|
132
|
-
} else if (result.status !== null) {
|
|
133
|
-
exitCode = result.status;
|
|
134
|
-
} else if (result.signal) {
|
|
135
|
-
exitCode = signalExitCode(result.signal);
|
|
136
|
-
} else {
|
|
137
|
-
exitCode = 0;
|
|
138
|
-
}
|
|
139
|
-
if (opts.quietSuccess && exitCode === 0) {
|
|
140
|
-
const duration = ((Date.now() - startTime) / 1e3).toFixed(1) + "s";
|
|
141
|
-
const fullRaw = (stdoutText + (stderrText ? "\n" + stderrText : "")).trim();
|
|
142
|
-
const id = storeBashOutputSync(command, fullRaw, 0, opts.cwd ?? null);
|
|
143
|
-
const summary = `[tg: ok] ${command} (${duration}, recall: token-goat bash-output ${id})
|
|
144
|
-
`;
|
|
145
|
-
writeStdout(summary);
|
|
146
|
-
return 0;
|
|
147
|
-
}
|
|
148
|
-
const limits = resolveCompressLimits();
|
|
149
|
-
const { applied, marker, compressed, text: delivered } = deliverCompressed(filter, stdoutText, stderrText, exitCode, argv, {
|
|
150
|
-
compressionProfile: profile,
|
|
151
|
-
maxLines: limits.maxLines,
|
|
152
|
-
maxBytes: limits.maxBytes
|
|
153
|
-
});
|
|
154
|
-
let text = delivered;
|
|
155
|
-
const maxTokens = opts.maxTokens ?? 0;
|
|
156
|
-
if (maxTokens > 0) text = capTokens(text, maxTokens);
|
|
157
|
-
const body = text + marker;
|
|
158
|
-
writeStdout(body.endsWith("\n") ? body : body + "\n");
|
|
159
|
-
if (applied) recordSavings(compressed);
|
|
160
|
-
return exitCode;
|
|
161
|
-
}
|
|
162
|
-
function recordSavings(result) {
|
|
163
|
-
const delivered = deliveredOutputBytes(result.originalBytes);
|
|
164
|
-
const bytesSaved = Math.max(0, delivered - result.compressedBytes);
|
|
165
|
-
if (bytesSaved < MIN_RECORD_STAT_BYTES) return;
|
|
166
|
-
recordStat(`bash_compress:${result.filterName}`, bytesSaved, compressedTokensSaved(bytesSaved));
|
|
167
|
-
}
|
|
168
|
-
export {
|
|
169
|
-
DEFAULT_TIMEOUT_SECONDS,
|
|
170
|
-
MAX_CAPTURE_BYTES,
|
|
171
|
-
run,
|
|
172
|
-
runRaw
|
|
173
|
-
};
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
|
-
const require = __cjsRequire(import.meta.url);
|
|
3
|
-
import {
|
|
4
|
-
detectHarness
|
|
5
|
-
} from "./token-goat-chunk-CA6V36KW.mjs";
|
|
6
|
-
import {
|
|
7
|
-
init_define_import_meta_env
|
|
8
|
-
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
9
|
-
|
|
10
|
-
// src/delivery_cap.ts
|
|
11
|
-
init_define_import_meta_env();
|
|
12
|
-
var CLAUDE_CODE_BASH_OUTPUT_CAP_BYTES = 2e4;
|
|
13
|
-
function bashOutputCapBytes(harness = detectHarness()) {
|
|
14
|
-
return harness === "claudecode" ? CLAUDE_CODE_BASH_OUTPUT_CAP_BYTES : null;
|
|
15
|
-
}
|
|
16
|
-
function clipToDeliveryCap(text, reserveBytes) {
|
|
17
|
-
const cap = bashOutputCapBytes();
|
|
18
|
-
const room = cap === null ? null : cap - reserveBytes;
|
|
19
|
-
if (room === null || room <= 0 || Buffer.byteLength(text, "utf-8") <= room) return { text, clipped: false };
|
|
20
|
-
const sliced = Buffer.from(text, "utf-8").subarray(0, room).toString("utf-8");
|
|
21
|
-
const lastNewline = sliced.lastIndexOf("\n");
|
|
22
|
-
return { text: lastNewline > 0 ? sliced.slice(0, lastNewline) : sliced, clipped: true };
|
|
23
|
-
}
|
|
24
|
-
function deliveredOutputBytes(originalBytes, harness) {
|
|
25
|
-
const cap = bashOutputCapBytes(harness);
|
|
26
|
-
if (cap === null) return Math.max(0, originalBytes);
|
|
27
|
-
return Math.max(0, Math.min(originalBytes, cap));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// src/shell.ts
|
|
31
|
-
init_define_import_meta_env();
|
|
32
|
-
import * as fs from "node:fs";
|
|
33
|
-
import * as path from "node:path";
|
|
34
|
-
var WSL_LAUNCHER_SEGMENTS = /* @__PURE__ */ new Set(["system32", "syswow64", "windowsapps"]);
|
|
35
|
-
function isExecutable(p) {
|
|
36
|
-
try {
|
|
37
|
-
return fs.statSync(p).isFile();
|
|
38
|
-
} catch {
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
function isWslLauncherDir(dir) {
|
|
43
|
-
return dir.toLowerCase().split(/[\\/]+/).some((seg) => WSL_LAUNCHER_SEGMENTS.has(seg));
|
|
44
|
-
}
|
|
45
|
-
function locateBashOnPath(pathValue, isExe = isExecutable) {
|
|
46
|
-
for (const dir of (pathValue ?? "").split(path.delimiter)) {
|
|
47
|
-
if (!dir || isWslLauncherDir(dir)) continue;
|
|
48
|
-
const cand = path.join(dir, "bash.exe");
|
|
49
|
-
if (isExe(cand)) return cand;
|
|
50
|
-
}
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
function knownGitBashPaths() {
|
|
54
|
-
const localPrograms = process.env["LOCALAPPDATA"] ? path.join(process.env["LOCALAPPDATA"], "Programs") : void 0;
|
|
55
|
-
const bases = [
|
|
56
|
-
process.env["ProgramFiles"],
|
|
57
|
-
process.env["ProgramFiles(x86)"],
|
|
58
|
-
process.env["ProgramW6432"],
|
|
59
|
-
localPrograms
|
|
60
|
-
];
|
|
61
|
-
const out = [];
|
|
62
|
-
for (const base of bases) {
|
|
63
|
-
if (!base) continue;
|
|
64
|
-
out.push(path.join(base, "Git", "bin", "bash.exe"));
|
|
65
|
-
out.push(path.join(base, "Git", "usr", "bin", "bash.exe"));
|
|
66
|
-
}
|
|
67
|
-
return out;
|
|
68
|
-
}
|
|
69
|
-
function resolveWindowsBash() {
|
|
70
|
-
if (process.platform !== "win32") return null;
|
|
71
|
-
const override = process.env["TOKEN_GOAT_BASH"];
|
|
72
|
-
if (override && isExecutable(override)) return override;
|
|
73
|
-
const onPath = locateBashOnPath(process.env["PATH"]);
|
|
74
|
-
if (onPath) return onPath;
|
|
75
|
-
for (const cand of knownGitBashPaths()) {
|
|
76
|
-
if (isExecutable(cand)) return cand;
|
|
77
|
-
}
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
function wrappedShell() {
|
|
81
|
-
if (process.platform !== "win32") return true;
|
|
82
|
-
return resolveWindowsBash() ?? true;
|
|
83
|
-
}
|
|
84
|
-
function canRunWrappedShell() {
|
|
85
|
-
return process.platform !== "win32" || resolveWindowsBash() !== null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export {
|
|
89
|
-
clipToDeliveryCap,
|
|
90
|
-
deliveredOutputBytes,
|
|
91
|
-
wrappedShell,
|
|
92
|
-
canRunWrappedShell
|
|
93
|
-
};
|