@mxalbert/context-mode 2.0.0

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 (365) hide show
  1. package/.claude-plugin/marketplace.json +33 -0
  2. package/.claude-plugin/plugin.json +31 -0
  3. package/.codex-plugin/hooks.json +65 -0
  4. package/.codex-plugin/mcp.json +13 -0
  5. package/.codex-plugin/plugin.json +31 -0
  6. package/.openclaw-plugin/index.ts +12 -0
  7. package/.openclaw-plugin/openclaw.plugin.json +38 -0
  8. package/.openclaw-plugin/package.json +28 -0
  9. package/LICENSE +94 -0
  10. package/README.md +1619 -0
  11. package/bin/statusline.mjs +378 -0
  12. package/build/adapters/antigravity/index.d.ts +51 -0
  13. package/build/adapters/antigravity/index.js +169 -0
  14. package/build/adapters/antigravity-cli/index.d.ts +51 -0
  15. package/build/adapters/antigravity-cli/index.js +342 -0
  16. package/build/adapters/base.d.ts +92 -0
  17. package/build/adapters/base.js +135 -0
  18. package/build/adapters/claude-code/config.d.ts +8 -0
  19. package/build/adapters/claude-code/config.js +8 -0
  20. package/build/adapters/claude-code/hooks.d.ts +106 -0
  21. package/build/adapters/claude-code/hooks.js +183 -0
  22. package/build/adapters/claude-code/index.d.ts +80 -0
  23. package/build/adapters/claude-code/index.js +569 -0
  24. package/build/adapters/claude-code-base.d.ts +49 -0
  25. package/build/adapters/claude-code-base.js +113 -0
  26. package/build/adapters/client-map.d.ts +10 -0
  27. package/build/adapters/client-map.js +42 -0
  28. package/build/adapters/codex/config.d.ts +8 -0
  29. package/build/adapters/codex/config.js +8 -0
  30. package/build/adapters/codex/hooks.d.ts +55 -0
  31. package/build/adapters/codex/hooks.js +64 -0
  32. package/build/adapters/codex/index.d.ts +87 -0
  33. package/build/adapters/codex/index.js +995 -0
  34. package/build/adapters/codex/paths.d.ts +1 -0
  35. package/build/adapters/codex/paths.js +12 -0
  36. package/build/adapters/codex/usage.d.ts +107 -0
  37. package/build/adapters/codex/usage.js +227 -0
  38. package/build/adapters/copilot-base.d.ts +94 -0
  39. package/build/adapters/copilot-base.js +297 -0
  40. package/build/adapters/copilot-cli/hooks.d.ts +33 -0
  41. package/build/adapters/copilot-cli/hooks.js +64 -0
  42. package/build/adapters/copilot-cli/index.d.ts +48 -0
  43. package/build/adapters/copilot-cli/index.js +341 -0
  44. package/build/adapters/cursor/config.d.ts +4 -0
  45. package/build/adapters/cursor/config.js +4 -0
  46. package/build/adapters/cursor/hooks.d.ts +55 -0
  47. package/build/adapters/cursor/hooks.js +82 -0
  48. package/build/adapters/cursor/index.d.ts +64 -0
  49. package/build/adapters/cursor/index.js +517 -0
  50. package/build/adapters/detect.d.ts +136 -0
  51. package/build/adapters/detect.js +623 -0
  52. package/build/adapters/gemini-cli/config.d.ts +8 -0
  53. package/build/adapters/gemini-cli/config.js +8 -0
  54. package/build/adapters/gemini-cli/hooks.d.ts +83 -0
  55. package/build/adapters/gemini-cli/hooks.js +115 -0
  56. package/build/adapters/gemini-cli/index.d.ts +80 -0
  57. package/build/adapters/gemini-cli/index.js +483 -0
  58. package/build/adapters/jetbrains-copilot/config.d.ts +8 -0
  59. package/build/adapters/jetbrains-copilot/config.js +8 -0
  60. package/build/adapters/jetbrains-copilot/hooks.d.ts +59 -0
  61. package/build/adapters/jetbrains-copilot/hooks.js +87 -0
  62. package/build/adapters/jetbrains-copilot/index.d.ts +31 -0
  63. package/build/adapters/jetbrains-copilot/index.js +131 -0
  64. package/build/adapters/kimi/config.d.ts +8 -0
  65. package/build/adapters/kimi/config.js +8 -0
  66. package/build/adapters/kimi/hooks.d.ts +28 -0
  67. package/build/adapters/kimi/hooks.js +34 -0
  68. package/build/adapters/kimi/index.d.ts +66 -0
  69. package/build/adapters/kimi/index.js +537 -0
  70. package/build/adapters/kimi/paths.d.ts +21 -0
  71. package/build/adapters/kimi/paths.js +52 -0
  72. package/build/adapters/kimi/usage.d.ts +82 -0
  73. package/build/adapters/kimi/usage.js +217 -0
  74. package/build/adapters/kiro/hooks.d.ts +61 -0
  75. package/build/adapters/kiro/hooks.js +94 -0
  76. package/build/adapters/kiro/index.d.ts +61 -0
  77. package/build/adapters/kiro/index.js +301 -0
  78. package/build/adapters/omp/index.d.ts +65 -0
  79. package/build/adapters/omp/index.js +182 -0
  80. package/build/adapters/omp/plugin.d.ts +81 -0
  81. package/build/adapters/omp/plugin.js +331 -0
  82. package/build/adapters/omp/usage.d.ts +49 -0
  83. package/build/adapters/omp/usage.js +110 -0
  84. package/build/adapters/openclaw/config.d.ts +8 -0
  85. package/build/adapters/openclaw/config.js +8 -0
  86. package/build/adapters/openclaw/hooks.d.ts +50 -0
  87. package/build/adapters/openclaw/hooks.js +61 -0
  88. package/build/adapters/openclaw/index.d.ts +74 -0
  89. package/build/adapters/openclaw/index.js +439 -0
  90. package/build/adapters/openclaw/mcp-tools.d.ts +54 -0
  91. package/build/adapters/openclaw/mcp-tools.js +207 -0
  92. package/build/adapters/openclaw/plugin.d.ts +141 -0
  93. package/build/adapters/openclaw/plugin.js +818 -0
  94. package/build/adapters/openclaw/session-db.d.ts +55 -0
  95. package/build/adapters/openclaw/session-db.js +88 -0
  96. package/build/adapters/openclaw/usage.d.ts +34 -0
  97. package/build/adapters/openclaw/usage.js +52 -0
  98. package/build/adapters/openclaw/workspace-router.d.ts +29 -0
  99. package/build/adapters/openclaw/workspace-router.js +64 -0
  100. package/build/adapters/opencode/config.d.ts +8 -0
  101. package/build/adapters/opencode/config.js +8 -0
  102. package/build/adapters/opencode/hooks.d.ts +38 -0
  103. package/build/adapters/opencode/hooks.js +50 -0
  104. package/build/adapters/opencode/index.d.ts +144 -0
  105. package/build/adapters/opencode/index.js +638 -0
  106. package/build/adapters/opencode/plugin.d.ts +249 -0
  107. package/build/adapters/opencode/plugin.js +1536 -0
  108. package/build/adapters/opencode/v2.d.ts +117 -0
  109. package/build/adapters/opencode/v2.js +224 -0
  110. package/build/adapters/opencode/zod3tov4.d.ts +1 -0
  111. package/build/adapters/opencode/zod3tov4.js +132 -0
  112. package/build/adapters/pi/extension.d.ts +81 -0
  113. package/build/adapters/pi/extension.js +861 -0
  114. package/build/adapters/pi/index.d.ts +57 -0
  115. package/build/adapters/pi/index.js +173 -0
  116. package/build/adapters/pi/mcp-bridge.d.ts +268 -0
  117. package/build/adapters/pi/mcp-bridge.js +894 -0
  118. package/build/adapters/qwen-code/hooks.d.ts +26 -0
  119. package/build/adapters/qwen-code/hooks.js +29 -0
  120. package/build/adapters/qwen-code/index.d.ts +40 -0
  121. package/build/adapters/qwen-code/index.js +361 -0
  122. package/build/adapters/qwen-code/usage.d.ts +90 -0
  123. package/build/adapters/qwen-code/usage.js +222 -0
  124. package/build/adapters/types.d.ts +360 -0
  125. package/build/adapters/types.js +118 -0
  126. package/build/adapters/vscode-copilot/config.d.ts +8 -0
  127. package/build/adapters/vscode-copilot/config.js +8 -0
  128. package/build/adapters/vscode-copilot/hooks.d.ts +71 -0
  129. package/build/adapters/vscode-copilot/hooks.js +98 -0
  130. package/build/adapters/vscode-copilot/index.d.ts +32 -0
  131. package/build/adapters/vscode-copilot/index.js +222 -0
  132. package/build/adapters/zed/index.d.ts +40 -0
  133. package/build/adapters/zed/index.js +163 -0
  134. package/build/cli.d.ts +34 -0
  135. package/build/cli.js +1828 -0
  136. package/build/db-base.d.ts +250 -0
  137. package/build/db-base.js +756 -0
  138. package/build/executor.d.ts +72 -0
  139. package/build/executor.js +678 -0
  140. package/build/exit-classify.d.ts +19 -0
  141. package/build/exit-classify.js +12 -0
  142. package/build/fetch/blocks.d.ts +145 -0
  143. package/build/fetch/blocks.js +244 -0
  144. package/build/fetch/extract.d.ts +68 -0
  145. package/build/fetch/extract.js +123 -0
  146. package/build/fetch/page-store.d.ts +66 -0
  147. package/build/fetch/page-store.js +174 -0
  148. package/build/fetch-cache.d.ts +13 -0
  149. package/build/fetch-cache.js +15 -0
  150. package/build/lifecycle.d.ts +124 -0
  151. package/build/lifecycle.js +278 -0
  152. package/build/package-identity.d.ts +26 -0
  153. package/build/package-identity.js +28 -0
  154. package/build/runPool.d.ts +36 -0
  155. package/build/runPool.js +51 -0
  156. package/build/runtime.d.ts +71 -0
  157. package/build/runtime.js +659 -0
  158. package/build/search/auto-memory.d.ts +47 -0
  159. package/build/search/auto-memory.js +170 -0
  160. package/build/search/ctx-search-schema.d.ts +90 -0
  161. package/build/search/ctx-search-schema.js +135 -0
  162. package/build/search/flood-guard.d.ts +57 -0
  163. package/build/search/flood-guard.js +80 -0
  164. package/build/search/unified.d.ts +56 -0
  165. package/build/search/unified.js +104 -0
  166. package/build/security.d.ts +206 -0
  167. package/build/security.js +780 -0
  168. package/build/server.d.ts +240 -0
  169. package/build/server.js +5034 -0
  170. package/build/session/analytics.d.ts +773 -0
  171. package/build/session/analytics.js +2420 -0
  172. package/build/session/db.d.ts +468 -0
  173. package/build/session/db.js +1330 -0
  174. package/build/session/error-classifier.d.ts +87 -0
  175. package/build/session/error-classifier.js +303 -0
  176. package/build/session/event-emit.d.ts +48 -0
  177. package/build/session/event-emit.js +101 -0
  178. package/build/session/extract.d.ts +333 -0
  179. package/build/session/extract.js +2688 -0
  180. package/build/session/model-prices.json +429 -0
  181. package/build/session/persist-tool-calls.d.ts +54 -0
  182. package/build/session/persist-tool-calls.js +105 -0
  183. package/build/session/pricing.d.ts +64 -0
  184. package/build/session/pricing.js +151 -0
  185. package/build/session/project-attribution.d.ts +73 -0
  186. package/build/session/project-attribution.js +245 -0
  187. package/build/session/purge.d.ts +138 -0
  188. package/build/session/purge.js +240 -0
  189. package/build/session/retrieval-marker.d.ts +39 -0
  190. package/build/session/retrieval-marker.js +65 -0
  191. package/build/session/snapshot.d.ts +46 -0
  192. package/build/session/snapshot.js +532 -0
  193. package/build/store-directory.d.ts +56 -0
  194. package/build/store-directory.js +254 -0
  195. package/build/store.d.ts +147 -0
  196. package/build/store.js +1714 -0
  197. package/build/truncate.d.ts +57 -0
  198. package/build/truncate.js +142 -0
  199. package/build/types.d.ts +117 -0
  200. package/build/types.js +20 -0
  201. package/build/util/claude-config.d.ts +32 -0
  202. package/build/util/claude-config.js +84 -0
  203. package/build/util/hook-config.d.ts +27 -0
  204. package/build/util/hook-config.js +76 -0
  205. package/build/util/jsonc.d.ts +14 -0
  206. package/build/util/jsonc.js +104 -0
  207. package/build/util/plugin-cache-integrity.d.ts +51 -0
  208. package/build/util/plugin-cache-integrity.js +146 -0
  209. package/build/util/project-dir.d.ts +149 -0
  210. package/build/util/project-dir.js +340 -0
  211. package/build/util/sibling-mcp.d.ts +79 -0
  212. package/build/util/sibling-mcp.js +181 -0
  213. package/cli.bundle.mjs +1495 -0
  214. package/configs/antigravity/GEMINI.md +68 -0
  215. package/configs/antigravity/mcp_config.json +7 -0
  216. package/configs/antigravity-cli/hooks/hooks.json +37 -0
  217. package/configs/antigravity-cli/hooks.json +37 -0
  218. package/configs/antigravity-cli/mcp_config.json +10 -0
  219. package/configs/antigravity-cli/plugin.json +14 -0
  220. package/configs/antigravity-cli/rules/context-mode.md +77 -0
  221. package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
  222. package/configs/claude-code/CLAUDE.md +91 -0
  223. package/configs/codex/AGENTS.md +99 -0
  224. package/configs/codex/config.toml +8 -0
  225. package/configs/codex/hooks.json +47 -0
  226. package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
  227. package/configs/copilot-cli/.mcp.json +12 -0
  228. package/configs/copilot-cli/README.md +47 -0
  229. package/configs/copilot-cli/hooks.json +41 -0
  230. package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
  231. package/configs/cursor/context-mode.mdc +76 -0
  232. package/configs/cursor/hooks.json +21 -0
  233. package/configs/cursor/mcp.json +7 -0
  234. package/configs/gemini-cli/GEMINI.md +88 -0
  235. package/configs/gemini-cli/mcp.json +7 -0
  236. package/configs/gemini-cli/settings.json +60 -0
  237. package/configs/jetbrains-copilot/copilot-instructions.md +80 -0
  238. package/configs/jetbrains-copilot/hooks.json +16 -0
  239. package/configs/jetbrains-copilot/mcp.json +7 -0
  240. package/configs/kilo/AGENTS.md +84 -0
  241. package/configs/kilo/kilo.json +6 -0
  242. package/configs/kimi/hooks.json +54 -0
  243. package/configs/kiro/KIRO.md +84 -0
  244. package/configs/kiro/agent.json +18 -0
  245. package/configs/kiro/mcp.json +7 -0
  246. package/configs/omp/SYSTEM.md +85 -0
  247. package/configs/omp/mcp.json +7 -0
  248. package/configs/openclaw/AGENTS.md +87 -0
  249. package/configs/openclaw/openclaw.json +13 -0
  250. package/configs/opencode/AGENTS.md +84 -0
  251. package/configs/opencode/opencode.json +6 -0
  252. package/configs/pi/AGENTS.md +3 -0
  253. package/configs/qwen-code/QWEN.md +91 -0
  254. package/configs/vscode-copilot/copilot-instructions.md +80 -0
  255. package/configs/vscode-copilot/hooks.json +16 -0
  256. package/configs/vscode-copilot/mcp.json +7 -0
  257. package/configs/zed/AGENTS.md +68 -0
  258. package/hooks/antigravity-cli/payload.mjs +98 -0
  259. package/hooks/antigravity-cli/posttooluse.mjs +138 -0
  260. package/hooks/antigravity-cli/pretooluse.mjs +78 -0
  261. package/hooks/antigravity-cli/stop.mjs +58 -0
  262. package/hooks/auto-injection.mjs +102 -0
  263. package/hooks/cache-heal-utils.mjs +379 -0
  264. package/hooks/codex/platform.mjs +1 -0
  265. package/hooks/codex/posttooluse.mjs +66 -0
  266. package/hooks/codex/precompact.mjs +70 -0
  267. package/hooks/codex/pretooluse.mjs +42 -0
  268. package/hooks/codex/sessionstart.mjs +121 -0
  269. package/hooks/codex/stop.mjs +138 -0
  270. package/hooks/codex/userpromptsubmit.mjs +75 -0
  271. package/hooks/copilot-cli/posttooluse.mjs +79 -0
  272. package/hooks/copilot-cli/precompact.mjs +66 -0
  273. package/hooks/copilot-cli/pretooluse.mjs +41 -0
  274. package/hooks/copilot-cli/sessionstart.mjs +121 -0
  275. package/hooks/copilot-cli/stop.mjs +59 -0
  276. package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
  277. package/hooks/core/codex-caps.mjs +112 -0
  278. package/hooks/core/formatters.mjs +356 -0
  279. package/hooks/core/mcp-ready.mjs +109 -0
  280. package/hooks/core/platform-detect.mjs +49 -0
  281. package/hooks/core/routing.mjs +1050 -0
  282. package/hooks/core/stdin.mjs +90 -0
  283. package/hooks/core/tool-naming.mjs +57 -0
  284. package/hooks/cursor/afteragentresponse.mjs +74 -0
  285. package/hooks/cursor/hooks.json +31 -0
  286. package/hooks/cursor/posttooluse.mjs +72 -0
  287. package/hooks/cursor/pretooluse.mjs +26 -0
  288. package/hooks/cursor/sessionstart.mjs +97 -0
  289. package/hooks/cursor/stop.mjs +48 -0
  290. package/hooks/ensure-deps.mjs +254 -0
  291. package/hooks/formatters/claude-code.mjs +113 -0
  292. package/hooks/formatters/cursor.mjs +37 -0
  293. package/hooks/formatters/gemini-cli.mjs +55 -0
  294. package/hooks/formatters/vscode-copilot.mjs +55 -0
  295. package/hooks/gemini-cli/aftermodel.mjs +70 -0
  296. package/hooks/gemini-cli/aftertool.mjs +61 -0
  297. package/hooks/gemini-cli/beforeagent.mjs +99 -0
  298. package/hooks/gemini-cli/beforetool.mjs +27 -0
  299. package/hooks/gemini-cli/precompress.mjs +55 -0
  300. package/hooks/gemini-cli/sessionstart.mjs +137 -0
  301. package/hooks/heal-partial-install.mjs +712 -0
  302. package/hooks/hooks.json +143 -0
  303. package/hooks/jetbrains-copilot/posttooluse.mjs +61 -0
  304. package/hooks/jetbrains-copilot/precompact.mjs +55 -0
  305. package/hooks/jetbrains-copilot/pretooluse.mjs +27 -0
  306. package/hooks/jetbrains-copilot/sessionstart.mjs +124 -0
  307. package/hooks/kimi/platform.mjs +1 -0
  308. package/hooks/kimi/posttooluse.mjs +72 -0
  309. package/hooks/kimi/precompact.mjs +80 -0
  310. package/hooks/kimi/pretooluse.mjs +42 -0
  311. package/hooks/kimi/sessionend.mjs +61 -0
  312. package/hooks/kimi/sessionstart.mjs +113 -0
  313. package/hooks/kimi/stop.mjs +132 -0
  314. package/hooks/kimi/userpromptsubmit.mjs +90 -0
  315. package/hooks/kiro/agentspawn.mjs +97 -0
  316. package/hooks/kiro/posttooluse.mjs +51 -0
  317. package/hooks/kiro/pretooluse.mjs +64 -0
  318. package/hooks/kiro/userpromptsubmit.mjs +88 -0
  319. package/hooks/normalize-hooks.mjs +323 -0
  320. package/hooks/platform-bridge.mjs +340 -0
  321. package/hooks/posttooluse.mjs +225 -0
  322. package/hooks/precompact.mjs +100 -0
  323. package/hooks/pretooluse.mjs +227 -0
  324. package/hooks/qwen-code/platform.mjs +1 -0
  325. package/hooks/qwen-code/stop.mjs +168 -0
  326. package/hooks/routing-block.mjs +105 -0
  327. package/hooks/run-hook.mjs +95 -0
  328. package/hooks/security.bundle.mjs +2 -0
  329. package/hooks/session-attribution.bundle.mjs +1 -0
  330. package/hooks/session-db.bundle.mjs +145 -0
  331. package/hooks/session-directive.mjs +520 -0
  332. package/hooks/session-extract.bundle.mjs +3 -0
  333. package/hooks/session-helpers.mjs +428 -0
  334. package/hooks/session-loaders.mjs +448 -0
  335. package/hooks/session-snapshot.bundle.mjs +32 -0
  336. package/hooks/sessionstart.mjs +464 -0
  337. package/hooks/stop.mjs +82 -0
  338. package/hooks/suppress-stderr.mjs +25 -0
  339. package/hooks/userpromptsubmit.mjs +108 -0
  340. package/hooks/vscode-copilot/posttooluse.mjs +61 -0
  341. package/hooks/vscode-copilot/precompact.mjs +55 -0
  342. package/hooks/vscode-copilot/pretooluse.mjs +27 -0
  343. package/hooks/vscode-copilot/sessionstart.mjs +129 -0
  344. package/openclaw.plugin.json +38 -0
  345. package/package.json +132 -0
  346. package/scripts/heal-better-sqlite3.mjs +430 -0
  347. package/scripts/heal-installed-plugins.mjs +635 -0
  348. package/scripts/plugin-cache-integrity.mjs +248 -0
  349. package/scripts/postinstall.mjs +397 -0
  350. package/server.bundle.mjs +1421 -0
  351. package/server.js +5 -0
  352. package/skills/.ignore +7 -0
  353. package/skills/context-mode/SKILL.md +300 -0
  354. package/skills/context-mode/references/anti-patterns.md +283 -0
  355. package/skills/context-mode/references/patterns-javascript.md +298 -0
  356. package/skills/context-mode/references/patterns-python.md +304 -0
  357. package/skills/context-mode/references/patterns-shell.md +277 -0
  358. package/skills/ctx-doctor/SKILL.md +22 -0
  359. package/skills/ctx-index/SKILL.md +46 -0
  360. package/skills/ctx-insight/SKILL.md +24 -0
  361. package/skills/ctx-purge/SKILL.md +49 -0
  362. package/skills/ctx-search/SKILL.md +35 -0
  363. package/skills/ctx-stats/SKILL.md +26 -0
  364. package/skills/ctx-upgrade/SKILL.md +31 -0
  365. package/start.mjs +616 -0
@@ -0,0 +1,773 @@
1
+ /**
2
+ * AnalyticsEngine — Runtime savings + session continuity reporting.
3
+ *
4
+ * Computes context-window savings from runtime stats and queries
5
+ * session continuity data from SessionDB.
6
+ *
7
+ * Usage:
8
+ * const engine = new AnalyticsEngine(sessionDb);
9
+ * const report = engine.queryAll(runtimeStats);
10
+ */
11
+ /** Database adapter — anything with a prepare() method (better-sqlite3, bun:sqlite, etc.) */
12
+ export interface DatabaseAdapter {
13
+ prepare(sql: string): {
14
+ run(...params: unknown[]): unknown;
15
+ get(...params: unknown[]): unknown;
16
+ all(...params: unknown[]): unknown[];
17
+ };
18
+ }
19
+ /** Context savings result (#1) */
20
+ export interface ContextSavings {
21
+ rawBytes: number;
22
+ contextBytes: number;
23
+ savedBytes: number;
24
+ savedPercent: number;
25
+ }
26
+ /** Think in code comparison result (#2) */
27
+ export interface ThinkInCodeComparison {
28
+ fileBytes: number;
29
+ outputBytes: number;
30
+ ratio: number;
31
+ }
32
+ /** Tool-level savings result (#3) */
33
+ export interface ToolSavingsRow {
34
+ tool: string;
35
+ rawBytes: number;
36
+ contextBytes: number;
37
+ savedBytes: number;
38
+ }
39
+ /** Sandbox I/O result (#19) */
40
+ export interface SandboxIO {
41
+ inputBytes: number;
42
+ outputBytes: number;
43
+ }
44
+ /** MCP tool usage row — concurrency stats for batch-style tools. */
45
+ export interface McpToolUsageRow {
46
+ tool_name: string;
47
+ calls: number;
48
+ median_concurrency: number | null;
49
+ max_concurrency: number | null;
50
+ }
51
+ /**
52
+ * Conversation-scoped stats — aggregated from `session_events` for a single
53
+ * `session_id` across every worktree DB plus the compact-rescue snapshot from
54
+ * `session_resume`. Replaces the broken in-memory `tool_call_counter` that
55
+ * only saw `ctx_*` MCP calls and reset to 0 every time the MCP server PID
56
+ * changed (which is what made hours-of-work conversations show "1 call · 5 KB").
57
+ */
58
+ export interface ConversationStats {
59
+ /** session_id this aggregate covers (the current Claude Code conversation). */
60
+ sessionId: string;
61
+ /** Total event count for this session_id, summed across all DBs. */
62
+ events: number;
63
+ /** Distinct DB files this session_id appeared in (a rotation indicator). */
64
+ dbCount: number;
65
+ /** Wall-clock days from first to last event. Captures real activity length. */
66
+ daysAlive: number;
67
+ /** Bytes restored from the compact snapshot for this session_id. 0 if no compact. */
68
+ snapshotBytes: number;
69
+ /** Number of compact snapshots consumed for this session_id. */
70
+ snapshotsConsumed: number;
71
+ /** Category breakdown for this session_id. */
72
+ byCategory: Array<{
73
+ category: string;
74
+ count: number;
75
+ label: string;
76
+ }>;
77
+ /**
78
+ * Earliest event timestamp (ms epoch) for this session_id across every DB.
79
+ * Used by the section-1 "started X" line in the narrative renderer. 0 when
80
+ * the session has no events yet. Optional for back-compat with older
81
+ * callers / fixtures that pre-date the narrative layout.
82
+ */
83
+ firstEventMs?: number;
84
+ /** Latest event timestamp (ms epoch) — pairs with firstEventMs. */
85
+ lastEventMs?: number;
86
+ /**
87
+ * Wall-clock timestamp of the most recent /compact rescue for this session.
88
+ * Drives the "On <datetime>, /compact fired" line in section 1. Undefined
89
+ * when the conversation has never been compacted.
90
+ */
91
+ lastRescueMs?: number;
92
+ /**
93
+ * Per-day capture breakdown for the section-1 horizontal timeline. Each
94
+ * entry is one calendar day (UTC midnight ms) with that day's event count
95
+ * + optional rescueBytes when /compact fired on that day. Empty array
96
+ * when no events recorded yet.
97
+ */
98
+ byDay?: Array<{
99
+ ms: number;
100
+ count: number;
101
+ rescueBytes?: number;
102
+ }>;
103
+ }
104
+ /** Runtime stats tracked by server.ts during a live session. */
105
+ export interface RuntimeStats {
106
+ bytesReturned: Record<string, number>;
107
+ bytesIndexed: number;
108
+ bytesSandboxed: number;
109
+ calls: Record<string, number>;
110
+ sessionStart: number;
111
+ cacheHits: number;
112
+ cacheMisses?: number;
113
+ cacheBytesSaved: number;
114
+ }
115
+ /**
116
+ * Index observability snapshot — point-in-time view of the persistent
117
+ * content store. Optional input to `formatReport` so callers that don't
118
+ * have store access (or don't want the extra DB hit) can omit it.
119
+ */
120
+ export interface IndexState {
121
+ totalChunks: number;
122
+ totalSources: number;
123
+ lastIndexedAt?: string;
124
+ }
125
+ /** Unified report combining runtime stats, DB analytics, and continuity data. */
126
+ export interface FullReport {
127
+ /** Runtime context savings (passed in, not from DB) */
128
+ savings: {
129
+ processed_kb: number;
130
+ entered_kb: number;
131
+ saved_kb: number;
132
+ pct: number;
133
+ savings_ratio: number;
134
+ by_tool: Array<{
135
+ tool: string;
136
+ calls: number;
137
+ context_kb: number;
138
+ tokens: number;
139
+ }>;
140
+ total_calls: number;
141
+ total_bytes_returned: number;
142
+ kept_out: number;
143
+ total_processed: number;
144
+ };
145
+ cache?: {
146
+ hits: number;
147
+ misses: number;
148
+ hit_rate: number;
149
+ bytes_saved: number;
150
+ ttl_hours_left: number;
151
+ total_with_cache: number;
152
+ total_savings_ratio: number;
153
+ };
154
+ /** Session metadata from SessionDB */
155
+ session: {
156
+ id: string;
157
+ uptime_min: string;
158
+ };
159
+ /** Session continuity data */
160
+ continuity: {
161
+ total_events: number;
162
+ by_category: Array<{
163
+ category: string;
164
+ count: number;
165
+ label: string;
166
+ preview: string;
167
+ why: string;
168
+ }>;
169
+ compact_count: number;
170
+ resume_ready: boolean;
171
+ };
172
+ /** Persistent project memory — all events across all sessions */
173
+ projectMemory: {
174
+ total_events: number;
175
+ session_count: number;
176
+ by_category: Array<{
177
+ category: string;
178
+ count: number;
179
+ label: string;
180
+ }>;
181
+ };
182
+ }
183
+ /**
184
+ * Human-readable labels for event categories.
185
+ *
186
+ * Each label is a sentence-case phrase that reads like a benefit, not a
187
+ * column name. The user shouldn't see raw schema words like "external-ref"
188
+ * or "agent-finding" — those leak the database into the UX. When a new
189
+ * category lands without an entry here, the renderer falls through to the
190
+ * raw category id; that's a copy-debt signal, fix it here.
191
+ */
192
+ export declare const categoryLabels: Record<string, string>;
193
+ /** Explains why each category matters for continuity. */
194
+ export declare const categoryHints: Record<string, string>;
195
+ export declare class AnalyticsEngine {
196
+ private readonly db;
197
+ /**
198
+ * Create an AnalyticsEngine.
199
+ *
200
+ * Accepts either a SessionDB instance (extracts internal db via
201
+ * the protected getter — use the static fromDB helper for raw adapters)
202
+ * or any object with a prepare() method for direct usage.
203
+ */
204
+ constructor(db: DatabaseAdapter);
205
+ /**
206
+ * #1 Context Savings Total — bytes kept out of context window.
207
+ *
208
+ * Stub: requires server.ts to accumulate rawBytes and contextBytes
209
+ * during a live session. Call with tracked values.
210
+ */
211
+ static contextSavingsTotal(rawBytes: number, contextBytes: number): ContextSavings;
212
+ /**
213
+ * #2 Think in Code Comparison — ratio of file size to sandbox output size.
214
+ *
215
+ * Stub: requires server.ts tracking of execute/execute_file calls.
216
+ */
217
+ static thinkInCodeComparison(fileBytes: number, outputBytes: number): ThinkInCodeComparison;
218
+ /**
219
+ * #3 Tool Savings — per-tool breakdown of context savings.
220
+ *
221
+ * Stub: requires per-tool accumulators in server.ts.
222
+ */
223
+ static toolSavings(tools: Array<{
224
+ tool: string;
225
+ rawBytes: number;
226
+ contextBytes: number;
227
+ }>): ToolSavingsRow[];
228
+ /**
229
+ * #19 Sandbox I/O — total input/output bytes processed by the sandbox.
230
+ *
231
+ * Stub: requires PolyglotExecutor byte counters.
232
+ */
233
+ static sandboxIO(inputBytes: number, outputBytes: number): SandboxIO;
234
+ /**
235
+ * MCP tool usage — call counts and concurrency stats per MCP tool.
236
+ *
237
+ * Reads `mcp_tool_call` events, parses the JSON payload, and aggregates:
238
+ * - call count per tool_name
239
+ * - median + max of `params.concurrency` (only for tools that take it,
240
+ * e.g. ctx_batch_execute, ctx_fetch_and_index). Returns null when the
241
+ * tool doesn't carry a concurrency param so callers can render N/A.
242
+ *
243
+ * Best-effort: malformed rows or truncated payloads are skipped silently.
244
+ */
245
+ getMcpToolUsage(): McpToolUsageRow[];
246
+ /**
247
+ * Build a FullReport by merging runtime stats (passed in)
248
+ * with continuity data from the DB.
249
+ *
250
+ * This is the ONE call that ctx_stats should use.
251
+ */
252
+ queryAll(runtimeStats: RuntimeStats): FullReport;
253
+ }
254
+ /**
255
+ * Where one adapter stores its context-mode sidecars on disk. Mirrors the
256
+ * map in `src/adapters/detect.ts:92-111` (`getSessionDirSegments`) so we
257
+ * never go out of sync as a single source of truth.
258
+ *
259
+ * `sessionsDir` = `<home>/<segments>/context-mode/sessions`
260
+ * `contentDir` = `<home>/<segments>/context-mode/content`
261
+ *
262
+ * Why duplicated here: `getSessionDirSegments` returns segments relative to
263
+ * `homedir()`; analytics needs the absolute joined paths for both `sessions`
264
+ * and `content` siblings. Keeping a parallel hard-coded list avoids importing
265
+ * detect.ts (which pulls in adapter loaders) into the stats path.
266
+ */
267
+ export interface AdapterDirEntry {
268
+ /** Adapter id matching `src/adapters/detect.ts` PlatformId. */
269
+ name: string;
270
+ /** Absolute path to `<home>/<segments>/context-mode/sessions`. */
271
+ sessionsDir: string;
272
+ /** Absolute path to `<home>/<segments>/context-mode/content`. */
273
+ contentDir: string;
274
+ }
275
+ /**
276
+ * Enumerate every known adapter's sessions + content dirs under `home`.
277
+ * Used by `getMultiAdapterLifetimeStats` and `getMultiAdapterRealBytesStats`
278
+ * so a single call surfaces "your work everywhere on this machine across
279
+ * all AI tools" (the marketing line).
280
+ *
281
+ * Returns ALL 17 adapters even when the dir doesn't exist on disk — the
282
+ * scanner functions filter to existing dirs. That keeps the enumeration
283
+ * pure / testable without filesystem dependencies.
284
+ */
285
+ export declare function enumerateAdapterDirs(opts?: {
286
+ home?: string;
287
+ }): AdapterDirEntry[];
288
+ /** Aggregated stats spanning every SessionDB + auto-memory under the user's profile. */
289
+ export interface LifetimeStats {
290
+ totalEvents: number;
291
+ totalSessions: number;
292
+ autoMemoryCount: number;
293
+ autoMemoryProjects: number;
294
+ /** Per-prefix breakdown of auto-memory files (user/feedback/project/...). */
295
+ autoMemoryByPrefix: Record<string, number>;
296
+ /**
297
+ * Per-category event counts aggregated across every SessionDB on disk.
298
+ * Keys are the raw category strings (file/cwd/rule/...) — the renderer
299
+ * looks them up against `categoryLabels` for display. Empty `{}` when no
300
+ * sidecar has any events. Optional for back-compat with older fixtures.
301
+ */
302
+ categoryCounts: Record<string, number>;
303
+ /**
304
+ * Total bytes restored from compact-rescue snapshots across every DB on
305
+ * disk. Adds the rescue benefit to lifetime $ so the headline isn't
306
+ * silently undercounting the killer feature. 0 when no compact has fired
307
+ * or older fixtures don't pass this. Optional for back-compat with tests.
308
+ */
309
+ rescueBytes?: number;
310
+ /**
311
+ * Earliest event timestamp (ms epoch) across every DB. Used for the
312
+ * "since 2026-04-14" lifetime narrative. 0 when unknown. Optional.
313
+ */
314
+ firstEventMs?: number;
315
+ /**
316
+ * Distinct project_dir count across every DB. Different from
317
+ * `autoMemoryProjects` (which only counts dirs with auto-memory files).
318
+ * Captures every cwd context-mode has ever seen events for. Optional.
319
+ */
320
+ distinctProjects?: number;
321
+ }
322
+ /**
323
+ * Aggregate lifetime stats from all SessionDB files in `sessionsDir` and
324
+ * all auto-memory markdown files under `memoryRoot/<project>/memory/`.
325
+ *
326
+ * Best-effort: silently ignores missing/unreadable files so ctx_stats
327
+ * can never be broken by a corrupt sidecar.
328
+ */
329
+ export declare function getLifetimeStats(opts?: {
330
+ sessionsDir?: string;
331
+ memoryRoot?: string;
332
+ /** Override for tests — defaults to db-base loadDatabase(). */
333
+ loadDatabase?: () => unknown;
334
+ }): LifetimeStats;
335
+ /**
336
+ * Aggregate every event for one `session_id` across all SessionDB files in
337
+ * `sessionsDir` plus the compact-rescue snapshot bytes from `session_resume`.
338
+ *
339
+ * Why this exists: the Claude Code session_id can persist across days while
340
+ * the underlying DB file rotates (size cap), and a compact-rescue snapshot
341
+ * carries hundreds of KB of context that would otherwise have been lost. The
342
+ * old in-memory `tool_call_counter` saw none of this — it counted only `ctx_*`
343
+ * MCP calls against the current MCP server PID and reset on every restart.
344
+ * Reading from `session_events` + `session_resume` is the source-of-truth
345
+ * version that matches what users actually experienced.
346
+ */
347
+ export declare function getConversationStats(opts: {
348
+ sessionId: string;
349
+ sessionsDir?: string;
350
+ /** Optional worktree filename prefix (sha256(cwd)[:16]). When omitted, scans every DB. */
351
+ worktreeHash?: string;
352
+ loadDatabase?: () => unknown;
353
+ }): ConversationStats;
354
+ /**
355
+ * Real-bytes counter the renderer uses to replace the conservative
356
+ * `events × 256` token estimate. Reads four sources from disk and
357
+ * returns the sum the renderer divides by 4 to get tokens.
358
+ *
359
+ * - `eventDataBytes` = SUM(LENGTH(data)) FROM session_events
360
+ * - `bytesAvoided` = SUM(bytes_avoided) FROM session_events
361
+ * - `bytesReturned` = SUM(bytes_returned) FROM session_events
362
+ * - `snapshotBytes` = SUM(LENGTH(snapshot)) FROM session_resume
363
+ * - `totalSavedTokens` = (eventDataBytes + bytesAvoided + snapshotBytes) / 4
364
+ *
365
+ * `bytesReturned` is reported but NOT folded into `totalSavedTokens`
366
+ * because it represents bytes the model already paid for — adding it
367
+ * would double-count what's already on the user's invoice.
368
+ */
369
+ export interface RealBytesStats {
370
+ eventDataBytes: number;
371
+ bytesAvoided: number;
372
+ bytesReturned: number;
373
+ snapshotBytes: number;
374
+ /**
375
+ * v1.0.133 Slice 3: bytes attributed to this session in the FTS5 content
376
+ * DB — `SUM(LENGTH(title) + LENGTH(content)) FROM chunks WHERE session_id = ?`.
377
+ *
378
+ * Read-only, render-time computation. Populated only when
379
+ * `getRealBytesStats` is called with both `sessionId` AND `contentDbPath`
380
+ * (i.e. the conversation tier from ctx_stats). Lifetime / project tiers
381
+ * leave this at 0 — aggregating across every adapter's content DB is a
382
+ * separate concern.
383
+ *
384
+ * Legacy chunks with empty `session_id` (pre-Slice-1) are NOT backfilled:
385
+ * the architect rejected the time-window join as unsafe. Old conversations
386
+ * stay low; new conversations populate honestly.
387
+ */
388
+ contentBytes: number;
389
+ totalSavedTokens: number;
390
+ }
391
+ /**
392
+ * v1.0.133 Slice 3: Sum the bytes attributed to one session in the FTS5
393
+ * content DB.
394
+ *
395
+ * Returns `LENGTH(title) + LENGTH(content)` summed across every chunk
396
+ * whose `session_id` column matches `sessionId`. Best-effort — returns 0
397
+ * when the DB file is missing, the schema lacks the `session_id` column
398
+ * (pre-Slice-1 content DBs), or the query fails. Never throws.
399
+ *
400
+ * Render-time only. Does NOT mutate the content DB. Architect-approved
401
+ * because the read-only join carries no risk of cross-session attribution
402
+ * (the FK was set at chunk insert time by Slice 1).
403
+ */
404
+ export declare function getContentBytesForSession(sessionId: string, contentDbPath: string, opts?: {
405
+ loadDatabase?: () => unknown;
406
+ }): number;
407
+ /**
408
+ * v1.0.134 SLICE C — lifetime tier all-chunks aggregate.
409
+ *
410
+ * Sibling of {@link getContentBytesForSession} that omits the session_id
411
+ * filter so the lifetime tier sees every chunk in the content store —
412
+ * including legacy unattributed rows (sessionId === '') and chunks
413
+ * attributed to other adapters' sessions. Without this, the lifetime
414
+ * "kept out" headline only counts session_events.bytes_avoided and
415
+ * misses the bulk of indexed payload.
416
+ *
417
+ * Best-effort: returns 0 when the DB file is missing, the schema lacks
418
+ * the `chunks` table, or the query fails. Never throws — same contract
419
+ * as the rest of the analytics module so a corrupt content DB cannot
420
+ * crash ctx_stats.
421
+ */
422
+ export declare function getContentBytesAllSessions(contentDbPath: string, opts?: {
423
+ loadDatabase?: () => unknown;
424
+ }): number;
425
+ /**
426
+ * Compute real-bytes stats across one session, one project (worktree
427
+ * filter), or every session on disk (lifetime).
428
+ *
429
+ * - Pass `sessionId` for the conversation tier.
430
+ * - Pass `worktreeHash` to filter `*.db` files by name prefix
431
+ * (per-project lifetime — `sha256(cwd).slice(0, 16)`).
432
+ * - Pass neither — full lifetime aggregate.
433
+ *
434
+ * Best-effort: returns zeroes when the dir is missing, the DB is
435
+ * corrupt, or the session has no events. Never throws — same
436
+ * contract as `getConversationStats` / `getLifetimeStats` so the
437
+ * stats-render path can never crash on a bad sidecar.
438
+ */
439
+ export declare function getRealBytesStats(opts: {
440
+ sessionId?: string;
441
+ sessionsDir?: string;
442
+ worktreeHash?: string;
443
+ /**
444
+ * v1.0.148 follow-up (Bug E+F): when set, the function aggregates across
445
+ * EVERY session whose `session_meta.project_dir` matches this value, not
446
+ * just one session_id. Resolves the per-conversation under-attribution:
447
+ * one Claude Code conversation typically spans many session_ids (resume
448
+ * cycles, /compact rebirths, PID sub-process sessions spawned by
449
+ * ctx_execute), so a single-session_id filter loses the sandbox-burst
450
+ * bytes_avoided that all live under the conversation's cwd.
451
+ *
452
+ * Uses a META subquery (`session_id IN (SELECT session_id FROM
453
+ * session_meta WHERE project_dir = ?)`), then sums ALL events for
454
+ * matching sessions regardless of their event-level project_dir
455
+ * (sandbox-burst events write `project_dir = ''` even when the
456
+ * META row carries the parent cwd — see Bug F).
457
+ *
458
+ * Mutually exclusive with `sessionId`. When both are set, `sessionId`
459
+ * wins for back-compat.
460
+ */
461
+ projectDir?: string;
462
+ /**
463
+ * v1.0.133 Slice 3: when set alongside `sessionId`, the function joins
464
+ * the FTS5 content DB at this path and folds chunk bytes into
465
+ * `bytesAvoided` + `totalSavedTokens` + `contentBytes`. Render-time
466
+ * only — no DB writes.
467
+ */
468
+ contentDbPath?: string;
469
+ loadDatabase?: () => unknown;
470
+ }): RealBytesStats;
471
+ /**
472
+ * v1.0.169 — Section 1 "Where you are now" = the LIVE conversation window.
473
+ *
474
+ * A single live conversation fans out into sub-agents and ctx_execute
475
+ * sub-process sessions. Each runs in its OWN, disposable context window (its
476
+ * own session_id) — but all under the SAME worktree DB, because the worktree
477
+ * hash is sha256(cwd) and they share the cwd. Their retrieval (ctx_search /
478
+ * ctx_fetch_and_index returns) entered THOSE windows and was thrown away when
479
+ * each returned its short summary; it never touched the window the user is
480
+ * reading now. So the live-window savings bar must split the worktree by
481
+ * which retrieval actually landed in the user's window:
482
+ *
483
+ * bytesReturned ("With context-mode") = THIS session's retrieval only —
484
+ * what genuinely entered the live window.
485
+ * bytesAvoided ("kept out") = everything the whole worktree moved
486
+ * (avoided + every session's retrieval) MINUS what landed in your window.
487
+ *
488
+ * Scoping by `worktreeHash` (not project-root + time) means the user's OTHER
489
+ * parallel worktrees never bleed in — a different worktree is a different
490
+ * cwd-hash, hence a different DB file the prefix filter excludes — while the
491
+ * sub-agent fan-out this conversation actually spawned is fully credited.
492
+ */
493
+ export declare function getConversationWindowStats(opts: {
494
+ sessionId: string;
495
+ worktreeHash: string;
496
+ sessionsDir?: string;
497
+ contentDbPath?: string;
498
+ }): RealBytesStats;
499
+ /**
500
+ * Real-usage filter thresholds. Decided in the B3a /diagnose conversation
501
+ * to suppress fixture-noise dirs (test runs that touched ~/.X but never
502
+ * carried real user work).
503
+ *
504
+ * An adapter is `isReal=true` iff ALL four hold:
505
+ * eventCount >= 100
506
+ * distinctProjects >= 5
507
+ * lastActivity within 30 days
508
+ * avgEventBytes >= 50
509
+ *
510
+ * Tuneable via `getMultiAdapterLifetimeStats({ filter })` for testing.
511
+ */
512
+ export interface RealUsageFilter {
513
+ minEvents?: number;
514
+ minProjects?: number;
515
+ recencyMs?: number;
516
+ minAvgBytes?: number;
517
+ /** Fixed "now" timestamp for deterministic testing. Defaults to Date.now(). */
518
+ nowMs?: number;
519
+ }
520
+ /** Per-adapter scan result returned by {@link scanOneAdapter}. */
521
+ export interface AdapterScanResult {
522
+ /** Adapter id (matches `enumerateAdapterDirs().name`). */
523
+ name: string;
524
+ /** Total event rows across every `*.db` in this adapter's sessions dir. */
525
+ eventCount: number;
526
+ /** Total distinct session_meta rows across every db. */
527
+ sessionCount: number;
528
+ /** Sum of LENGTH(data) across every session_event row. */
529
+ dataBytes: number;
530
+ /** Sum of LENGTH(snapshot) across consumed compact-rescue snapshots. */
531
+ rescueBytes: number;
532
+ /** Reserved for future content/ scan (B3b). 0 today. */
533
+ contentBytes: number;
534
+ /** Distinct session_id count across all dbs (alias of sessionCount). */
535
+ uuidConvs: number;
536
+ /** Distinct project_dir values across all session_events. */
537
+ projectDirs: string[];
538
+ /** Earliest event ms epoch (Number.POSITIVE_INFINITY when no events). */
539
+ firstMs: number;
540
+ /** Latest event ms epoch (0 when no events). */
541
+ lastMs: number;
542
+ /** Real-usage flag — see {@link RealUsageFilter}. */
543
+ isReal: boolean;
544
+ }
545
+ /** Aggregated multi-adapter lifetime stats. */
546
+ export interface MultiAdapterLifetimeStats {
547
+ /** Sum of eventCount across every adapter that exists on disk. */
548
+ totalEvents: number;
549
+ /** Sum of sessionCount across every adapter. */
550
+ totalSessions: number;
551
+ /** Sum of dataBytes + rescueBytes across every adapter. */
552
+ totalBytes: number;
553
+ /** Per-adapter rows for adapters that have >= one .db file. */
554
+ perAdapter: AdapterScanResult[];
555
+ }
556
+ /**
557
+ * Aggregate lifetime stats across every adapter dir under `home`.
558
+ * The marketing line — "your work everywhere on this machine across all
559
+ * AI tools" — depends on this. Existing `getLifetimeStats` (single dir)
560
+ * is untouched; this is purely additive.
561
+ */
562
+ export declare function getMultiAdapterLifetimeStats(opts?: {
563
+ home?: string;
564
+ loadDatabase?: () => unknown;
565
+ filter?: RealUsageFilter;
566
+ }): MultiAdapterLifetimeStats;
567
+ /** Aggregated multi-adapter real-bytes stats. */
568
+ export interface MultiAdapterRealBytesStats extends RealBytesStats {
569
+ /** Per-adapter row in the same shape as {@link RealBytesStats}, keyed by name. */
570
+ perAdapter: Array<RealBytesStats & {
571
+ name: string;
572
+ }>;
573
+ }
574
+ /**
575
+ * Aggregate real-bytes stats across every adapter dir under `home`.
576
+ * Mirrors `getRealBytesStats` (single dir, analytics.ts:887-989) but
577
+ * iterates {@link enumerateAdapterDirs}. Optional `sessionId` /
578
+ * `worktreeHash` filters apply uniformly to every dir.
579
+ */
580
+ export declare function getMultiAdapterRealBytesStats(opts?: {
581
+ home?: string;
582
+ sessionId?: string;
583
+ worktreeHash?: string;
584
+ loadDatabase?: () => unknown;
585
+ }): MultiAdapterRealBytesStats;
586
+ /**
587
+ * Marketing-grade labels for auto-memory file prefixes. The renderer sees raw
588
+ * filename prefixes (`project_codex_hooks.md` → `project`) — without this map
589
+ * the user gets schema words in the UI, which leaks the database into UX.
590
+ */
591
+ export declare const autoMemoryLabels: Record<string, string>;
592
+ /**
593
+ * Marketing-grade labels for adapter ids surfaced by
594
+ * {@link enumerateAdapterDirs} / {@link getMultiAdapterLifetimeStats}.
595
+ * The renderer never shows raw IDs — UX uses the names users see in
596
+ * each tool's own surface area.
597
+ */
598
+ export declare const adapterLabels: Record<string, string>;
599
+ /**
600
+ * Format a byte count for the narrative dashboard.
601
+ *
602
+ * Single-unit auto-scale (Grafana / CloudWatch / Datadog convention).
603
+ * Decimals shrink as the integer part grows so the number stays readable
604
+ * at every magnitude. Max output width is 8 characters which fits the
605
+ * existing `padStart(8)` callsites in Sections 1, 3, 4.
606
+ *
607
+ * < 1 KB → "X B" e.g. "100 B"
608
+ * 1 KB – < 100 KB → "X.Y KB" e.g. "4.7 KB", "92.8 KB"
609
+ * 100 KB – < 1 MB → "X KB" e.g. "227 KB", "976 KB"
610
+ * 1 MB – < 100 MB → "X.Y MB" e.g. "4.5 MB", "11.6 MB"
611
+ * 100 MB – < 1 GB → "X MB" e.g. "178 MB", "906 MB"
612
+ * 1 GB – < 100 GB → "X.YY GB" e.g. "1.00 GB", "11.36 GB"
613
+ * ≥ 100 GB → "X.Y GB" e.g. "216.6 GB"
614
+ *
615
+ * Replaced the dual-unit "X KB (0.YY MB)" form because the parenthetical
616
+ * rounded to 0.00 / 0.01 in the common range and added noise without
617
+ * information. Scale awareness comes from the unit jump between rows.
618
+ */
619
+ export declare function kb(b: number): string;
620
+ export declare function detectLocaleAndTz(): {
621
+ locale: string;
622
+ tz: string;
623
+ };
624
+ /**
625
+ * Render the section-4 "For example: what would that cost?" block.
626
+ *
627
+ * Translates a lifetime token total into a relatable Opus-4 dollar figure
628
+ * + 3 tangible comparisons (Cursor Pro / Claude Max / weekends of API
629
+ * coding) + 10-dev team scale projection + alternate-model scale row,
630
+ * capped with an EXAMPLES disclaimer. The renderer is intentionally
631
+ * liberal with rounding (whole-month Cursor counts, integer weekends)
632
+ * because this section is illustrative — the EXAMPLES line tells users
633
+ * not to confuse it for a bill.
634
+ *
635
+ * Returns [] when there's nothing to scale (lifetimeTokens === 0) so
636
+ * the section disappears cleanly on a fresh install.
637
+ *
638
+ * Math constants:
639
+ * Opus 4.7/4.8 = $5.00 per 1M input tokens (fallback when PI_CONTEXT_MODE_PRICE_OUTPUT_PER_TOKEN not set)
640
+ * Sonnet 4.6 = $3.00 per 1M input tokens
641
+ * GPT-4o = $2.50 per 1M input tokens
642
+ * Gemini 2 = $1.25 per 1M input tokens
643
+ * Haiku 4.5 = $1.00 per 1M input tokens
644
+ * Cursor Pro = $20 / month → "X months of Cursor Pro"
645
+ * Claude Max = $200 / month → "X.X months of Claude Max"
646
+ * Weekend coding ≈ $73.67 → "X weekends of nonstop API coding"
647
+ * Team multiplier = 10× → "At a 10-dev team scale: ~$X over Y days, or ~$Z/year"
648
+ */
649
+ export declare function renderCostExample(lifetimeBytes: number, lifetimeTokens: number, lifetimeDays: number): string[];
650
+ /**
651
+ * One day on the horizontal narrative timeline. `ms` is midnight-UTC of
652
+ * the day (caller is responsible for normalising); `count` is captures
653
+ * for that day; `rescueBytes` (when >0) overlays the ◆ /compact glyph.
654
+ */
655
+ export interface TimelineDay {
656
+ ms: number;
657
+ count: number;
658
+ rescueBytes?: number;
659
+ }
660
+ /**
661
+ * Render the proportional-spacing horizontal day strip used in section 1
662
+ * of the 5-section narrative. Returns the lines verbatim ready to splice
663
+ * into the formatReport line buffer:
664
+ *
665
+ * apr 28 ●──────────────────────●────█──────────────────────◆────● may 10
666
+ *
667
+ * apr 28 277 captures
668
+ * may 4 438 captures ← peak
669
+ * may 9 261 captures ◆ /compact rescued 1552 KB
670
+ * may 10 100 captures
671
+ *
672
+ * ● active day █ peak day ◆ /compact rescue
673
+ *
674
+ * The strip body is exactly 56 chars wide. Day positions are computed as
675
+ * `round((day - first) / (last - first) * 55)`. Glyph priority for a
676
+ * column: rescue (◆) > peak (█) > active (●). Filler is the box-drawing
677
+ * `─` character so the strip reads cleanly in monospace terminals.
678
+ */
679
+ export declare function renderHorizontalTimeline(days: TimelineDay[], locale: string, tz: string): string[];
680
+ /**
681
+ * Render a UTC ms timestamp as a human-readable local datetime string in
682
+ * the canonical Mert-approved format:
683
+ *
684
+ * "28 Apr 2026 at 12:16 (Europe/Istanbul)"
685
+ *
686
+ * Used by the 5-section narrative renderer (formatReport) so users see
687
+ * exactly when their conversation started + when /compact rescues fired
688
+ * in their wall-clock timezone — never UTC, never ambiguous.
689
+ *
690
+ * - 24-hour clock with zero-padded minutes ("20:54", not "8:54 PM").
691
+ * - Day is NOT zero-padded ("9 May", not "09 May") to match the target.
692
+ * - IANA timezone is appended verbatim in parentheses regardless of
693
+ * locale so users never misread Istanbul-time as UTC.
694
+ * - Returns "" for ms === 0 or NaN so callers can guard the rendered
695
+ * line ("started …") without an extra timestamp-validity check.
696
+ */
697
+ export declare function formatLocalDateTime(ms: number, locale: string, tz: string): string;
698
+ /**
699
+ * Per-token USD rate — resolves on every call.
700
+ * Dynamic when PI_CONTEXT_MODE_PRICE_OUTPUT_PER_TOKEN is set, Opus 4.7/4.8 input
701
+ * ($5 per 1M tokens) otherwise.
702
+ */
703
+ export declare function pricePerToken(): number;
704
+ /**
705
+ * Back-compat alias for the original Opus-rate const (PR #401 architect
706
+ * P1.1 — single source of truth). Kept as a literal so any third-party
707
+ * consumer importing the named constant still resolves to the same
708
+ * fallback rate. New code should call pricePerToken() to pick up the
709
+ * dynamic Pi env override.
710
+ *
711
+ * @deprecated Use pricePerToken() to honor PI_CONTEXT_MODE_PRICE_OUTPUT_PER_TOKEN.
712
+ */
713
+ export declare const OPUS_INPUT_PRICE_PER_TOKEN: number;
714
+ /** Convert a token count to a USD string at the current per-token rate. */
715
+ export declare function tokensToUsd(tokens: number): string;
716
+ /**
717
+ * Render a FullReport as a visual savings dashboard designed for screenshotting.
718
+ *
719
+ * Design principles:
720
+ * - Before/After comparison bar is the HERO — one glance = "wow"
721
+ * - "tokens saved" is the number people share
722
+ * - Per-tool breakdown shows what each tool SAVED, sorted by impact
723
+ * - Project memory: category bars showing persistent data across sessions
724
+ * - No: Pct column, category tables, tips, jargon
725
+ */
726
+ export declare function formatReport(report: FullReport, version?: string, latestVersion?: string | null, opts?: {
727
+ lifetime?: LifetimeStats;
728
+ mcpUsage?: McpToolUsageRow[];
729
+ conversation?: ConversationStats;
730
+ /**
731
+ * Phase 8 of D2 PRD — pass realBytes pre-aggregated from
732
+ * `getRealBytesStats(...)` and the renderer will use those numbers
733
+ * for the $ math instead of the conservative `events × 256` estimate.
734
+ *
735
+ * - `realBytes.lifetime` overrides `lifetimeTokensWithout`.
736
+ * - `realBytes.conversation` overrides `conversationTokens`.
737
+ * - Either may be omitted independently — missing values fall back
738
+ * to the legacy estimate so this feature can never produce
739
+ * a smaller number than before (Mert: stats only go up).
740
+ * - When the new value is SMALLER than the legacy estimate (fresh
741
+ * sessions before any sandbox events emit), we keep the larger
742
+ * number to honour the same monotonic-growth invariant.
743
+ */
744
+ realBytes?: {
745
+ lifetime?: RealBytesStats;
746
+ conversation?: RealBytesStats;
747
+ };
748
+ /**
749
+ * B3b — multi-adapter aggregation surfaced by
750
+ * `getMultiAdapterLifetimeStats(...)` (analytics.ts:1248). When present,
751
+ * the renderer adds a "Where it came from" sub-block under the receipt,
752
+ * promotes the headline to "across N AI tools" when >= 2 real adapters
753
+ * are detected, and renames the all-work block to "All your work
754
+ * everywhere". Backward compat: omitting this opt preserves the legacy
755
+ * single-adapter renderer output unchanged.
756
+ */
757
+ multiAdapter?: MultiAdapterLifetimeStats;
758
+ /**
759
+ * Point-in-time snapshot of the persistent content store. Optional —
760
+ * callers that don't have store access can omit it and the renderer
761
+ * skips the observability section gracefully.
762
+ */
763
+ indexState?: IndexState;
764
+ /**
765
+ * 5-section narrative renderer overrides. Defaults to ambient
766
+ * `process.cwd()` + `Date.now()` + `detectLocaleAndTz()` for production
767
+ * use; tests inject deterministic values so output is byte-stable.
768
+ */
769
+ cwd?: string;
770
+ now?: number;
771
+ locale?: string;
772
+ tz?: string;
773
+ }): string;