token-goat 2.9.30 → 2.9.31

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.
Files changed (172) hide show
  1. package/README.md +24 -7
  2. package/SECURITY.md +10 -2
  3. package/THIRD_PARTY_NOTICES.md +1 -1
  4. package/dist/native/linux-arm64/tg-hook +0 -0
  5. package/dist/native/linux-x64/tg-hook +0 -0
  6. package/dist/native/win32-arm64/tg-hook.exe +0 -0
  7. package/dist/native/win32-x64/tg-hook.exe +0 -0
  8. package/dist/{token-goat-chunk-QSBRTGM2.mjs → token-goat-chunk-25TRAWET.mjs} +17 -7
  9. package/dist/token-goat-chunk-2KXO6V2B.mjs +203 -0
  10. package/dist/{token-goat-chunk-IVKIZWAB.mjs → token-goat-chunk-2M7E4PF7.mjs} +41 -12
  11. package/dist/{token-goat-chunk-SMVRN2HU.mjs → token-goat-chunk-2UPZGKKY.mjs} +2 -2
  12. package/dist/token-goat-chunk-2WLPWKHV.mjs +135 -0
  13. package/dist/token-goat-chunk-2XBMMDBD.mjs +126 -0
  14. package/dist/token-goat-chunk-32ZOY4N5.mjs +23 -0
  15. package/dist/token-goat-chunk-3EZRNKTK.mjs +188 -0
  16. package/dist/token-goat-chunk-4ADZHI7J.mjs +63 -0
  17. package/dist/token-goat-chunk-4GBVPH3D.mjs +242 -0
  18. package/dist/{token-goat-chunk-KWPNJBDH.mjs → token-goat-chunk-4GILLLWY.mjs} +7 -4
  19. package/dist/{token-goat-chunk-67TQJ76B.mjs → token-goat-chunk-4NAPX7BA.mjs} +36 -3
  20. package/dist/{token-goat-chunk-PU7V3FD6.mjs → token-goat-chunk-4SC7UJYV.mjs} +3354 -2553
  21. package/dist/token-goat-chunk-4SMRO4TO.mjs +366 -0
  22. package/dist/{token-goat-chunk-AFLYLHDH.mjs → token-goat-chunk-4ZBFWLXF.mjs} +283 -427
  23. package/dist/{token-goat-chunk-JDWCRD6M.mjs → token-goat-chunk-57BIAXJL.mjs} +73 -40
  24. package/dist/token-goat-chunk-5AC66HNP.mjs +115 -0
  25. package/dist/token-goat-chunk-5IMLKSOP.mjs +145 -0
  26. package/dist/token-goat-chunk-646E6U44.mjs +398 -0
  27. package/dist/token-goat-chunk-65N4RHP4.mjs +33 -0
  28. package/dist/{token-goat-chunk-JN4XMLSD.mjs → token-goat-chunk-6DG6HWP3.mjs} +8 -2
  29. package/dist/token-goat-chunk-6ENQSNRZ.mjs +573 -0
  30. package/dist/token-goat-chunk-6ZY4GNF2.mjs +91 -0
  31. package/dist/{token-goat-chunk-VPBBTSJR.mjs → token-goat-chunk-76FGDLRO.mjs} +262 -255
  32. package/dist/{token-goat-chunk-7IN2MKDS.mjs → token-goat-chunk-7EZTZOMC.mjs} +121 -289
  33. package/dist/{token-goat-chunk-OJBNYTJP.mjs → token-goat-chunk-7MAOA6X4.mjs} +640 -80
  34. package/dist/token-goat-chunk-7R2ZVVAX.mjs +218 -0
  35. package/dist/{token-goat-chunk-PD5VKH46.mjs → token-goat-chunk-AUNGYYQX.mjs} +2 -2
  36. package/dist/{token-goat-chunk-HG45AF2B.mjs → token-goat-chunk-AUQYRMMH.mjs} +794 -37
  37. package/dist/token-goat-chunk-B3PEKBYG.mjs +128 -0
  38. package/dist/{token-goat-chunk-A3CK6N5Z.mjs → token-goat-chunk-BA4HI6BW.mjs} +1309 -110
  39. package/dist/{token-goat-chunk-JB6QN6VF.mjs → token-goat-chunk-BFCWY73P.mjs} +59 -23
  40. package/dist/{token-goat-chunk-B45VMIIH.mjs → token-goat-chunk-BWGFUZZE.mjs} +7 -5
  41. package/dist/{token-goat-chunk-P4QMM4WK.mjs → token-goat-chunk-C75NA7PL.mjs} +50 -23
  42. package/dist/token-goat-chunk-CBIQ7WTD.mjs +87 -0
  43. package/dist/token-goat-chunk-CETM74KC.mjs +307 -0
  44. package/dist/{token-goat-chunk-H2PHHGM5.mjs → token-goat-chunk-CIMQ2BB3.mjs} +57 -12
  45. package/dist/{token-goat-chunk-N2NMER56.mjs → token-goat-chunk-CJLS3CDL.mjs} +46 -4
  46. package/dist/{token-goat-chunk-VES7EL7O.mjs → token-goat-chunk-CO3UYX6G.mjs} +1 -1
  47. package/dist/token-goat-chunk-COPNXRJM.mjs +154 -0
  48. package/dist/token-goat-chunk-CSJ5L3NL.mjs +479 -0
  49. package/dist/{token-goat-chunk-4LCCNRTG.mjs → token-goat-chunk-CVCEVUGK.mjs} +8 -6
  50. package/dist/{token-goat-chunk-B2RZZCSS.mjs → token-goat-chunk-CXHOFN7R.mjs} +37 -20
  51. package/dist/token-goat-chunk-D735C4QM.mjs +603 -0
  52. package/dist/{token-goat-chunk-APGZXSPS.mjs → token-goat-chunk-DGS2LVNA.mjs} +47 -30
  53. package/dist/{token-goat-chunk-54K3EUV3.mjs → token-goat-chunk-DOWRSMWO.mjs} +1 -1
  54. package/dist/token-goat-chunk-E45INMY3.mjs +71 -0
  55. package/dist/token-goat-chunk-EC5F37F4.mjs +244 -0
  56. package/dist/{token-goat-chunk-XTQAOTSO.mjs → token-goat-chunk-EDBDRVH5.mjs} +4 -1
  57. package/dist/{token-goat-chunk-5BCIJXMO.mjs → token-goat-chunk-EELTBKDM.mjs} +14 -10
  58. package/dist/{token-goat-chunk-W3QCJS6G.mjs → token-goat-chunk-F4HYL64R.mjs} +8 -6
  59. package/dist/token-goat-chunk-F6PAUIEK.mjs +624 -0
  60. package/dist/token-goat-chunk-FDSREWLX.mjs +692 -0
  61. package/dist/token-goat-chunk-GGGG7RWB.mjs +236 -0
  62. package/dist/{token-goat-chunk-C63JQV5K.mjs → token-goat-chunk-GL3ETKSC.mjs} +233 -15
  63. package/dist/token-goat-chunk-GVB2MUST.mjs +1298 -0
  64. package/dist/token-goat-chunk-GWFLAUMQ.mjs +307 -0
  65. package/dist/{token-goat-chunk-ZEJVMUQH.mjs → token-goat-chunk-HLPW63A7.mjs} +2 -2
  66. package/dist/{token-goat-chunk-GOLEOXZP.mjs → token-goat-chunk-HU4TORC5.mjs} +44 -26
  67. package/dist/{token-goat-chunk-Z57YODA2.mjs → token-goat-chunk-I7VKZZAW.mjs} +2 -2
  68. package/dist/{token-goat-chunk-NYKZLXMK.mjs → token-goat-chunk-IA6XSPE3.mjs} +6 -6
  69. package/dist/{token-goat-chunk-E3OQX2XT.mjs → token-goat-chunk-IAGXMHIK.mjs} +81 -42
  70. package/dist/{token-goat-chunk-HYBFA5X3.mjs → token-goat-chunk-IK2DRMFX.mjs} +24 -68
  71. package/dist/{token-goat-chunk-COBQGL5S.mjs → token-goat-chunk-INNPTRUX.mjs} +249 -299
  72. package/dist/{token-goat-chunk-7NADEDKJ.mjs → token-goat-chunk-JJ5BX4F4.mjs} +6 -1
  73. package/dist/{token-goat-chunk-PEPTHRFF.mjs → token-goat-chunk-JVFSHVFJ.mjs} +6 -6
  74. package/dist/token-goat-chunk-KGEFFSHD.mjs +50 -0
  75. package/dist/token-goat-chunk-KQBLRPBN.mjs +433 -0
  76. package/dist/{token-goat-chunk-Q5VTGRH7.mjs → token-goat-chunk-LCNQ2CNN.mjs} +55 -319
  77. package/dist/token-goat-chunk-M3LFJDPU.mjs +58 -0
  78. package/dist/token-goat-chunk-M52KPT3C.mjs +87 -0
  79. package/dist/token-goat-chunk-MBEG42H7.mjs +150 -0
  80. package/dist/{token-goat-chunk-EVC4TOLE.mjs → token-goat-chunk-METECDOH.mjs} +3 -12
  81. package/dist/{token-goat-chunk-T4FK7VOV.mjs → token-goat-chunk-MHX7XCC3.mjs} +2 -1
  82. package/dist/token-goat-chunk-N5LSOVSU.mjs +137 -0
  83. package/dist/{token-goat-chunk-LOBGHXPB.mjs → token-goat-chunk-N5MMR2IW.mjs} +1 -1
  84. package/dist/token-goat-chunk-N7B3JQ2C.mjs +160 -0
  85. package/dist/{token-goat-chunk-RHXKCZX3.mjs → token-goat-chunk-NISNMD7T.mjs} +2182 -2959
  86. package/dist/token-goat-chunk-NMD42SFQ.mjs +434 -0
  87. package/dist/token-goat-chunk-NOSS5VWY.mjs +174 -0
  88. package/dist/{token-goat-chunk-KQNZDCBD.mjs → token-goat-chunk-NSNSPPBG.mjs} +25 -3
  89. package/dist/token-goat-chunk-OBOFIHKN.mjs +38 -0
  90. package/dist/token-goat-chunk-OBRFJ5O5.mjs +76 -0
  91. package/dist/{token-goat-chunk-JTQQGXZH.mjs → token-goat-chunk-ODBT2USM.mjs} +124 -57
  92. package/dist/token-goat-chunk-OS5EM5PP.mjs +107 -0
  93. package/dist/token-goat-chunk-OUKDTVNU.mjs +1178 -0
  94. package/dist/token-goat-chunk-OXFMP75N.mjs +566 -0
  95. package/dist/{token-goat-chunk-DZX2YDPT.mjs → token-goat-chunk-OYIYKDDT.mjs} +2 -2
  96. package/dist/{token-goat-chunk-QCLDAQTS.mjs → token-goat-chunk-P6HQSWQ3.mjs} +50 -53
  97. package/dist/token-goat-chunk-P6VGNTQT.mjs +71 -0
  98. package/dist/{token-goat-chunk-7BXSNBY5.mjs → token-goat-chunk-PFWIMH2E.mjs} +23 -15
  99. package/dist/token-goat-chunk-PJZXIS3G.mjs +39 -0
  100. package/dist/token-goat-chunk-PUO5INXP.mjs +606 -0
  101. package/dist/{token-goat-chunk-GFBSN7BD.mjs → token-goat-chunk-PX5PQWTY.mjs} +22 -250
  102. package/dist/token-goat-chunk-Q5QDBC6O.mjs +111 -0
  103. package/dist/token-goat-chunk-QFNQXSDT.mjs +40 -0
  104. package/dist/{token-goat-chunk-FBNQNSJK.mjs → token-goat-chunk-QH7YVYOY.mjs} +2198 -2782
  105. package/dist/{token-goat-chunk-64FBPDTK.mjs → token-goat-chunk-QLMUHEPB.mjs} +12 -9
  106. package/dist/{token-goat-chunk-RZRTDST7.mjs → token-goat-chunk-QQOJIHL3.mjs} +37 -11
  107. package/dist/{token-goat-chunk-XKI4SEMX.mjs → token-goat-chunk-QQTWJY2O.mjs} +19 -0
  108. package/dist/{token-goat-chunk-U4N3BN2W.mjs → token-goat-chunk-QYHO5CPH.mjs} +3 -2
  109. package/dist/token-goat-chunk-RELAVWAD.mjs +27 -0
  110. package/dist/{token-goat-chunk-ETBHVDB6.mjs → token-goat-chunk-RSLPOBKQ.mjs} +22 -11
  111. package/dist/token-goat-chunk-SAMFDIS2.mjs +712 -0
  112. package/dist/token-goat-chunk-SDELXLDM.mjs +48 -0
  113. package/dist/{token-goat-chunk-4JJS54TR.mjs → token-goat-chunk-SOAFZNNZ.mjs} +161 -38
  114. package/dist/{token-goat-chunk-664RTTUV.mjs → token-goat-chunk-SRTHT7KV.mjs} +21 -16
  115. package/dist/{token-goat-chunk-4FVIAHSI.mjs → token-goat-chunk-T5P5ACNQ.mjs} +7 -6
  116. package/dist/token-goat-chunk-T5YGG4V3.mjs +45 -0
  117. package/dist/token-goat-chunk-TAUZ4OFS.mjs +110 -0
  118. package/dist/{token-goat-chunk-COTXZHTS.mjs → token-goat-chunk-TM6MC754.mjs} +4 -3
  119. package/dist/token-goat-chunk-TWUKBOMP.mjs +72 -0
  120. package/dist/{token-goat-chunk-3CHUNJ2O.mjs → token-goat-chunk-U2KSB4SG.mjs} +8 -5
  121. package/dist/{token-goat-chunk-3Z5S4KGM.mjs → token-goat-chunk-U4WHMG6Y.mjs} +29 -49
  122. package/dist/{token-goat-chunk-PY533T7F.mjs → token-goat-chunk-UGJHGR5A.mjs} +21 -15
  123. package/dist/{token-goat-chunk-CYCEYAEO.mjs → token-goat-chunk-UOUDVA5U.mjs} +1 -1
  124. package/dist/{token-goat-chunk-JDHE26RP.mjs → token-goat-chunk-URRJ33GR.mjs} +16 -5
  125. package/dist/{token-goat-chunk-56EIMS3E.mjs → token-goat-chunk-UZOROKIS.mjs} +23 -17
  126. package/dist/{token-goat-chunk-CZ3LUXYZ.mjs → token-goat-chunk-VIHGGGEI.mjs} +427 -308
  127. package/dist/token-goat-chunk-VTJLAA7P.mjs +21 -0
  128. package/dist/token-goat-chunk-VWDXLT4F.mjs +678 -0
  129. package/dist/{token-goat-chunk-YARZFCZS.mjs → token-goat-chunk-VWZA2H4P.mjs} +62 -53
  130. package/dist/{token-goat-chunk-SQSSNQPE.mjs → token-goat-chunk-VXEXMJDL.mjs} +3979 -4745
  131. package/dist/token-goat-chunk-WBA5SKT4.mjs +547 -0
  132. package/dist/{token-goat-chunk-SJHWVNDQ.mjs → token-goat-chunk-WDH44VAU.mjs} +7 -4
  133. package/dist/token-goat-chunk-X2SUP3EI.mjs +107 -0
  134. package/dist/token-goat-chunk-X3BELZSJ.mjs +125 -0
  135. package/dist/{token-goat-chunk-FZ3NEW5O.mjs → token-goat-chunk-XLRBNZCC.mjs} +13 -8
  136. package/dist/{token-goat-chunk-Z44YO3QZ.mjs → token-goat-chunk-XRKUX6CM.mjs} +1 -1
  137. package/dist/token-goat-chunk-XX6O25RN.mjs +82 -0
  138. package/dist/token-goat-chunk-XZSUHBVS.mjs +65 -0
  139. package/dist/{token-goat-chunk-WY3L4DVT.mjs → token-goat-chunk-Y2ZTQOCL.mjs} +13 -8
  140. package/dist/{token-goat-chunk-UQ5UK2T7.mjs → token-goat-chunk-YB4VMGK2.mjs} +1 -1
  141. package/dist/token-goat-chunk-YBIIY3HR.mjs +51 -0
  142. package/dist/{token-goat-chunk-ZQSJMO2R.mjs → token-goat-chunk-YHVDKQBR.mjs} +105 -105
  143. package/dist/{token-goat-chunk-H2K3QP6H.mjs → token-goat-chunk-YJB5XLWI.mjs} +8 -6
  144. package/dist/{token-goat-chunk-ZCDTCVIS.mjs → token-goat-chunk-YKB6AQG3.mjs} +5 -2
  145. package/dist/token-goat-chunk-YMTG2YA5.mjs +35 -0
  146. package/dist/token-goat-chunk-YPBZYQRV.mjs +40 -0
  147. package/dist/{token-goat-chunk-HOJLCGDP.mjs → token-goat-chunk-YY6AVUFL.mjs} +2033 -2251
  148. package/dist/token-goat-chunk-Z7N5OMMP.mjs +98 -0
  149. package/dist/token-goat-chunk-ZNMNUPAZ.mjs +938 -0
  150. package/dist/token-goat-hook-client.cjs +1 -1
  151. package/dist/token-goat-hook-client.mjs +3 -3
  152. package/dist/token-goat-hook.mjs +67 -38
  153. package/dist/token-goat.core.mjs +4 -4
  154. package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
  155. package/docs/cli.md +40 -21
  156. package/docs/install.md +19 -9
  157. package/docs/security.md +5 -3
  158. package/package.json +2 -1
  159. package/dist/token-goat-chunk-5KDMCMF2.mjs +0 -362
  160. package/dist/token-goat-chunk-6QMAS7AB.mjs +0 -42
  161. package/dist/token-goat-chunk-7F2UBPD3.mjs +0 -397
  162. package/dist/token-goat-chunk-ARDNEC2U.mjs +0 -248
  163. package/dist/token-goat-chunk-ATCPY7EQ.mjs +0 -134
  164. package/dist/token-goat-chunk-EFHMMIGP.mjs +0 -68
  165. package/dist/token-goat-chunk-IVALLT3S.mjs +0 -48
  166. package/dist/token-goat-chunk-JDU3EXSU.mjs +0 -65
  167. package/dist/token-goat-chunk-MKPF4CL5.mjs +0 -37
  168. package/dist/token-goat-chunk-RC35RBUL.mjs +0 -312
  169. package/dist/token-goat-chunk-USILZ3T3.mjs +0 -53
  170. package/dist/token-goat-chunk-VL3YIFHZ.mjs +0 -2975
  171. package/dist/token-goat-chunk-XU4P6Z24.mjs +0 -133
  172. package/dist/token-goat-chunk-Y776AENC.mjs +0 -1597
@@ -0,0 +1,712 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ claudeConfigDir
5
+ } from "./token-goat-chunk-BPGCUMR7.mjs";
6
+ import {
7
+ DEFAULT_MAX_AGE_MS,
8
+ DEFAULT_MAX_COUNT
9
+ } from "./token-goat-chunk-4ZBFWLXF.mjs";
10
+ import {
11
+ recordStat
12
+ } from "./token-goat-chunk-U4WHMG6Y.mjs";
13
+ import {
14
+ redactSecrets
15
+ } from "./token-goat-chunk-T5P5ACNQ.mjs";
16
+ import {
17
+ shortFingerprint
18
+ } from "./token-goat-chunk-4GBVPH3D.mjs";
19
+ import {
20
+ registerReset
21
+ } from "./token-goat-chunk-YZAAZU2S.mjs";
22
+ import {
23
+ atomicWriteText,
24
+ ensureDirSync,
25
+ isCodeFenceDelimiter,
26
+ stripLower
27
+ } from "./token-goat-chunk-HU4TORC5.mjs";
28
+ import {
29
+ dataDir
30
+ } from "./token-goat-chunk-CO3UYX6G.mjs";
31
+ import {
32
+ init_define_import_meta_env
33
+ } from "./token-goat-chunk-A37V4PBF.mjs";
34
+
35
+ // src/skill_cache.ts
36
+ init_define_import_meta_env();
37
+ import * as fs from "fs/promises";
38
+ import { resolve } from "path";
39
+ import { readdirSync, readFileSync, existsSync, statSync, unlinkSync } from "node:fs";
40
+ var COMPACT_END_MARKER = "<!-- COMPACT_END -->";
41
+ var SKILLS_OUTPUT_SUBDIR = "skills";
42
+ var _skillOutputsDirOverride = null;
43
+ var _skillsSourceDirOverride = null;
44
+ var _pluginsManifestPathOverride = null;
45
+ registerReset(() => {
46
+ _skillOutputsDirOverride = null;
47
+ _skillsSourceDirOverride = null;
48
+ _pluginsManifestPathOverride = null;
49
+ inProcessHitLockQueues.clear();
50
+ });
51
+ function skillOutputsDir() {
52
+ if (_skillOutputsDirOverride) return _skillOutputsDirOverride;
53
+ return resolve(dataDir(), SKILLS_OUTPUT_SUBDIR);
54
+ }
55
+ function skillsSourceDir() {
56
+ if (_skillsSourceDirOverride) return _skillsSourceDirOverride;
57
+ return resolve(claudeConfigDir(), "skills");
58
+ }
59
+ async function ensureSkillsDir() {
60
+ try {
61
+ ensureDirSync(skillOutputsDir());
62
+ } catch {
63
+ }
64
+ }
65
+ function contentHash(content) {
66
+ return shortFingerprint(content);
67
+ }
68
+ function safeSessionFragment(sessionId) {
69
+ let result = "";
70
+ for (const c of sessionId) {
71
+ if (result.length >= 16) break;
72
+ if (/[a-zA-Z0-9_-]/.test(c)) {
73
+ result += c;
74
+ }
75
+ }
76
+ return result.slice(0, 16);
77
+ }
78
+ function safeSkillName(skillName) {
79
+ if (!skillName || skillName.length > 128) return null;
80
+ if (!/^[A-Za-z0-9_:-]+$/.test(skillName)) return null;
81
+ return skillName.replace(/[^a-zA-Z0-9_:-]/g, "_");
82
+ }
83
+ function sanitizeSkillId(name) {
84
+ return name.replace(/:/g, "~");
85
+ }
86
+ function sessionSkillPrefix(sessionId, skillName) {
87
+ const safeSession = safeSessionFragment(sessionId);
88
+ return `${safeSession}-${sanitizeSkillId(skillName)}-`;
89
+ }
90
+ function outputIdFor(sessionId, skillName, contentSha) {
91
+ return `${sessionSkillPrefix(sessionId, skillName)}${contentSha}`;
92
+ }
93
+ var LOAD_RECORD_EXT = ".load";
94
+ function readLoadRecordsSync(dir) {
95
+ const records = [];
96
+ let files;
97
+ try {
98
+ files = readdirSync(dir);
99
+ } catch {
100
+ return records;
101
+ }
102
+ for (const file of files) {
103
+ if (!file.endsWith(LOAD_RECORD_EXT)) continue;
104
+ try {
105
+ const parsed = JSON.parse(readFileSync(resolve(dir, file), "utf-8"));
106
+ if (typeof parsed.skillName !== "string" || typeof parsed.bodyOutputId !== "string") continue;
107
+ records.push({ ...parsed, loadId: file.slice(0, -LOAD_RECORD_EXT.length) });
108
+ } catch {
109
+ continue;
110
+ }
111
+ }
112
+ return records;
113
+ }
114
+ async function sessionLoads(safeSession, name) {
115
+ const metas = await listOutputs();
116
+ const own = metas.filter((m) => m.skillName === name && m.outputId.startsWith(`${safeSession}-`));
117
+ const storedIds = new Set(metas.map((m) => m.outputId));
118
+ const recorded = readLoadRecordsSync(skillOutputsDir()).filter(
119
+ (r) => r.skillName === name && r.loadId.startsWith(`${safeSession}-`) && storedIds.has(r.bodyOutputId)
120
+ );
121
+ return [...own, ...recorded].map((e) => ({ bodyBytes: e.bodyBytes, ts: e.ts }));
122
+ }
123
+ function* contentLineEntries(lines) {
124
+ let inCodeBlock = false;
125
+ for (let i = 0; i < lines.length; i++) {
126
+ const stripped = lines[i].trim();
127
+ if (isCodeFenceDelimiter(stripped)) {
128
+ inCodeBlock = !inCodeBlock;
129
+ continue;
130
+ }
131
+ if (!inCodeBlock) yield [i, stripped];
132
+ }
133
+ }
134
+ function headingLevel(stripped) {
135
+ const match = stripped.match(/^(#{1,6}) (.*)$/);
136
+ if (!match) return 0;
137
+ return match[2].trim() ? match[1].length : 0;
138
+ }
139
+ function collectSectionBody(lines, startIdx, matchedLevel) {
140
+ const bodyLines = [];
141
+ let inBodyCodeBlock = false;
142
+ for (let j = startIdx; j < lines.length; j++) {
143
+ const stripped = lines[j].trim();
144
+ if (isCodeFenceDelimiter(stripped)) {
145
+ inBodyCodeBlock = !inBodyCodeBlock;
146
+ }
147
+ if (!inBodyCodeBlock) {
148
+ const level = headingLevel(stripped);
149
+ if (level >= 1 && level <= matchedLevel) break;
150
+ }
151
+ bodyLines.push(lines[j]);
152
+ }
153
+ return bodyLines;
154
+ }
155
+ function extractCompactFromMarker(body) {
156
+ if (!body || !body.includes(COMPACT_END_MARKER)) {
157
+ return null;
158
+ }
159
+ const lines = body.split("\n");
160
+ for (const [i, stripped] of contentLineEntries(lines)) {
161
+ if (stripped === COMPACT_END_MARKER) {
162
+ const preMarker = lines.slice(0, i).join("\n").trim();
163
+ return preMarker || null;
164
+ }
165
+ }
166
+ return null;
167
+ }
168
+ function parseSectionOrdinal(heading) {
169
+ const match = heading.match(/^(.*?)(?:#(\d+))?$/);
170
+ if (!match) return [heading, 1];
171
+ const baseHeading = match[1] || heading;
172
+ const ordinal = match[2] ? Math.max(1, parseInt(match[2], 10)) : 1;
173
+ return [baseHeading, ordinal];
174
+ }
175
+ function extractNamedSection(body, heading) {
176
+ if (!body || !heading) return null;
177
+ const [baseHeading, ordinal] = parseSectionOrdinal(heading);
178
+ const headingLower = stripLower(baseHeading);
179
+ const lines = body.split("\n");
180
+ let matchCount = 0;
181
+ let startIdx = -1;
182
+ let matchedLevel = 0;
183
+ for (const [i, stripped] of contentLineEntries(lines)) {
184
+ const level = headingLevel(stripped);
185
+ if (level >= 1 && level <= 3) {
186
+ const headingText = stripLower(stripped.slice(level));
187
+ if (headingText === headingLower) {
188
+ matchCount++;
189
+ if (matchCount === ordinal) {
190
+ startIdx = i + 1;
191
+ matchedLevel = level;
192
+ break;
193
+ }
194
+ }
195
+ }
196
+ }
197
+ if (startIdx === -1) return null;
198
+ const text = collectSectionBody(lines, startIdx, matchedLevel).join("\n").trim();
199
+ return text || null;
200
+ }
201
+ function extractChecklistSection(body) {
202
+ if (!body) return null;
203
+ const checklistHeadings = ["checklist", "check list", "to-do", "todo"];
204
+ const lines = body.split("\n");
205
+ let bestPriority = checklistHeadings.length;
206
+ let bestStart = -1;
207
+ for (const [i, stripped] of contentLineEntries(lines)) {
208
+ if (stripped.startsWith("## ") && stripped.length > 3) {
209
+ const headingText = stripLower(stripped.slice(3));
210
+ for (let p = 0; p < checklistHeadings.length; p++) {
211
+ if (headingText.startsWith(checklistHeadings[p])) {
212
+ if (p < bestPriority) {
213
+ bestPriority = p;
214
+ bestStart = i;
215
+ }
216
+ break;
217
+ }
218
+ }
219
+ }
220
+ }
221
+ if (bestStart === -1) return null;
222
+ const bodyLines = collectSectionBody(lines, bestStart + 1, 2);
223
+ let text = bodyLines.join("\n").trim();
224
+ const maxChars = 2e3;
225
+ if (text.length > maxChars) {
226
+ const cut = maxChars - 1;
227
+ text = text.slice(0, cut).trimEnd() + "\u2026";
228
+ }
229
+ return text || null;
230
+ }
231
+ async function listOutputs() {
232
+ try {
233
+ const dir = skillOutputsDir();
234
+ const entries = await fs.readdir(dir, { withFileTypes: true });
235
+ const metas = [];
236
+ for (const entry of entries) {
237
+ if (!entry.isFile() || !entry.name.endsWith(".meta")) {
238
+ continue;
239
+ }
240
+ try {
241
+ const content = await fs.readFile(resolve(dir, entry.name), "utf-8");
242
+ const meta = JSON.parse(content);
243
+ metas.push(meta);
244
+ } catch {
245
+ continue;
246
+ }
247
+ }
248
+ return metas;
249
+ } catch {
250
+ return [];
251
+ }
252
+ }
253
+ async function hasSessionOutput(sessionId, skillName) {
254
+ try {
255
+ if (!sessionId) return false;
256
+ const name = safeSkillName(skillName);
257
+ if (!name) return false;
258
+ return (await sessionLoads(safeSessionFragment(sessionId), name)).length > 0;
259
+ } catch {
260
+ return false;
261
+ }
262
+ }
263
+ async function sessionOutputBodyBytes(sessionId, skillName) {
264
+ try {
265
+ if (!sessionId) return null;
266
+ const name = safeSkillName(skillName);
267
+ if (!name) return null;
268
+ const matches = await sessionLoads(safeSessionFragment(sessionId), name);
269
+ if (matches.length === 0) return null;
270
+ matches.sort((a, b) => b.ts - a.ts);
271
+ return matches[0].bodyBytes;
272
+ } catch {
273
+ return null;
274
+ }
275
+ }
276
+ async function findCrossSessionEntry(skillName, contentSha) {
277
+ const name = safeSkillName(skillName);
278
+ if (!name || !contentSha) return null;
279
+ try {
280
+ const metas = await listOutputs();
281
+ for (const meta of metas) {
282
+ if (meta.skillName !== name || meta.contentSha !== contentSha) {
283
+ continue;
284
+ }
285
+ const dir = skillOutputsDir();
286
+ const bodyPath = resolve(dir, `${meta.outputId}.txt`);
287
+ try {
288
+ const bodyExists = await fs.access(bodyPath).then(() => true).catch(() => false);
289
+ if (bodyExists) {
290
+ return meta;
291
+ }
292
+ } catch {
293
+ continue;
294
+ }
295
+ }
296
+ } catch {
297
+ return null;
298
+ }
299
+ return null;
300
+ }
301
+ var lastStoreTs = 0;
302
+ function nextStoreTs() {
303
+ lastStoreTs = Math.max(Date.now(), lastStoreTs + 1);
304
+ return lastStoreTs;
305
+ }
306
+ async function storeOutput(sessionId, skillName, body, opts) {
307
+ try {
308
+ await ensureSkillsDir();
309
+ const name = safeSkillName(skillName);
310
+ if (!name) return null;
311
+ const sha = contentHash(body);
312
+ const outId = outputIdFor(sessionId, skillName, sha);
313
+ const dir = skillOutputsDir();
314
+ const ts = nextStoreTs();
315
+ const existing = await findCrossSessionEntry(skillName, sha);
316
+ if (existing) {
317
+ if (existing.outputId !== outId && sessionId) {
318
+ const record = {
319
+ skillName: name,
320
+ contentSha: sha,
321
+ bodyOutputId: existing.outputId,
322
+ bodyBytes: Buffer.byteLength(body, "utf-8"),
323
+ ts
324
+ };
325
+ await atomicWriteText(resolve(dir, `${outId}${LOAD_RECORD_EXT}`), JSON.stringify(record, null, 2));
326
+ }
327
+ return existing;
328
+ }
329
+ const bodyBytes = Buffer.byteLength(body, "utf-8");
330
+ const truncated = bodyBytes > 256 * 1024;
331
+ const redactedBody = redactSecrets(body);
332
+ if (redactedBody.count > 0) recordStat("secret_redacted", 0, redactedBody.count, void 0, SKILLS_OUTPUT_SUBDIR);
333
+ let storedBody = redactedBody.text;
334
+ if (truncated) {
335
+ const buf = Buffer.from(redactedBody.text, "utf-8");
336
+ let truncStart = Math.max(0, buf.length - 262144);
337
+ if (truncStart < buf.length) {
338
+ const byte = buf[truncStart];
339
+ if ((byte & 192) === 128) {
340
+ let charStart = truncStart - 1;
341
+ while (charStart >= 0 && (buf[charStart] & 192) === 128) {
342
+ charStart--;
343
+ }
344
+ if (charStart >= 0) {
345
+ const leadByte = buf[charStart];
346
+ let charLen = 1;
347
+ if ((leadByte & 128) === 0) charLen = 1;
348
+ else if ((leadByte & 224) === 192) charLen = 2;
349
+ else if ((leadByte & 240) === 224) charLen = 3;
350
+ else if ((leadByte & 248) === 240) charLen = 4;
351
+ truncStart = charStart + charLen;
352
+ }
353
+ }
354
+ }
355
+ const truncBuf = buf.slice(truncStart);
356
+ storedBody = truncBuf.toString("utf-8");
357
+ }
358
+ await atomicWriteText(resolve(dir, `${outId}.txt`), storedBody);
359
+ const meta = {
360
+ outputId: outId,
361
+ skillName: name,
362
+ contentSha: sha,
363
+ bodyBytes,
364
+ ts,
365
+ truncated,
366
+ sourcePath: opts?.sourcePath || ""
367
+ };
368
+ await atomicWriteText(resolve(dir, `${outId}.meta`), JSON.stringify(meta, null, 2));
369
+ pruneSkillOutputs();
370
+ return meta;
371
+ } catch {
372
+ return null;
373
+ }
374
+ }
375
+ async function storeCompact(sessionId, skillName, compactText, sourceSha) {
376
+ try {
377
+ await ensureSkillsDir();
378
+ const name = safeSkillName(skillName);
379
+ if (!name) return;
380
+ const safeSession = safeSessionFragment(sessionId);
381
+ const fileId = `${safeSession}@${sanitizeSkillId(name)}@compact`;
382
+ const dir = skillOutputsDir();
383
+ const redacted = redactSecrets(compactText);
384
+ if (redacted.count > 0) recordStat("secret_redacted", 0, redacted.count, void 0, SKILLS_OUTPUT_SUBDIR);
385
+ let text = redacted.text;
386
+ if (sourceSha) {
387
+ text = `<!-- source_sha: ${sourceSha.slice(0, 12)} -->
388
+ ${text}`;
389
+ }
390
+ await atomicWriteText(resolve(dir, fileId), text);
391
+ } catch {
392
+ }
393
+ }
394
+ function compactSessionSuffix(skillName) {
395
+ const name = safeSkillName(skillName);
396
+ return name ? `@${sanitizeSkillId(name)}@compact` : null;
397
+ }
398
+ function matchesCompactSuffix(entryName, isFile, suffix) {
399
+ return isFile && entryName.endsWith("@compact") && entryName.endsWith(suffix);
400
+ }
401
+ function getCompactAnySessionSync(skillName) {
402
+ try {
403
+ const suffix = compactSessionSuffix(skillName);
404
+ if (!suffix) return null;
405
+ const dir = skillOutputsDir();
406
+ const entries = readdirSync(dir, { withFileTypes: true });
407
+ for (const entry of entries) {
408
+ if (!matchesCompactSuffix(entry.name, entry.isFile(), suffix)) continue;
409
+ try {
410
+ const text = readFileSync(resolve(dir, entry.name), "utf-8");
411
+ if (text.trim()) return text;
412
+ } catch {
413
+ continue;
414
+ }
415
+ }
416
+ return null;
417
+ } catch {
418
+ return null;
419
+ }
420
+ }
421
+ function extractSourceShaFromCompact(compactText) {
422
+ if (!compactText) return null;
423
+ const match = compactText.match(/<!--\s*source_sha:\s*([a-f0-9]{12})\s*-->/);
424
+ return match ? match[1] : null;
425
+ }
426
+ function isCompactStale(compactText, skillName, currentBodySha) {
427
+ if (!compactText) return null;
428
+ const embeddedSha = extractSourceShaFromCompact(compactText);
429
+ if (!embeddedSha) return null;
430
+ const currentSha = currentBodySha.slice(0, 12);
431
+ return embeddedSha !== currentSha;
432
+ }
433
+ async function readSkillHits(skillName) {
434
+ try {
435
+ const name = safeSkillName(skillName);
436
+ if (!name) return { count: 0, lastTs: 0 };
437
+ const dir = skillOutputsDir();
438
+ const hitsFile = resolve(dir, `${sanitizeSkillId(name)}.hits`);
439
+ const content = await fs.readFile(hitsFile, "utf-8").catch(() => null);
440
+ if (content) {
441
+ const parsed = JSON.parse(content);
442
+ return parsed;
443
+ }
444
+ } catch {
445
+ }
446
+ return { count: 0, lastTs: 0 };
447
+ }
448
+ var SKILL_HIT_LOCK_RETRY_MS = 5;
449
+ var SKILL_HIT_LOCK_STALE_MS = 5e3;
450
+ var SKILL_HIT_LOCK_MAX_ATTEMPTS = Math.ceil(SKILL_HIT_LOCK_STALE_MS / SKILL_HIT_LOCK_RETRY_MS);
451
+ var inProcessHitLockQueues = /* @__PURE__ */ new Map();
452
+ function runExclusiveInProcess(key, fn) {
453
+ const prior = inProcessHitLockQueues.get(key) ?? Promise.resolve();
454
+ const run = prior.then(fn, fn);
455
+ inProcessHitLockQueues.set(
456
+ key,
457
+ run.then(
458
+ () => void 0,
459
+ () => void 0
460
+ )
461
+ );
462
+ return run;
463
+ }
464
+ async function acquireSkillHitLock(lockPath) {
465
+ for (let attempt = 0; attempt < SKILL_HIT_LOCK_MAX_ATTEMPTS; attempt++) {
466
+ try {
467
+ await fs.mkdir(lockPath);
468
+ return true;
469
+ } catch (err) {
470
+ if (err.code !== "EEXIST") return false;
471
+ try {
472
+ const st = await fs.stat(lockPath);
473
+ if (Date.now() - st.mtimeMs > SKILL_HIT_LOCK_STALE_MS) {
474
+ await fs.rmdir(lockPath).catch(() => {
475
+ });
476
+ continue;
477
+ }
478
+ } catch {
479
+ continue;
480
+ }
481
+ await new Promise((r) => setTimeout(r, SKILL_HIT_LOCK_RETRY_MS));
482
+ }
483
+ }
484
+ return false;
485
+ }
486
+ async function incrementSkillHit(skillName) {
487
+ try {
488
+ const name = safeSkillName(skillName);
489
+ if (!name) return;
490
+ await ensureSkillsDir();
491
+ const dir = skillOutputsDir();
492
+ const hitsFile = resolve(dir, `${sanitizeSkillId(name)}.hits`);
493
+ const lockPath = `${hitsFile}.lock`;
494
+ await runExclusiveInProcess(lockPath, async () => {
495
+ const locked = await acquireSkillHitLock(lockPath);
496
+ if (!locked) return;
497
+ try {
498
+ const hits = await readSkillHits(name);
499
+ hits.count++;
500
+ hits.lastTs = Date.now();
501
+ await atomicWriteText(hitsFile, JSON.stringify(hits, null, 2));
502
+ } finally {
503
+ await fs.rmdir(lockPath).catch(() => {
504
+ });
505
+ }
506
+ });
507
+ } catch {
508
+ }
509
+ }
510
+ function formatAge(ageMs) {
511
+ const s = Math.floor(ageMs / 1e3);
512
+ if (s < 60) return `${s}s`;
513
+ const m = Math.floor(s / 60);
514
+ if (m < 60) return `${m}m`;
515
+ const h = Math.floor(m / 60);
516
+ if (h < 24) return `${h}h`;
517
+ const d = Math.floor(h / 24);
518
+ return `${d}d`;
519
+ }
520
+ async function listSkills(sessionId) {
521
+ try {
522
+ const metas = (await listOutputs()).sort((a, b) => b.ts - a.ts);
523
+ const results = [];
524
+ const seen = /* @__PURE__ */ new Set();
525
+ for (const meta of metas) {
526
+ if (sessionId && !meta.outputId.startsWith(`${safeSessionFragment(sessionId)}-`)) {
527
+ continue;
528
+ }
529
+ if (seen.has(meta.skillName)) continue;
530
+ seen.add(meta.skillName);
531
+ const dir = skillOutputsDir();
532
+ const sanitizedName = sanitizeSkillId(meta.skillName);
533
+ const outputSuffix = `-${sanitizedName}-${meta.contentSha}`;
534
+ const safeSession = meta.outputId.endsWith(outputSuffix) ? meta.outputId.slice(0, meta.outputId.length - outputSuffix.length) : safeSessionFragment(meta.outputId.split("-")[0]);
535
+ const compactFileId = `${safeSession}@${sanitizedName}@compact`;
536
+ let compactLen = 0;
537
+ let compactText = "";
538
+ try {
539
+ const stat2 = await fs.stat(resolve(dir, compactFileId));
540
+ compactLen = stat2.size;
541
+ compactText = await fs.readFile(resolve(dir, compactFileId), "utf-8").catch(() => "");
542
+ } catch {
543
+ compactLen = 0;
544
+ }
545
+ const hasMarker = extractCompactFromMarker(
546
+ await fs.readFile(resolve(dir, `${meta.outputId}.txt`), "utf-8").catch(() => "")
547
+ ) !== null;
548
+ const compactStale = isCompactStale(compactText, meta.skillName, meta.contentSha);
549
+ const { count: hitCount } = await readSkillHits(meta.skillName);
550
+ const ageMs = Date.now() - meta.ts;
551
+ results.push({
552
+ name: meta.skillName,
553
+ bodyLen: meta.bodyBytes,
554
+ compactLen,
555
+ hasMarker,
556
+ compactStale,
557
+ hitCount,
558
+ ageMs
559
+ });
560
+ }
561
+ return results;
562
+ } catch {
563
+ return [];
564
+ }
565
+ }
566
+ async function getSkillFilePath(skillName) {
567
+ try {
568
+ const name = safeSkillName(skillName);
569
+ if (!name) return null;
570
+ const metas = await listOutputs();
571
+ for (const meta of metas) {
572
+ if (meta.skillName === name && meta.sourcePath) {
573
+ return meta.sourcePath;
574
+ }
575
+ }
576
+ return await installedSkillPath(name);
577
+ } catch {
578
+ return null;
579
+ }
580
+ }
581
+ async function resolvePluginSkillPath(pluginName, skillSlug) {
582
+ const manifestPath = _pluginsManifestPathOverride ?? resolve(claudeConfigDir(), "plugins", "installed_plugins.json");
583
+ let raw;
584
+ try {
585
+ raw = await fs.readFile(manifestPath, "utf8");
586
+ } catch {
587
+ return null;
588
+ }
589
+ let parsed;
590
+ try {
591
+ parsed = JSON.parse(raw);
592
+ } catch {
593
+ return null;
594
+ }
595
+ if (typeof parsed !== "object" || parsed === null) return null;
596
+ const plugins = parsed["plugins"];
597
+ if (typeof plugins !== "object" || plugins === null) return null;
598
+ for (const [key, entries] of Object.entries(plugins)) {
599
+ if (key !== pluginName && !key.startsWith(pluginName + "@")) continue;
600
+ if (!Array.isArray(entries)) continue;
601
+ for (const entry of entries) {
602
+ if (typeof entry !== "object" || entry === null) continue;
603
+ const installPath = entry["installPath"];
604
+ if (typeof installPath !== "string" || installPath === "") continue;
605
+ const diskPath = resolve(installPath, "skills", skillSlug, "SKILL.md");
606
+ try {
607
+ await fs.access(diskPath);
608
+ return diskPath;
609
+ } catch {
610
+ continue;
611
+ }
612
+ }
613
+ }
614
+ return null;
615
+ }
616
+ async function installedSkillPath(skillName) {
617
+ const name = safeSkillName(skillName);
618
+ if (!name) return null;
619
+ const colonIdx = name.indexOf(":");
620
+ if (colonIdx > 0 && colonIdx < name.length - 1) {
621
+ const pluginPath = await resolvePluginSkillPath(name.slice(0, colonIdx), name.slice(colonIdx + 1));
622
+ if (pluginPath !== null) return pluginPath;
623
+ }
624
+ const diskPath = resolve(skillsSourceDir(), name, "SKILL.md");
625
+ try {
626
+ await fs.access(diskPath);
627
+ return diskPath;
628
+ } catch {
629
+ return null;
630
+ }
631
+ }
632
+ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_AGE_MS) {
633
+ const dir = skillOutputsDir();
634
+ let removed = 0;
635
+ try {
636
+ if (!existsSync(dir)) return 0;
637
+ const cutoff = Date.now() - maxAgeMs;
638
+ const entries = [];
639
+ for (const file of readdirSync(dir)) {
640
+ if (!file.endsWith(".meta")) continue;
641
+ const outputId = file.slice(0, -".meta".length);
642
+ let ts;
643
+ try {
644
+ const parsed = JSON.parse(readFileSync(resolve(dir, file), "utf-8"));
645
+ ts = parsed.ts;
646
+ } catch {
647
+ try {
648
+ ts = statSync(resolve(dir, file)).mtimeMs;
649
+ } catch {
650
+ continue;
651
+ }
652
+ }
653
+ entries.push({ outputId, ts });
654
+ }
655
+ const removeEntry = (outputId) => {
656
+ for (const ext of [".meta", ".txt"]) {
657
+ try {
658
+ unlinkSync(resolve(dir, `${outputId}${ext}`));
659
+ } catch {
660
+ }
661
+ }
662
+ removed++;
663
+ };
664
+ const kept = [];
665
+ for (const entry of entries) {
666
+ if (entry.ts < cutoff) {
667
+ removeEntry(entry.outputId);
668
+ } else {
669
+ kept.push(entry);
670
+ }
671
+ }
672
+ if (kept.length > maxCount) {
673
+ kept.sort((a, b) => a.ts - b.ts);
674
+ for (const entry of kept.slice(0, kept.length - maxCount)) {
675
+ removeEntry(entry.outputId);
676
+ }
677
+ }
678
+ for (const record of readLoadRecordsSync(dir)) {
679
+ if (record.ts >= cutoff && existsSync(resolve(dir, `${record.bodyOutputId}.meta`))) continue;
680
+ try {
681
+ unlinkSync(resolve(dir, `${record.loadId}${LOAD_RECORD_EXT}`));
682
+ } catch {
683
+ }
684
+ }
685
+ } catch {
686
+ return removed;
687
+ }
688
+ return removed;
689
+ }
690
+
691
+ export {
692
+ SKILLS_OUTPUT_SUBDIR,
693
+ skillOutputsDir,
694
+ contentHash,
695
+ safeSkillName,
696
+ extractCompactFromMarker,
697
+ extractNamedSection,
698
+ extractChecklistSection,
699
+ listOutputs,
700
+ hasSessionOutput,
701
+ sessionOutputBodyBytes,
702
+ storeOutput,
703
+ storeCompact,
704
+ getCompactAnySessionSync,
705
+ isCompactStale,
706
+ incrementSkillHit,
707
+ formatAge,
708
+ listSkills,
709
+ getSkillFilePath,
710
+ installedSkillPath,
711
+ pruneSkillOutputs
712
+ };