token-goat 2.6.23 → 2.6.24
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 +3 -3
- package/dist/token-goat-hook.mjs +74 -58
- package/dist/token-goat.mjs +77 -61
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -403,11 +403,11 @@ This writes `.pi/extensions/token-goat.ts` in the current project only. Remove i
|
|
|
403
403
|
token-goat install --copilot
|
|
404
404
|
```
|
|
405
405
|
|
|
406
|
-
The `--copilot` flag patches Claude Code and registers a Copilot CLI hook config: `~/.copilot/hooks/token-goat.json` (a `{ version, hooks }` file registering `preToolUse`, `postToolUse`, `preCompact`, `agentStop`, `subagentStop`, and `userPromptSubmitted`, per Copilot's own [hooks reference](https://docs.github.com/en/copilot/reference/hooks-reference)) plus the shim script it points at, `~/.copilot/hooks/token-goat-shim.js`. Unlike Codex, Copilot's event names and response schema (`permissionDecision`/`modifiedArgs` for `preToolUse`, `modifiedResult`/`additionalContext` for `postToolUse`, `decision`/`reason` for `agentStop`/`subagentStop`) genuinely differ from Claude Code's, so the shim translates rather than passes through.
|
|
406
|
+
The `--copilot` flag patches Claude Code and registers a Copilot CLI hook config: `~/.copilot/hooks/token-goat.json` (a `{ version, hooks }` file registering `sessionStart`, `preToolUse`, `postToolUse`, `preCompact`, `agentStop`, `subagentStop`, and `userPromptSubmitted`, per Copilot's own [hooks reference](https://docs.github.com/en/copilot/reference/hooks-reference)) plus the shim script it points at, `~/.copilot/hooks/token-goat-shim.js`. Unlike Codex, Copilot's event names and response schema (`permissionDecision`/`modifiedArgs` for `preToolUse`, `modifiedResult`/`additionalContext` for `postToolUse`, `decision`/`reason` for `agentStop`/`subagentStop`) genuinely differ from Claude Code's, so the shim translates rather than passes through.
|
|
407
407
|
|
|
408
|
-
What works: **bash output compression and re-read denial** (`preToolUse` returns `modifiedArgs` or `permissionDecision: "deny"`), **image shrinking and post-edit indexing** (`postToolUse` returns `additionalContext`), and **stop-hallucination logging** (`agentStop`/`subagentStop` map a token-goat `deny` onto `decision: "block"`, everything else onto `decision: "allow"`). `preCompact` and `userPromptSubmitted` are notification-only on real Copilot CLI, per its docs: Copilot never reads a response body for either, so token-goat's compaction manifest and prompt-context hints have no surfacing channel there. The shim still calls through for both so token-goat's internal side effects keep running, but nothing gets injected back into the agent. Copilot's built-in tool names (`view`, `edit`, `create`, `bash`/`powershell`, `web_fetch`, `grep`, `glob`, `memory`, and MCP-server calls) are remapped onto token-goat's internal names where a clear match exists (`view`→Read, `edit`→Edit, `create`→Write, `bash`/`powershell`→Bash, `web_fetch`→WebFetch, `grep`→Grep, `glob`→Glob); `memory`, `task`, `ask_user`, and MCP tool calls pass through unmapped and simply no-op.
|
|
408
|
+
What works: **the command-routing reminder** (`sessionStart` returns `additionalContext`, so Copilot is told token-goat exists before it picks its first read tool — this is the one channel that lands ahead of that decision), **bash output compression and re-read denial** (`preToolUse` returns `modifiedArgs` or `permissionDecision: "deny"`), **image shrinking and post-edit indexing** (`postToolUse` returns `additionalContext`), and **stop-hallucination logging** (`agentStop`/`subagentStop` map a token-goat `deny` onto `decision: "block"`, everything else onto `decision: "allow"`). `preCompact` and `userPromptSubmitted` are notification-only on real Copilot CLI, per its docs: Copilot never reads a response body for either, so token-goat's compaction manifest and prompt-context hints have no surfacing channel there. The shim still calls through for both so token-goat's internal side effects keep running, but nothing gets injected back into the agent. Copilot's built-in tool names (`view`, `edit`, `create`, `bash`/`powershell`, `web_fetch`, `grep`, `glob`, `memory`, and MCP-server calls) are remapped onto token-goat's internal names where a clear match exists (`view`→Read, `edit`→Edit, `create`→Write, `bash`/`powershell`→Bash, `web_fetch`→WebFetch, `grep`→Grep, `glob`→Glob); `memory`, `task`, `ask_user`, and MCP tool calls pass through unmapped and simply no-op.
|
|
409
409
|
|
|
410
|
-
No ambient environment variable documents "this process is running under Copilot CLI" the way Codex/opencode set one, so the shim sets `TOKEN_GOAT_HARNESS_OVERRIDE=copilot_cli` itself before calling `token-goat hook` (same workaround `--pi` uses). Install also writes a token-goat routing block into `~/.copilot/copilot-instructions.md` (the same delimited-block gate written to `~/.claude/CLAUDE.md` and `~/.codex/AGENTS.md`), merged idempotently so any hand-written content outside the markers is preserved byte-for-byte. To install for one project instead of user scope: `token-goat install --copilot --local` (writes `.github/hooks/token-goat.json` and `.github/copilot-instructions.md` in the current project). To remove: `token-goat uninstall --copilot`.
|
|
410
|
+
No ambient environment variable documents "this process is running under Copilot CLI" the way Codex/opencode set one, so the shim sets `TOKEN_GOAT_HARNESS_OVERRIDE=copilot_cli` itself before calling `token-goat hook` (same workaround `--pi` uses). Install also writes a token-goat routing block into `~/.copilot/copilot-instructions.md` (the same delimited-block gate written to `~/.claude/CLAUDE.md` and `~/.codex/AGENTS.md`), merged idempotently so any hand-written content outside the markers is preserved byte-for-byte. If you set `COPILOT_HOME`, install follows it — hooks go to `$COPILOT_HOME/hooks/` and the routing block to `$COPILOT_HOME/copilot-instructions.md`, matching where Copilot CLI actually reads them. To install for one project instead of user scope: `token-goat install --copilot --local` (writes `.github/hooks/token-goat.json` and `.github/copilot-instructions.md` in the current project). To remove: `token-goat uninstall --copilot`.
|
|
411
411
|
|
|
412
412
|
**If Copilot CLI starts denying every tool call with `Denied by preToolUse hook ... (hook errored)`:** this is Copilot's own fail-closed behavior for a `preToolUse` hook that crashes, exits non-zero, or returns unparseable output -- it isn't limited to token-goat's own tool calls, since a fail-closed `preToolUse` hook blocks the whole session. Copilot caches hook configs at session start, so **renaming or reinstalling the hook mid-session has no effect** -- the only recovery is: run `token-goat install --copilot` (or `token-goat doctor`, which now checks the installed hook end-to-end and calls out a stale node-binary path from an nvm/fnm/volta upgrade specifically), then **fully restart Copilot CLI**.
|
|
413
413
|
|
package/dist/token-goat-hook.mjs
CHANGED
|
@@ -49,7 +49,7 @@ var init_define_import_meta_env = __esm({
|
|
|
49
49
|
import { createRequire } from "node:module";
|
|
50
50
|
function resolveVersion() {
|
|
51
51
|
if (true) {
|
|
52
|
-
return "2.6.
|
|
52
|
+
return "2.6.24";
|
|
53
53
|
}
|
|
54
54
|
const require2 = createRequire(import.meta.url);
|
|
55
55
|
const pkg = require2("../package.json");
|
|
@@ -1464,8 +1464,8 @@ function normalizeDarwinSystemAlias(p) {
|
|
|
1464
1464
|
}
|
|
1465
1465
|
function resolveIndexPath(file2, base = process.cwd()) {
|
|
1466
1466
|
const isWindowsAbsolute = (s) => /^[a-zA-Z]:[/\\]/.test(s);
|
|
1467
|
-
const
|
|
1468
|
-
return normalizePath(
|
|
1467
|
+
const resolve25 = isWindowsAbsolute(file2) || isWindowsAbsolute(base) ? path2.win32.resolve : path2.resolve;
|
|
1468
|
+
return normalizePath(resolve25(base, file2));
|
|
1469
1469
|
}
|
|
1470
1470
|
function toDisplayPath(root, target) {
|
|
1471
1471
|
if (root === void 0) return target;
|
|
@@ -2653,14 +2653,23 @@ const path = require('node:path')
|
|
|
2653
2653
|
const { pathToFileURL } = require('node:url')
|
|
2654
2654
|
|
|
2655
2655
|
// Copilot event name -> token-goat internal HookEventName (src/types.ts's
|
|
2656
|
-
// HOOK_EVENTS). Only these
|
|
2656
|
+
// HOOK_EVENTS). Only these seven have a token-goat handler; every other real
|
|
2657
2657
|
// Copilot event (sessionEnd, postToolUseFailure, subagentStart,
|
|
2658
2658
|
// errorOccurred, notification, permissionRequest) is left unimplemented
|
|
2659
2659
|
// rather than guessed at, and falls through to the default no-op below.
|
|
2660
|
-
// 'sessionStart'
|
|
2661
|
-
//
|
|
2662
|
-
//
|
|
2660
|
+
// 'sessionStart' was previously a permanent no-op on the stated grounds that
|
|
2661
|
+
// token-goat has no internal session_start handler. That was simply wrong --
|
|
2662
|
+
// hooks_session_start.ts has long emitted the command-routing reminder that
|
|
2663
|
+
// every other harness receives -- and the no-op was the reason Copilot CLI
|
|
2664
|
+
// sessions alone never got told token-goat exists. It is wired now: verified
|
|
2665
|
+
// against Copilot CLI 1.0.77 that a hooks.json sessionStart entry returning
|
|
2666
|
+
// {additionalContext} does reach the model. The github/copilot-cli#2142
|
|
2667
|
+
// fire-and-forget bug that would have made this dead wiring was fixed in a
|
|
2668
|
+
// pre-release months before that version, and its companion multi-extension
|
|
2669
|
+
// hook-overwrite bug never applied here: it hit runtime *extension* hooks,
|
|
2670
|
+
// while this config-file hooks.json path goes through Copilot's own merge.
|
|
2663
2671
|
const COPILOT_TO_TG_EVENT = {
|
|
2672
|
+
sessionStart: 'session_start',
|
|
2664
2673
|
preToolUse: 'pre_tool_use',
|
|
2665
2674
|
postToolUse: 'post_tool_use',
|
|
2666
2675
|
preCompact: 'pre_compact',
|
|
@@ -2772,11 +2781,6 @@ async function tryInProcess(entryPath, tgEvent, canonical) {
|
|
|
2772
2781
|
async function main() {
|
|
2773
2782
|
const copilotEvent = process.argv[2] || ''
|
|
2774
2783
|
|
|
2775
|
-
if (copilotEvent === 'sessionStart') {
|
|
2776
|
-
process.stdout.write('{}')
|
|
2777
|
-
return
|
|
2778
|
-
}
|
|
2779
|
-
|
|
2780
2784
|
const tgEvent = COPILOT_TO_TG_EVENT[copilotEvent]
|
|
2781
2785
|
if (!tgEvent) {
|
|
2782
2786
|
process.stdout.write('{}')
|
|
@@ -2902,7 +2906,10 @@ function translate(copilotEvent, resp) {
|
|
|
2902
2906
|
return {}
|
|
2903
2907
|
}
|
|
2904
2908
|
|
|
2905
|
-
if (copilotEvent === 'postToolUse') {
|
|
2909
|
+
if (copilotEvent === 'postToolUse' || copilotEvent === 'sessionStart') {
|
|
2910
|
+
// Both surface token-goat's context through the same field. sessionStart is
|
|
2911
|
+
// the one channel that reaches the model before it picks its first read
|
|
2912
|
+
// tool, so this is where the routing reminder has to land.
|
|
2906
2913
|
const context = extractContext(resp)
|
|
2907
2914
|
if (context) return { additionalContext: context }
|
|
2908
2915
|
return {}
|
|
@@ -2926,9 +2933,8 @@ function translate(copilotEvent, resp) {
|
|
|
2926
2933
|
// doc that both are notification-only -- Copilot never reads a response
|
|
2927
2934
|
// body for either, so any additionalContext/systemMessage token-goat
|
|
2928
2935
|
// produces has no surfacing channel here. This still routes through the
|
|
2929
|
-
// token-goat hook call above
|
|
2930
|
-
//
|
|
2931
|
-
// discarded.
|
|
2936
|
+
// token-goat hook call above so the internal handler's own side effects keep
|
|
2937
|
+
// running; only the response is discarded.
|
|
2932
2938
|
return {}
|
|
2933
2939
|
}
|
|
2934
2940
|
|
|
@@ -2968,8 +2974,13 @@ main()
|
|
|
2968
2974
|
import * as fs4 from "node:fs";
|
|
2969
2975
|
import * as os4 from "node:os";
|
|
2970
2976
|
import * as path6 from "node:path";
|
|
2977
|
+
function copilotCliUserRoot() {
|
|
2978
|
+
const override = process.env["COPILOT_HOME"];
|
|
2979
|
+
if (override !== void 0 && override.trim() !== "") return path6.resolve(override);
|
|
2980
|
+
return path6.join(os4.homedir(), ".copilot");
|
|
2981
|
+
}
|
|
2971
2982
|
function copilotCliUserHooksDir() {
|
|
2972
|
-
return path6.join(
|
|
2983
|
+
return path6.join(copilotCliUserRoot(), "hooks");
|
|
2973
2984
|
}
|
|
2974
2985
|
function copilotCliProjectHooksDir() {
|
|
2975
2986
|
return path6.join(process.cwd(), ".github", "hooks");
|
|
@@ -3074,6 +3085,7 @@ var init_copilot_cli_install = __esm({
|
|
|
3074
3085
|
init_copilot_cli();
|
|
3075
3086
|
init_guidance_block();
|
|
3076
3087
|
COPILOT_CLI_HOOK_EVENTS = [
|
|
3088
|
+
"sessionStart",
|
|
3077
3089
|
"preToolUse",
|
|
3078
3090
|
"postToolUse",
|
|
3079
3091
|
"preCompact",
|
|
@@ -11510,16 +11522,16 @@ var init_file_type_handler = __esm({
|
|
|
11510
11522
|
|
|
11511
11523
|
// src/skill_cache.ts
|
|
11512
11524
|
import * as fs16 from "fs/promises";
|
|
11513
|
-
import { resolve as
|
|
11525
|
+
import { resolve as resolve8 } from "path";
|
|
11514
11526
|
import { homedir as homedir7 } from "os";
|
|
11515
11527
|
import { readdirSync as readdirSync8, readFileSync as readFileSync12, existsSync as existsSync14, statSync as statSync7, unlinkSync as unlinkSync9 } from "node:fs";
|
|
11516
11528
|
function skillOutputsDir() {
|
|
11517
11529
|
if (_skillOutputsDirOverride) return _skillOutputsDirOverride;
|
|
11518
|
-
return
|
|
11530
|
+
return resolve8(dataDir(), SKILLS_OUTPUT_SUBDIR);
|
|
11519
11531
|
}
|
|
11520
11532
|
function skillsSourceDir() {
|
|
11521
11533
|
if (_skillsSourceDirOverride) return _skillsSourceDirOverride;
|
|
11522
|
-
return
|
|
11534
|
+
return resolve8(homedir7(), ".claude", "skills");
|
|
11523
11535
|
}
|
|
11524
11536
|
async function ensureSkillsDir() {
|
|
11525
11537
|
try {
|
|
@@ -11662,7 +11674,7 @@ async function listOutputs() {
|
|
|
11662
11674
|
continue;
|
|
11663
11675
|
}
|
|
11664
11676
|
try {
|
|
11665
|
-
const content = await fs16.readFile(
|
|
11677
|
+
const content = await fs16.readFile(resolve8(dir, entry.name), "utf-8");
|
|
11666
11678
|
const meta3 = JSON.parse(content);
|
|
11667
11679
|
metas.push(meta3);
|
|
11668
11680
|
} catch {
|
|
@@ -11696,7 +11708,7 @@ async function findCrossSessionEntry(skillName, contentSha) {
|
|
|
11696
11708
|
continue;
|
|
11697
11709
|
}
|
|
11698
11710
|
const dir = skillOutputsDir();
|
|
11699
|
-
const bodyPath =
|
|
11711
|
+
const bodyPath = resolve8(dir, `${meta3.outputId}.txt`);
|
|
11700
11712
|
try {
|
|
11701
11713
|
const bodyExists = await fs16.access(bodyPath).then(() => true).catch(() => false);
|
|
11702
11714
|
if (bodyExists) {
|
|
@@ -11751,7 +11763,7 @@ async function storeOutput(sessionId, skillName, body, opts) {
|
|
|
11751
11763
|
const truncBuf = buf.slice(truncStart);
|
|
11752
11764
|
storedBody = truncBuf.toString("utf-8");
|
|
11753
11765
|
}
|
|
11754
|
-
await atomicWriteText(
|
|
11766
|
+
await atomicWriteText(resolve8(dir, `${outId}.txt`), storedBody);
|
|
11755
11767
|
const meta3 = {
|
|
11756
11768
|
outputId: outId,
|
|
11757
11769
|
skillName: name2,
|
|
@@ -11761,7 +11773,7 @@ async function storeOutput(sessionId, skillName, body, opts) {
|
|
|
11761
11773
|
truncated,
|
|
11762
11774
|
sourcePath: opts?.sourcePath || ""
|
|
11763
11775
|
};
|
|
11764
|
-
await atomicWriteText(
|
|
11776
|
+
await atomicWriteText(resolve8(dir, `${outId}.meta`), JSON.stringify(meta3, null, 2));
|
|
11765
11777
|
pruneSkillOutputs();
|
|
11766
11778
|
return meta3;
|
|
11767
11779
|
} catch {
|
|
@@ -11781,7 +11793,7 @@ async function storeCompact(sessionId, skillName, compactText, sourceSha) {
|
|
|
11781
11793
|
text = `<!-- source_sha: ${sourceSha.slice(0, 12)} -->
|
|
11782
11794
|
${text}`;
|
|
11783
11795
|
}
|
|
11784
|
-
await atomicWriteText(
|
|
11796
|
+
await atomicWriteText(resolve8(dir, fileId), text);
|
|
11785
11797
|
} catch {
|
|
11786
11798
|
}
|
|
11787
11799
|
}
|
|
@@ -11801,7 +11813,7 @@ function getCompactAnySessionSync(skillName) {
|
|
|
11801
11813
|
for (const entry of entries) {
|
|
11802
11814
|
if (!matchesCompactSuffix(entry.name, entry.isFile(), suffix)) continue;
|
|
11803
11815
|
try {
|
|
11804
|
-
const text = readFileSync12(
|
|
11816
|
+
const text = readFileSync12(resolve8(dir, entry.name), "utf-8");
|
|
11805
11817
|
if (text.trim()) return text;
|
|
11806
11818
|
} catch {
|
|
11807
11819
|
continue;
|
|
@@ -11829,7 +11841,7 @@ async function readSkillHits(skillName) {
|
|
|
11829
11841
|
const name2 = safeSkillName(skillName);
|
|
11830
11842
|
if (!name2) return { count: 0, lastTs: 0 };
|
|
11831
11843
|
const dir = skillOutputsDir();
|
|
11832
|
-
const hitsFile =
|
|
11844
|
+
const hitsFile = resolve8(dir, `${sanitizeSkillId(name2)}.hits`);
|
|
11833
11845
|
const content = await fs16.readFile(hitsFile, "utf-8").catch(() => null);
|
|
11834
11846
|
if (content) {
|
|
11835
11847
|
const parsed = JSON.parse(content);
|
|
@@ -11879,7 +11891,7 @@ async function incrementSkillHit(skillName) {
|
|
|
11879
11891
|
if (!name2) return;
|
|
11880
11892
|
await ensureSkillsDir();
|
|
11881
11893
|
const dir = skillOutputsDir();
|
|
11882
|
-
const hitsFile =
|
|
11894
|
+
const hitsFile = resolve8(dir, `${sanitizeSkillId(name2)}.hits`);
|
|
11883
11895
|
const lockPath = `${hitsFile}.lock`;
|
|
11884
11896
|
await runExclusiveInProcess(lockPath, async () => {
|
|
11885
11897
|
const locked = await acquireSkillHitLock(lockPath);
|
|
@@ -11926,14 +11938,14 @@ async function listSkills(sessionId) {
|
|
|
11926
11938
|
let compactLen = 0;
|
|
11927
11939
|
let compactText = "";
|
|
11928
11940
|
try {
|
|
11929
|
-
const stat2 = await fs16.stat(
|
|
11941
|
+
const stat2 = await fs16.stat(resolve8(dir, compactFileId));
|
|
11930
11942
|
compactLen = stat2.size;
|
|
11931
|
-
compactText = await fs16.readFile(
|
|
11943
|
+
compactText = await fs16.readFile(resolve8(dir, compactFileId), "utf-8").catch(() => "");
|
|
11932
11944
|
} catch {
|
|
11933
11945
|
compactLen = 0;
|
|
11934
11946
|
}
|
|
11935
11947
|
const hasMarker = extractCompactFromMarker(
|
|
11936
|
-
await fs16.readFile(
|
|
11948
|
+
await fs16.readFile(resolve8(dir, `${meta3.outputId}.txt`), "utf-8").catch(() => "")
|
|
11937
11949
|
) !== null;
|
|
11938
11950
|
const compactStale = isCompactStale(compactText, meta3.skillName, meta3.contentSha);
|
|
11939
11951
|
const { count: hitCount } = await readSkillHits(meta3.skillName);
|
|
@@ -11969,7 +11981,7 @@ async function getSkillFilePath(skillName) {
|
|
|
11969
11981
|
}
|
|
11970
11982
|
}
|
|
11971
11983
|
async function resolvePluginSkillPath(pluginName, skillSlug) {
|
|
11972
|
-
const manifestPath = _pluginsManifestPathOverride ??
|
|
11984
|
+
const manifestPath = _pluginsManifestPathOverride ?? resolve8(homedir7(), ".claude", "plugins", "installed_plugins.json");
|
|
11973
11985
|
let raw;
|
|
11974
11986
|
try {
|
|
11975
11987
|
raw = await fs16.readFile(manifestPath, "utf8");
|
|
@@ -11992,7 +12004,7 @@ async function resolvePluginSkillPath(pluginName, skillSlug) {
|
|
|
11992
12004
|
if (typeof entry !== "object" || entry === null) continue;
|
|
11993
12005
|
const installPath = entry["installPath"];
|
|
11994
12006
|
if (typeof installPath !== "string" || installPath === "") continue;
|
|
11995
|
-
const diskPath =
|
|
12007
|
+
const diskPath = resolve8(installPath, "skills", skillSlug, "SKILL.md");
|
|
11996
12008
|
try {
|
|
11997
12009
|
await fs16.access(diskPath);
|
|
11998
12010
|
return diskPath;
|
|
@@ -12011,7 +12023,7 @@ async function installedSkillPath(skillName) {
|
|
|
12011
12023
|
const pluginPath = await resolvePluginSkillPath(name2.slice(0, colonIdx), name2.slice(colonIdx + 1));
|
|
12012
12024
|
if (pluginPath !== null) return pluginPath;
|
|
12013
12025
|
}
|
|
12014
|
-
const diskPath =
|
|
12026
|
+
const diskPath = resolve8(skillsSourceDir(), name2, "SKILL.md");
|
|
12015
12027
|
try {
|
|
12016
12028
|
await fs16.access(diskPath);
|
|
12017
12029
|
return diskPath;
|
|
@@ -12031,11 +12043,11 @@ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_
|
|
|
12031
12043
|
const outputId = file2.slice(0, -".meta".length);
|
|
12032
12044
|
let ts;
|
|
12033
12045
|
try {
|
|
12034
|
-
const parsed = JSON.parse(readFileSync12(
|
|
12046
|
+
const parsed = JSON.parse(readFileSync12(resolve8(dir, file2), "utf-8"));
|
|
12035
12047
|
ts = parsed.ts;
|
|
12036
12048
|
} catch {
|
|
12037
12049
|
try {
|
|
12038
|
-
ts = statSync7(
|
|
12050
|
+
ts = statSync7(resolve8(dir, file2)).mtimeMs;
|
|
12039
12051
|
} catch {
|
|
12040
12052
|
continue;
|
|
12041
12053
|
}
|
|
@@ -12045,7 +12057,7 @@ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_
|
|
|
12045
12057
|
const removeEntry = (outputId) => {
|
|
12046
12058
|
for (const ext2 of [".meta", ".txt"]) {
|
|
12047
12059
|
try {
|
|
12048
|
-
unlinkSync9(
|
|
12060
|
+
unlinkSync9(resolve8(dir, `${outputId}${ext2}`));
|
|
12049
12061
|
} catch {
|
|
12050
12062
|
}
|
|
12051
12063
|
}
|
|
@@ -12159,7 +12171,7 @@ async function ocrImage(input) {
|
|
|
12159
12171
|
if (_ocrUnavailableThisProcess) return null;
|
|
12160
12172
|
const entryPath = resolveTesseractEntry();
|
|
12161
12173
|
if (entryPath === null) return null;
|
|
12162
|
-
return new Promise((
|
|
12174
|
+
return new Promise((resolve25) => {
|
|
12163
12175
|
let settled = false;
|
|
12164
12176
|
let child;
|
|
12165
12177
|
try {
|
|
@@ -12168,7 +12180,7 @@ async function ocrImage(input) {
|
|
|
12168
12180
|
});
|
|
12169
12181
|
} catch {
|
|
12170
12182
|
_ocrUnavailableThisProcess = true;
|
|
12171
|
-
|
|
12183
|
+
resolve25(null);
|
|
12172
12184
|
return;
|
|
12173
12185
|
}
|
|
12174
12186
|
const chunks = [];
|
|
@@ -12181,7 +12193,7 @@ async function ocrImage(input) {
|
|
|
12181
12193
|
child.kill();
|
|
12182
12194
|
} catch {
|
|
12183
12195
|
}
|
|
12184
|
-
|
|
12196
|
+
resolve25(result);
|
|
12185
12197
|
};
|
|
12186
12198
|
const timer = setTimeout(() => finish(null, true), _ocrTimeoutMs);
|
|
12187
12199
|
child.stdout?.on("data", (c) => chunks.push(c));
|
|
@@ -13619,7 +13631,7 @@ function ensureTransformerLoaded() {
|
|
|
13619
13631
|
}
|
|
13620
13632
|
}
|
|
13621
13633
|
function sleep(ms) {
|
|
13622
|
-
return new Promise((
|
|
13634
|
+
return new Promise((resolve25) => setTimeout(resolve25, ms));
|
|
13623
13635
|
}
|
|
13624
13636
|
async function buildExtractorWithRetry(pipelineFn, modelName) {
|
|
13625
13637
|
let lastError;
|
|
@@ -23932,7 +23944,7 @@ import * as readline from "node:readline";
|
|
|
23932
23944
|
async function defaultConfirm(question) {
|
|
23933
23945
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
23934
23946
|
try {
|
|
23935
|
-
const answer = await new Promise((
|
|
23947
|
+
const answer = await new Promise((resolve25) => rl.question(question, resolve25));
|
|
23936
23948
|
return /^y(es)?$/i.test(answer.trim());
|
|
23937
23949
|
} finally {
|
|
23938
23950
|
rl.close();
|
|
@@ -48520,7 +48532,7 @@ init_util2();
|
|
|
48520
48532
|
init_disk_cache();
|
|
48521
48533
|
init_lang_patterns();
|
|
48522
48534
|
import { readdirSync as readdirSync11, readFileSync as readFileSync21, statSync as statSync15 } from "fs";
|
|
48523
|
-
import { resolve as
|
|
48535
|
+
import { resolve as resolve10 } from "path";
|
|
48524
48536
|
|
|
48525
48537
|
// src/recall_index.ts
|
|
48526
48538
|
init_define_import_meta_env();
|
|
@@ -48753,7 +48765,7 @@ function depLockfileFingerprintSync(cmd, cwd) {
|
|
|
48753
48765
|
if (!candidates) return null;
|
|
48754
48766
|
for (const lockfile of candidates) {
|
|
48755
48767
|
try {
|
|
48756
|
-
const content = readFileSync21(
|
|
48768
|
+
const content = readFileSync21(resolve10(cwd, lockfile));
|
|
48757
48769
|
return shortFingerprint(content);
|
|
48758
48770
|
} catch {
|
|
48759
48771
|
continue;
|
|
@@ -48906,7 +48918,7 @@ function extractFirstPathArg(cmd, cwd, fallback) {
|
|
|
48906
48918
|
const token2 = tokens[i];
|
|
48907
48919
|
if (!token2.startsWith("-")) {
|
|
48908
48920
|
if (!token2.startsWith("/")) {
|
|
48909
|
-
return
|
|
48921
|
+
return resolve10(cwd, token2);
|
|
48910
48922
|
}
|
|
48911
48923
|
return token2;
|
|
48912
48924
|
}
|
|
@@ -64250,15 +64262,19 @@ var BRIDGE_CAPABILITY_MATRIX = [
|
|
|
64250
64262
|
harness: "copilot_cli",
|
|
64251
64263
|
label: "Copilot CLI",
|
|
64252
64264
|
sourceFile: "src/bridges/copilot_cli_install.ts (COPILOT_CLI_HOOK_EVENTS), src/bridges/copilot_cli.ts (COPILOT_TO_TG_EVENT)",
|
|
64253
|
-
implemented: /* @__PURE__ */ new Set([
|
|
64265
|
+
implemented: /* @__PURE__ */ new Set([
|
|
64266
|
+
"session_start",
|
|
64267
|
+
"pre_tool_use",
|
|
64268
|
+
"post_tool_use",
|
|
64269
|
+
"pre_compact",
|
|
64270
|
+
"stop",
|
|
64271
|
+
"subagent_stop",
|
|
64272
|
+
"user_prompt_submit"
|
|
64273
|
+
]),
|
|
64254
64274
|
reasons: [
|
|
64255
64275
|
{
|
|
64256
64276
|
events: ["notification"],
|
|
64257
64277
|
reason: "Copilot CLI has a real 'notification' hook event, but copilot_cli.ts's COPILOT_TO_TG_EVENT deliberately leaves it (and sessionEnd/postToolUseFailure/subagentStart/errorOccurred/permissionRequest) unimplemented rather than guessed at"
|
|
64258
|
-
},
|
|
64259
|
-
{
|
|
64260
|
-
events: ["session_start"],
|
|
64261
|
-
reason: "COPILOT_TO_TG_EVENT has no session-start mapping wired yet -- left unimplemented rather than guessed at"
|
|
64262
64278
|
}
|
|
64263
64279
|
]
|
|
64264
64280
|
},
|
|
@@ -69571,7 +69587,7 @@ import { existsSync as existsSync33, readdirSync as readdirSync20, statSync as s
|
|
|
69571
69587
|
import * as http from "http";
|
|
69572
69588
|
import * as https from "https";
|
|
69573
69589
|
import { isIPv4, isIPv6 } from "net";
|
|
69574
|
-
import { resolve as
|
|
69590
|
+
import { resolve as resolve18, join as join41 } from "path";
|
|
69575
69591
|
import { URL as URL2 } from "url";
|
|
69576
69592
|
import { promisify } from "util";
|
|
69577
69593
|
import { lookup as dnsLookup } from "dns";
|
|
@@ -69743,7 +69759,7 @@ function cleanupStaleDownloads() {
|
|
|
69743
69759
|
const files = readdirSync20(cacheDir);
|
|
69744
69760
|
for (const file2 of files) {
|
|
69745
69761
|
if (file2.endsWith(".tmp")) {
|
|
69746
|
-
const filePath =
|
|
69762
|
+
const filePath = resolve18(cacheDir, file2);
|
|
69747
69763
|
try {
|
|
69748
69764
|
const stat2 = statSync26(filePath);
|
|
69749
69765
|
if (Date.now() - stat2.mtimeMs < STALE_DOWNLOAD_AGE_MS) continue;
|
|
@@ -72320,8 +72336,8 @@ async function cmdMcpServe() {
|
|
|
72320
72336
|
const server = createMcpServer2();
|
|
72321
72337
|
const transport = new StdioServerTransport();
|
|
72322
72338
|
await server.connect(transport);
|
|
72323
|
-
await new Promise((
|
|
72324
|
-
server.server.onclose =
|
|
72339
|
+
await new Promise((resolve25) => {
|
|
72340
|
+
server.server.onclose = resolve25;
|
|
72325
72341
|
});
|
|
72326
72342
|
}
|
|
72327
72343
|
async function cmdHook(event, opts) {
|
|
@@ -73533,7 +73549,7 @@ function cmdWriteFile(dest, opts) {
|
|
|
73533
73549
|
throw new CliError(`TOKEN_GOAT_MAX_STDIN_MB must be a positive integer; got '${process.env["TOKEN_GOAT_MAX_STDIN_MB"] ?? ""}'`);
|
|
73534
73550
|
}
|
|
73535
73551
|
const maxBytes = maxMB * 1024 * 1024;
|
|
73536
|
-
return new Promise((
|
|
73552
|
+
return new Promise((resolve25, reject) => {
|
|
73537
73553
|
const chunks = [];
|
|
73538
73554
|
let totalBytes = 0;
|
|
73539
73555
|
let settled = false;
|
|
@@ -73557,7 +73573,7 @@ function cmdWriteFile(dest, opts) {
|
|
|
73557
73573
|
try {
|
|
73558
73574
|
atomicWriteBuffer(dest, Buffer.concat(chunks));
|
|
73559
73575
|
enqueueDirtyPathSafe(dest);
|
|
73560
|
-
|
|
73576
|
+
resolve25();
|
|
73561
73577
|
} catch (e) {
|
|
73562
73578
|
try {
|
|
73563
73579
|
mapFsError(e, void 0, dest);
|
|
@@ -82989,7 +83005,7 @@ init_image_shrink();
|
|
|
82989
83005
|
var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
|
|
82990
83006
|
var MAX_STDIN_BYTES = 64 * 1024 * 1024;
|
|
82991
83007
|
function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDIN_BYTES) {
|
|
82992
|
-
return new Promise((
|
|
83008
|
+
return new Promise((resolve25, reject) => {
|
|
82993
83009
|
const chunks = [];
|
|
82994
83010
|
let totalBytes = 0;
|
|
82995
83011
|
let settled = false;
|
|
@@ -83024,7 +83040,7 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
|
|
|
83024
83040
|
return;
|
|
83025
83041
|
}
|
|
83026
83042
|
try {
|
|
83027
|
-
|
|
83043
|
+
resolve25(JSON.parse(text));
|
|
83028
83044
|
} catch (err2) {
|
|
83029
83045
|
reject(err2 instanceof Error ? err2 : new Error(String(err2)));
|
|
83030
83046
|
}
|
package/dist/token-goat.mjs
CHANGED
|
@@ -3050,7 +3050,7 @@ var require_commander = __commonJS({
|
|
|
3050
3050
|
import { createRequire } from "node:module";
|
|
3051
3051
|
function resolveVersion() {
|
|
3052
3052
|
if (true) {
|
|
3053
|
-
return "2.6.
|
|
3053
|
+
return "2.6.24";
|
|
3054
3054
|
}
|
|
3055
3055
|
const require2 = createRequire(import.meta.url);
|
|
3056
3056
|
const pkg = require2("../package.json");
|
|
@@ -4283,8 +4283,8 @@ function normalizeDarwinSystemAlias(p) {
|
|
|
4283
4283
|
}
|
|
4284
4284
|
function resolveIndexPath(file2, base = process.cwd()) {
|
|
4285
4285
|
const isWindowsAbsolute = (s) => /^[a-zA-Z]:[/\\]/.test(s);
|
|
4286
|
-
const
|
|
4287
|
-
return normalizePath(
|
|
4286
|
+
const resolve25 = isWindowsAbsolute(file2) || isWindowsAbsolute(base) ? path2.win32.resolve : path2.resolve;
|
|
4287
|
+
return normalizePath(resolve25(base, file2));
|
|
4288
4288
|
}
|
|
4289
4289
|
function toDisplayPath(root, target) {
|
|
4290
4290
|
if (root === void 0) return target;
|
|
@@ -12084,14 +12084,23 @@ const path = require('node:path')
|
|
|
12084
12084
|
const { pathToFileURL } = require('node:url')
|
|
12085
12085
|
|
|
12086
12086
|
// Copilot event name -> token-goat internal HookEventName (src/types.ts's
|
|
12087
|
-
// HOOK_EVENTS). Only these
|
|
12087
|
+
// HOOK_EVENTS). Only these seven have a token-goat handler; every other real
|
|
12088
12088
|
// Copilot event (sessionEnd, postToolUseFailure, subagentStart,
|
|
12089
12089
|
// errorOccurred, notification, permissionRequest) is left unimplemented
|
|
12090
12090
|
// rather than guessed at, and falls through to the default no-op below.
|
|
12091
|
-
// 'sessionStart'
|
|
12092
|
-
//
|
|
12093
|
-
//
|
|
12091
|
+
// 'sessionStart' was previously a permanent no-op on the stated grounds that
|
|
12092
|
+
// token-goat has no internal session_start handler. That was simply wrong --
|
|
12093
|
+
// hooks_session_start.ts has long emitted the command-routing reminder that
|
|
12094
|
+
// every other harness receives -- and the no-op was the reason Copilot CLI
|
|
12095
|
+
// sessions alone never got told token-goat exists. It is wired now: verified
|
|
12096
|
+
// against Copilot CLI 1.0.77 that a hooks.json sessionStart entry returning
|
|
12097
|
+
// {additionalContext} does reach the model. The github/copilot-cli#2142
|
|
12098
|
+
// fire-and-forget bug that would have made this dead wiring was fixed in a
|
|
12099
|
+
// pre-release months before that version, and its companion multi-extension
|
|
12100
|
+
// hook-overwrite bug never applied here: it hit runtime *extension* hooks,
|
|
12101
|
+
// while this config-file hooks.json path goes through Copilot's own merge.
|
|
12094
12102
|
const COPILOT_TO_TG_EVENT = {
|
|
12103
|
+
sessionStart: 'session_start',
|
|
12095
12104
|
preToolUse: 'pre_tool_use',
|
|
12096
12105
|
postToolUse: 'post_tool_use',
|
|
12097
12106
|
preCompact: 'pre_compact',
|
|
@@ -12203,11 +12212,6 @@ async function tryInProcess(entryPath, tgEvent, canonical) {
|
|
|
12203
12212
|
async function main() {
|
|
12204
12213
|
const copilotEvent = process.argv[2] || ''
|
|
12205
12214
|
|
|
12206
|
-
if (copilotEvent === 'sessionStart') {
|
|
12207
|
-
process.stdout.write('{}')
|
|
12208
|
-
return
|
|
12209
|
-
}
|
|
12210
|
-
|
|
12211
12215
|
const tgEvent = COPILOT_TO_TG_EVENT[copilotEvent]
|
|
12212
12216
|
if (!tgEvent) {
|
|
12213
12217
|
process.stdout.write('{}')
|
|
@@ -12333,7 +12337,10 @@ function translate(copilotEvent, resp) {
|
|
|
12333
12337
|
return {}
|
|
12334
12338
|
}
|
|
12335
12339
|
|
|
12336
|
-
if (copilotEvent === 'postToolUse') {
|
|
12340
|
+
if (copilotEvent === 'postToolUse' || copilotEvent === 'sessionStart') {
|
|
12341
|
+
// Both surface token-goat's context through the same field. sessionStart is
|
|
12342
|
+
// the one channel that reaches the model before it picks its first read
|
|
12343
|
+
// tool, so this is where the routing reminder has to land.
|
|
12337
12344
|
const context = extractContext(resp)
|
|
12338
12345
|
if (context) return { additionalContext: context }
|
|
12339
12346
|
return {}
|
|
@@ -12357,9 +12364,8 @@ function translate(copilotEvent, resp) {
|
|
|
12357
12364
|
// doc that both are notification-only -- Copilot never reads a response
|
|
12358
12365
|
// body for either, so any additionalContext/systemMessage token-goat
|
|
12359
12366
|
// produces has no surfacing channel here. This still routes through the
|
|
12360
|
-
// token-goat hook call above
|
|
12361
|
-
//
|
|
12362
|
-
// discarded.
|
|
12367
|
+
// token-goat hook call above so the internal handler's own side effects keep
|
|
12368
|
+
// running; only the response is discarded.
|
|
12363
12369
|
return {}
|
|
12364
12370
|
}
|
|
12365
12371
|
|
|
@@ -12399,8 +12405,13 @@ main()
|
|
|
12399
12405
|
import * as fs11 from "node:fs";
|
|
12400
12406
|
import * as os5 from "node:os";
|
|
12401
12407
|
import * as path11 from "node:path";
|
|
12408
|
+
function copilotCliUserRoot() {
|
|
12409
|
+
const override = process.env["COPILOT_HOME"];
|
|
12410
|
+
if (override !== void 0 && override.trim() !== "") return path11.resolve(override);
|
|
12411
|
+
return path11.join(os5.homedir(), ".copilot");
|
|
12412
|
+
}
|
|
12402
12413
|
function copilotCliUserHooksDir() {
|
|
12403
|
-
return path11.join(
|
|
12414
|
+
return path11.join(copilotCliUserRoot(), "hooks");
|
|
12404
12415
|
}
|
|
12405
12416
|
function copilotCliProjectHooksDir() {
|
|
12406
12417
|
return path11.join(process.cwd(), ".github", "hooks");
|
|
@@ -12505,6 +12516,7 @@ var init_copilot_cli_install = __esm({
|
|
|
12505
12516
|
init_copilot_cli();
|
|
12506
12517
|
init_guidance_block();
|
|
12507
12518
|
COPILOT_CLI_HOOK_EVENTS = [
|
|
12519
|
+
"sessionStart",
|
|
12508
12520
|
"preToolUse",
|
|
12509
12521
|
"postToolUse",
|
|
12510
12522
|
"preCompact",
|
|
@@ -15646,16 +15658,16 @@ var init_file_type_handler = __esm({
|
|
|
15646
15658
|
|
|
15647
15659
|
// src/skill_cache.ts
|
|
15648
15660
|
import * as fs18 from "fs/promises";
|
|
15649
|
-
import { resolve as
|
|
15661
|
+
import { resolve as resolve8 } from "path";
|
|
15650
15662
|
import { homedir as homedir7 } from "os";
|
|
15651
15663
|
import { readdirSync as readdirSync8, readFileSync as readFileSync13, existsSync as existsSync14, statSync as statSync8, unlinkSync as unlinkSync9 } from "node:fs";
|
|
15652
15664
|
function skillOutputsDir() {
|
|
15653
15665
|
if (_skillOutputsDirOverride) return _skillOutputsDirOverride;
|
|
15654
|
-
return
|
|
15666
|
+
return resolve8(dataDir(), SKILLS_OUTPUT_SUBDIR);
|
|
15655
15667
|
}
|
|
15656
15668
|
function skillsSourceDir() {
|
|
15657
15669
|
if (_skillsSourceDirOverride) return _skillsSourceDirOverride;
|
|
15658
|
-
return
|
|
15670
|
+
return resolve8(homedir7(), ".claude", "skills");
|
|
15659
15671
|
}
|
|
15660
15672
|
async function ensureSkillsDir() {
|
|
15661
15673
|
try {
|
|
@@ -15798,7 +15810,7 @@ async function listOutputs() {
|
|
|
15798
15810
|
continue;
|
|
15799
15811
|
}
|
|
15800
15812
|
try {
|
|
15801
|
-
const content = await fs18.readFile(
|
|
15813
|
+
const content = await fs18.readFile(resolve8(dir, entry.name), "utf-8");
|
|
15802
15814
|
const meta3 = JSON.parse(content);
|
|
15803
15815
|
metas.push(meta3);
|
|
15804
15816
|
} catch {
|
|
@@ -15832,7 +15844,7 @@ async function findCrossSessionEntry(skillName, contentSha) {
|
|
|
15832
15844
|
continue;
|
|
15833
15845
|
}
|
|
15834
15846
|
const dir = skillOutputsDir();
|
|
15835
|
-
const bodyPath =
|
|
15847
|
+
const bodyPath = resolve8(dir, `${meta3.outputId}.txt`);
|
|
15836
15848
|
try {
|
|
15837
15849
|
const bodyExists = await fs18.access(bodyPath).then(() => true).catch(() => false);
|
|
15838
15850
|
if (bodyExists) {
|
|
@@ -15887,7 +15899,7 @@ async function storeOutput(sessionId, skillName, body, opts) {
|
|
|
15887
15899
|
const truncBuf = buf.slice(truncStart);
|
|
15888
15900
|
storedBody = truncBuf.toString("utf-8");
|
|
15889
15901
|
}
|
|
15890
|
-
await atomicWriteText(
|
|
15902
|
+
await atomicWriteText(resolve8(dir, `${outId}.txt`), storedBody);
|
|
15891
15903
|
const meta3 = {
|
|
15892
15904
|
outputId: outId,
|
|
15893
15905
|
skillName: name2,
|
|
@@ -15897,7 +15909,7 @@ async function storeOutput(sessionId, skillName, body, opts) {
|
|
|
15897
15909
|
truncated,
|
|
15898
15910
|
sourcePath: opts?.sourcePath || ""
|
|
15899
15911
|
};
|
|
15900
|
-
await atomicWriteText(
|
|
15912
|
+
await atomicWriteText(resolve8(dir, `${outId}.meta`), JSON.stringify(meta3, null, 2));
|
|
15901
15913
|
pruneSkillOutputs();
|
|
15902
15914
|
return meta3;
|
|
15903
15915
|
} catch {
|
|
@@ -15917,7 +15929,7 @@ async function storeCompact(sessionId, skillName, compactText, sourceSha) {
|
|
|
15917
15929
|
text = `<!-- source_sha: ${sourceSha.slice(0, 12)} -->
|
|
15918
15930
|
${text}`;
|
|
15919
15931
|
}
|
|
15920
|
-
await atomicWriteText(
|
|
15932
|
+
await atomicWriteText(resolve8(dir, fileId), text);
|
|
15921
15933
|
} catch {
|
|
15922
15934
|
}
|
|
15923
15935
|
}
|
|
@@ -15937,7 +15949,7 @@ function getCompactAnySessionSync(skillName) {
|
|
|
15937
15949
|
for (const entry of entries) {
|
|
15938
15950
|
if (!matchesCompactSuffix(entry.name, entry.isFile(), suffix)) continue;
|
|
15939
15951
|
try {
|
|
15940
|
-
const text = readFileSync13(
|
|
15952
|
+
const text = readFileSync13(resolve8(dir, entry.name), "utf-8");
|
|
15941
15953
|
if (text.trim()) return text;
|
|
15942
15954
|
} catch {
|
|
15943
15955
|
continue;
|
|
@@ -15965,7 +15977,7 @@ async function readSkillHits(skillName) {
|
|
|
15965
15977
|
const name2 = safeSkillName(skillName);
|
|
15966
15978
|
if (!name2) return { count: 0, lastTs: 0 };
|
|
15967
15979
|
const dir = skillOutputsDir();
|
|
15968
|
-
const hitsFile =
|
|
15980
|
+
const hitsFile = resolve8(dir, `${sanitizeSkillId(name2)}.hits`);
|
|
15969
15981
|
const content = await fs18.readFile(hitsFile, "utf-8").catch(() => null);
|
|
15970
15982
|
if (content) {
|
|
15971
15983
|
const parsed = JSON.parse(content);
|
|
@@ -16015,7 +16027,7 @@ async function incrementSkillHit(skillName) {
|
|
|
16015
16027
|
if (!name2) return;
|
|
16016
16028
|
await ensureSkillsDir();
|
|
16017
16029
|
const dir = skillOutputsDir();
|
|
16018
|
-
const hitsFile =
|
|
16030
|
+
const hitsFile = resolve8(dir, `${sanitizeSkillId(name2)}.hits`);
|
|
16019
16031
|
const lockPath = `${hitsFile}.lock`;
|
|
16020
16032
|
await runExclusiveInProcess(lockPath, async () => {
|
|
16021
16033
|
const locked = await acquireSkillHitLock(lockPath);
|
|
@@ -16062,14 +16074,14 @@ async function listSkills(sessionId) {
|
|
|
16062
16074
|
let compactLen = 0;
|
|
16063
16075
|
let compactText = "";
|
|
16064
16076
|
try {
|
|
16065
|
-
const stat2 = await fs18.stat(
|
|
16077
|
+
const stat2 = await fs18.stat(resolve8(dir, compactFileId));
|
|
16066
16078
|
compactLen = stat2.size;
|
|
16067
|
-
compactText = await fs18.readFile(
|
|
16079
|
+
compactText = await fs18.readFile(resolve8(dir, compactFileId), "utf-8").catch(() => "");
|
|
16068
16080
|
} catch {
|
|
16069
16081
|
compactLen = 0;
|
|
16070
16082
|
}
|
|
16071
16083
|
const hasMarker = extractCompactFromMarker(
|
|
16072
|
-
await fs18.readFile(
|
|
16084
|
+
await fs18.readFile(resolve8(dir, `${meta3.outputId}.txt`), "utf-8").catch(() => "")
|
|
16073
16085
|
) !== null;
|
|
16074
16086
|
const compactStale = isCompactStale(compactText, meta3.skillName, meta3.contentSha);
|
|
16075
16087
|
const { count: hitCount } = await readSkillHits(meta3.skillName);
|
|
@@ -16105,7 +16117,7 @@ async function getSkillFilePath(skillName) {
|
|
|
16105
16117
|
}
|
|
16106
16118
|
}
|
|
16107
16119
|
async function resolvePluginSkillPath(pluginName, skillSlug) {
|
|
16108
|
-
const manifestPath = _pluginsManifestPathOverride ??
|
|
16120
|
+
const manifestPath = _pluginsManifestPathOverride ?? resolve8(homedir7(), ".claude", "plugins", "installed_plugins.json");
|
|
16109
16121
|
let raw;
|
|
16110
16122
|
try {
|
|
16111
16123
|
raw = await fs18.readFile(manifestPath, "utf8");
|
|
@@ -16128,7 +16140,7 @@ async function resolvePluginSkillPath(pluginName, skillSlug) {
|
|
|
16128
16140
|
if (typeof entry !== "object" || entry === null) continue;
|
|
16129
16141
|
const installPath = entry["installPath"];
|
|
16130
16142
|
if (typeof installPath !== "string" || installPath === "") continue;
|
|
16131
|
-
const diskPath =
|
|
16143
|
+
const diskPath = resolve8(installPath, "skills", skillSlug, "SKILL.md");
|
|
16132
16144
|
try {
|
|
16133
16145
|
await fs18.access(diskPath);
|
|
16134
16146
|
return diskPath;
|
|
@@ -16147,7 +16159,7 @@ async function installedSkillPath(skillName) {
|
|
|
16147
16159
|
const pluginPath = await resolvePluginSkillPath(name2.slice(0, colonIdx), name2.slice(colonIdx + 1));
|
|
16148
16160
|
if (pluginPath !== null) return pluginPath;
|
|
16149
16161
|
}
|
|
16150
|
-
const diskPath =
|
|
16162
|
+
const diskPath = resolve8(skillsSourceDir(), name2, "SKILL.md");
|
|
16151
16163
|
try {
|
|
16152
16164
|
await fs18.access(diskPath);
|
|
16153
16165
|
return diskPath;
|
|
@@ -16167,11 +16179,11 @@ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_
|
|
|
16167
16179
|
const outputId = file2.slice(0, -".meta".length);
|
|
16168
16180
|
let ts;
|
|
16169
16181
|
try {
|
|
16170
|
-
const parsed = JSON.parse(readFileSync13(
|
|
16182
|
+
const parsed = JSON.parse(readFileSync13(resolve8(dir, file2), "utf-8"));
|
|
16171
16183
|
ts = parsed.ts;
|
|
16172
16184
|
} catch {
|
|
16173
16185
|
try {
|
|
16174
|
-
ts = statSync8(
|
|
16186
|
+
ts = statSync8(resolve8(dir, file2)).mtimeMs;
|
|
16175
16187
|
} catch {
|
|
16176
16188
|
continue;
|
|
16177
16189
|
}
|
|
@@ -16181,7 +16193,7 @@ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_
|
|
|
16181
16193
|
const removeEntry = (outputId) => {
|
|
16182
16194
|
for (const ext2 of [".meta", ".txt"]) {
|
|
16183
16195
|
try {
|
|
16184
|
-
unlinkSync9(
|
|
16196
|
+
unlinkSync9(resolve8(dir, `${outputId}${ext2}`));
|
|
16185
16197
|
} catch {
|
|
16186
16198
|
}
|
|
16187
16199
|
}
|
|
@@ -16273,7 +16285,7 @@ async function ocrImage(input) {
|
|
|
16273
16285
|
if (_ocrUnavailableThisProcess) return null;
|
|
16274
16286
|
const entryPath = resolveTesseractEntry();
|
|
16275
16287
|
if (entryPath === null) return null;
|
|
16276
|
-
return new Promise((
|
|
16288
|
+
return new Promise((resolve25) => {
|
|
16277
16289
|
let settled = false;
|
|
16278
16290
|
let child;
|
|
16279
16291
|
try {
|
|
@@ -16282,7 +16294,7 @@ async function ocrImage(input) {
|
|
|
16282
16294
|
});
|
|
16283
16295
|
} catch {
|
|
16284
16296
|
_ocrUnavailableThisProcess = true;
|
|
16285
|
-
|
|
16297
|
+
resolve25(null);
|
|
16286
16298
|
return;
|
|
16287
16299
|
}
|
|
16288
16300
|
const chunks = [];
|
|
@@ -16295,7 +16307,7 @@ async function ocrImage(input) {
|
|
|
16295
16307
|
child.kill();
|
|
16296
16308
|
} catch {
|
|
16297
16309
|
}
|
|
16298
|
-
|
|
16310
|
+
resolve25(result);
|
|
16299
16311
|
};
|
|
16300
16312
|
const timer = setTimeout(() => finish(null, true), _ocrTimeoutMs);
|
|
16301
16313
|
child.stdout?.on("data", (c) => chunks.push(c));
|
|
@@ -17537,7 +17549,7 @@ import * as readline from "node:readline";
|
|
|
17537
17549
|
async function defaultConfirm(question) {
|
|
17538
17550
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
17539
17551
|
try {
|
|
17540
|
-
const answer = await new Promise((
|
|
17552
|
+
const answer = await new Promise((resolve25) => rl.question(question, resolve25));
|
|
17541
17553
|
return /^y(es)?$/i.test(answer.trim());
|
|
17542
17554
|
} finally {
|
|
17543
17555
|
rl.close();
|
|
@@ -17603,7 +17615,7 @@ function ensureTransformerLoaded() {
|
|
|
17603
17615
|
}
|
|
17604
17616
|
}
|
|
17605
17617
|
function sleep(ms) {
|
|
17606
|
-
return new Promise((
|
|
17618
|
+
return new Promise((resolve25) => setTimeout(resolve25, ms));
|
|
17607
17619
|
}
|
|
17608
17620
|
async function buildExtractorWithRetry(pipelineFn, modelName) {
|
|
17609
17621
|
let lastError;
|
|
@@ -24655,10 +24667,10 @@ function embedFileSerialized(absPath, dbPath, sha) {
|
|
|
24655
24667
|
activeEmbedSlots += 1;
|
|
24656
24668
|
return dispatchEmbed();
|
|
24657
24669
|
}
|
|
24658
|
-
return new Promise((
|
|
24670
|
+
return new Promise((resolve25) => {
|
|
24659
24671
|
embedSlotWaiters.push(() => {
|
|
24660
24672
|
activeEmbedSlots += 1;
|
|
24661
|
-
|
|
24673
|
+
resolve25(dispatchEmbed());
|
|
24662
24674
|
});
|
|
24663
24675
|
});
|
|
24664
24676
|
};
|
|
@@ -25064,7 +25076,7 @@ async function runWorkerLoop(dir, pollIntervalMs, shouldStop = () => false) {
|
|
|
25064
25076
|
lastKnownRootsSweepMs = Date.now();
|
|
25065
25077
|
}
|
|
25066
25078
|
if (shouldStop()) break;
|
|
25067
|
-
await new Promise((
|
|
25079
|
+
await new Promise((resolve25) => setTimeout(resolve25, pollIntervalMs));
|
|
25068
25080
|
}
|
|
25069
25081
|
}
|
|
25070
25082
|
function runDetachedWorkerDaemon() {
|
|
@@ -49089,7 +49101,7 @@ init_util2();
|
|
|
49089
49101
|
init_disk_cache();
|
|
49090
49102
|
init_lang_patterns();
|
|
49091
49103
|
import { readdirSync as readdirSync13, readFileSync as readFileSync24, statSync as statSync17 } from "fs";
|
|
49092
|
-
import { resolve as
|
|
49104
|
+
import { resolve as resolve13 } from "path";
|
|
49093
49105
|
|
|
49094
49106
|
// src/recall_index.ts
|
|
49095
49107
|
init_define_import_meta_env();
|
|
@@ -49322,7 +49334,7 @@ function depLockfileFingerprintSync(cmd, cwd) {
|
|
|
49322
49334
|
if (!candidates) return null;
|
|
49323
49335
|
for (const lockfile of candidates) {
|
|
49324
49336
|
try {
|
|
49325
|
-
const content = readFileSync24(
|
|
49337
|
+
const content = readFileSync24(resolve13(cwd, lockfile));
|
|
49326
49338
|
return shortFingerprint(content);
|
|
49327
49339
|
} catch {
|
|
49328
49340
|
continue;
|
|
@@ -49475,7 +49487,7 @@ function extractFirstPathArg(cmd, cwd, fallback) {
|
|
|
49475
49487
|
const token2 = tokens[i];
|
|
49476
49488
|
if (!token2.startsWith("-")) {
|
|
49477
49489
|
if (!token2.startsWith("/")) {
|
|
49478
|
-
return
|
|
49490
|
+
return resolve13(cwd, token2);
|
|
49479
49491
|
}
|
|
49480
49492
|
return token2;
|
|
49481
49493
|
}
|
|
@@ -72393,7 +72405,7 @@ init_image_shrink();
|
|
|
72393
72405
|
var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
|
|
72394
72406
|
var MAX_STDIN_BYTES = 64 * 1024 * 1024;
|
|
72395
72407
|
function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDIN_BYTES) {
|
|
72396
|
-
return new Promise((
|
|
72408
|
+
return new Promise((resolve25, reject) => {
|
|
72397
72409
|
const chunks = [];
|
|
72398
72410
|
let totalBytes = 0;
|
|
72399
72411
|
let settled = false;
|
|
@@ -72428,7 +72440,7 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
|
|
|
72428
72440
|
return;
|
|
72429
72441
|
}
|
|
72430
72442
|
try {
|
|
72431
|
-
|
|
72443
|
+
resolve25(JSON.parse(text));
|
|
72432
72444
|
} catch (err2) {
|
|
72433
72445
|
reject(err2 instanceof Error ? err2 : new Error(String(err2)));
|
|
72434
72446
|
}
|
|
@@ -73878,15 +73890,19 @@ var BRIDGE_CAPABILITY_MATRIX = [
|
|
|
73878
73890
|
harness: "copilot_cli",
|
|
73879
73891
|
label: "Copilot CLI",
|
|
73880
73892
|
sourceFile: "src/bridges/copilot_cli_install.ts (COPILOT_CLI_HOOK_EVENTS), src/bridges/copilot_cli.ts (COPILOT_TO_TG_EVENT)",
|
|
73881
|
-
implemented: /* @__PURE__ */ new Set([
|
|
73893
|
+
implemented: /* @__PURE__ */ new Set([
|
|
73894
|
+
"session_start",
|
|
73895
|
+
"pre_tool_use",
|
|
73896
|
+
"post_tool_use",
|
|
73897
|
+
"pre_compact",
|
|
73898
|
+
"stop",
|
|
73899
|
+
"subagent_stop",
|
|
73900
|
+
"user_prompt_submit"
|
|
73901
|
+
]),
|
|
73882
73902
|
reasons: [
|
|
73883
73903
|
{
|
|
73884
73904
|
events: ["notification"],
|
|
73885
73905
|
reason: "Copilot CLI has a real 'notification' hook event, but copilot_cli.ts's COPILOT_TO_TG_EVENT deliberately leaves it (and sessionEnd/postToolUseFailure/subagentStart/errorOccurred/permissionRequest) unimplemented rather than guessed at"
|
|
73886
|
-
},
|
|
73887
|
-
{
|
|
73888
|
-
events: ["session_start"],
|
|
73889
|
-
reason: "COPILOT_TO_TG_EVENT has no session-start mapping wired yet -- left unimplemented rather than guessed at"
|
|
73890
73906
|
}
|
|
73891
73907
|
]
|
|
73892
73908
|
},
|
|
@@ -78722,7 +78738,7 @@ import { existsSync as existsSync34, readdirSync as readdirSync20, statSync as s
|
|
|
78722
78738
|
import * as http from "http";
|
|
78723
78739
|
import * as https from "https";
|
|
78724
78740
|
import { isIPv4, isIPv6 } from "net";
|
|
78725
|
-
import { resolve as
|
|
78741
|
+
import { resolve as resolve18, join as join41 } from "path";
|
|
78726
78742
|
import { URL as URL2 } from "url";
|
|
78727
78743
|
import { promisify } from "util";
|
|
78728
78744
|
import { lookup as dnsLookup } from "dns";
|
|
@@ -78894,7 +78910,7 @@ function cleanupStaleDownloads() {
|
|
|
78894
78910
|
const files = readdirSync20(cacheDir);
|
|
78895
78911
|
for (const file2 of files) {
|
|
78896
78912
|
if (file2.endsWith(".tmp")) {
|
|
78897
|
-
const filePath =
|
|
78913
|
+
const filePath = resolve18(cacheDir, file2);
|
|
78898
78914
|
try {
|
|
78899
78915
|
const stat2 = statSync27(filePath);
|
|
78900
78916
|
if (Date.now() - stat2.mtimeMs < STALE_DOWNLOAD_AGE_MS) continue;
|
|
@@ -81471,8 +81487,8 @@ async function cmdMcpServe() {
|
|
|
81471
81487
|
const server = createMcpServer2();
|
|
81472
81488
|
const transport = new StdioServerTransport();
|
|
81473
81489
|
await server.connect(transport);
|
|
81474
|
-
await new Promise((
|
|
81475
|
-
server.server.onclose =
|
|
81490
|
+
await new Promise((resolve25) => {
|
|
81491
|
+
server.server.onclose = resolve25;
|
|
81476
81492
|
});
|
|
81477
81493
|
}
|
|
81478
81494
|
async function cmdHook(event, opts) {
|
|
@@ -82684,7 +82700,7 @@ function cmdWriteFile(dest, opts) {
|
|
|
82684
82700
|
throw new CliError(`TOKEN_GOAT_MAX_STDIN_MB must be a positive integer; got '${process.env["TOKEN_GOAT_MAX_STDIN_MB"] ?? ""}'`);
|
|
82685
82701
|
}
|
|
82686
82702
|
const maxBytes = maxMB * 1024 * 1024;
|
|
82687
|
-
return new Promise((
|
|
82703
|
+
return new Promise((resolve25, reject) => {
|
|
82688
82704
|
const chunks = [];
|
|
82689
82705
|
let totalBytes = 0;
|
|
82690
82706
|
let settled = false;
|
|
@@ -82708,7 +82724,7 @@ function cmdWriteFile(dest, opts) {
|
|
|
82708
82724
|
try {
|
|
82709
82725
|
atomicWriteBuffer(dest, Buffer.concat(chunks));
|
|
82710
82726
|
enqueueDirtyPathSafe(dest);
|
|
82711
|
-
|
|
82727
|
+
resolve25();
|
|
82712
82728
|
} catch (e) {
|
|
82713
82729
|
try {
|
|
82714
82730
|
mapFsError(e, void 0, dest);
|