bikky 0.4.0 → 0.4.1

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 (340) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/CODE_OF_CONDUCT.md +80 -0
  3. package/CONTRIBUTING.md +1 -1
  4. package/README.md +32 -2
  5. package/SECURITY.md +58 -0
  6. package/SUPPORT.md +22 -0
  7. package/dist/config.d.ts +19 -0
  8. package/dist/config.js +72 -0
  9. package/dist/daemon/extraction.d.ts +12 -2
  10. package/dist/daemon/extraction.js +85 -133
  11. package/dist/daemon/transcript-sources.d.ts +26 -0
  12. package/dist/daemon/transcript-sources.js +193 -0
  13. package/dist/daemon/watcher.d.ts +3 -2
  14. package/dist/daemon/watcher.js +51 -2
  15. package/dist/install.d.ts +9 -1
  16. package/dist/install.js +62 -34
  17. package/dist/mcp/api.d.ts +4 -3
  18. package/dist/mcp/api.js +4 -3
  19. package/dist/mcp/tools.js +317 -93
  20. package/dist/search-scope.d.ts +24 -0
  21. package/dist/search-scope.js +174 -0
  22. package/docs/configuration.md +30 -1
  23. package/docs/privacy-first.md +140 -0
  24. package/package.json +23 -5
  25. package/dist/cli.d.ts.map +0 -1
  26. package/dist/cli.js.map +0 -1
  27. package/dist/config.d.ts.map +0 -1
  28. package/dist/config.js.map +0 -1
  29. package/dist/config.test.d.ts +0 -9
  30. package/dist/config.test.d.ts.map +0 -1
  31. package/dist/config.test.js +0 -576
  32. package/dist/config.test.js.map +0 -1
  33. package/dist/daemon/capture-policy.d.ts.map +0 -1
  34. package/dist/daemon/capture-policy.js.map +0 -1
  35. package/dist/daemon/capture-policy.test.d.ts +0 -2
  36. package/dist/daemon/capture-policy.test.d.ts.map +0 -1
  37. package/dist/daemon/capture-policy.test.js +0 -48
  38. package/dist/daemon/capture-policy.test.js.map +0 -1
  39. package/dist/daemon/consolidation.d.ts.map +0 -1
  40. package/dist/daemon/consolidation.js.map +0 -1
  41. package/dist/daemon/entity-typing.d.ts.map +0 -1
  42. package/dist/daemon/entity-typing.js.map +0 -1
  43. package/dist/daemon/entity-typing.test.d.ts +0 -2
  44. package/dist/daemon/entity-typing.test.d.ts.map +0 -1
  45. package/dist/daemon/entity-typing.test.js +0 -50
  46. package/dist/daemon/entity-typing.test.js.map +0 -1
  47. package/dist/daemon/episode-summary.d.ts.map +0 -1
  48. package/dist/daemon/episode-summary.js.map +0 -1
  49. package/dist/daemon/episode-summary.test.d.ts +0 -2
  50. package/dist/daemon/episode-summary.test.d.ts.map +0 -1
  51. package/dist/daemon/episode-summary.test.js +0 -104
  52. package/dist/daemon/episode-summary.test.js.map +0 -1
  53. package/dist/daemon/extraction-quality.test.d.ts +0 -2
  54. package/dist/daemon/extraction-quality.test.d.ts.map +0 -1
  55. package/dist/daemon/extraction-quality.test.js +0 -283
  56. package/dist/daemon/extraction-quality.test.js.map +0 -1
  57. package/dist/daemon/extraction-rules.d.ts.map +0 -1
  58. package/dist/daemon/extraction-rules.js.map +0 -1
  59. package/dist/daemon/extraction-rules.test.d.ts +0 -2
  60. package/dist/daemon/extraction-rules.test.d.ts.map +0 -1
  61. package/dist/daemon/extraction-rules.test.js +0 -203
  62. package/dist/daemon/extraction-rules.test.js.map +0 -1
  63. package/dist/daemon/extraction.d.ts.map +0 -1
  64. package/dist/daemon/extraction.js.map +0 -1
  65. package/dist/daemon/extraction.test.d.ts +0 -2
  66. package/dist/daemon/extraction.test.d.ts.map +0 -1
  67. package/dist/daemon/extraction.test.js +0 -225
  68. package/dist/daemon/extraction.test.js.map +0 -1
  69. package/dist/daemon/index.d.ts.map +0 -1
  70. package/dist/daemon/index.js.map +0 -1
  71. package/dist/daemon/loop.d.ts.map +0 -1
  72. package/dist/daemon/loop.js.map +0 -1
  73. package/dist/daemon/loop.test.d.ts +0 -2
  74. package/dist/daemon/loop.test.d.ts.map +0 -1
  75. package/dist/daemon/loop.test.js +0 -85
  76. package/dist/daemon/loop.test.js.map +0 -1
  77. package/dist/daemon/maintenance-state.d.ts.map +0 -1
  78. package/dist/daemon/maintenance-state.js.map +0 -1
  79. package/dist/daemon/maintenance-state.test.d.ts +0 -2
  80. package/dist/daemon/maintenance-state.test.d.ts.map +0 -1
  81. package/dist/daemon/maintenance-state.test.js +0 -56
  82. package/dist/daemon/maintenance-state.test.js.map +0 -1
  83. package/dist/daemon/qdrant.d.ts.map +0 -1
  84. package/dist/daemon/qdrant.js.map +0 -1
  85. package/dist/daemon/qdrant.test.d.ts +0 -8
  86. package/dist/daemon/qdrant.test.d.ts.map +0 -1
  87. package/dist/daemon/qdrant.test.js +0 -265
  88. package/dist/daemon/qdrant.test.js.map +0 -1
  89. package/dist/daemon/relations-vocab.d.ts.map +0 -1
  90. package/dist/daemon/relations-vocab.js.map +0 -1
  91. package/dist/daemon/relations-vocab.test.d.ts +0 -2
  92. package/dist/daemon/relations-vocab.test.d.ts.map +0 -1
  93. package/dist/daemon/relations-vocab.test.js +0 -69
  94. package/dist/daemon/relations-vocab.test.js.map +0 -1
  95. package/dist/daemon/relations.d.ts.map +0 -1
  96. package/dist/daemon/relations.js.map +0 -1
  97. package/dist/daemon/relations.test.d.ts +0 -2
  98. package/dist/daemon/relations.test.d.ts.map +0 -1
  99. package/dist/daemon/relations.test.js +0 -36
  100. package/dist/daemon/relations.test.js.map +0 -1
  101. package/dist/daemon/session-index.d.ts.map +0 -1
  102. package/dist/daemon/session-index.js.map +0 -1
  103. package/dist/daemon/session-index.test.d.ts +0 -2
  104. package/dist/daemon/session-index.test.d.ts.map +0 -1
  105. package/dist/daemon/session-index.test.js +0 -60
  106. package/dist/daemon/session-index.test.js.map +0 -1
  107. package/dist/daemon/session-summary.d.ts.map +0 -1
  108. package/dist/daemon/session-summary.js.map +0 -1
  109. package/dist/daemon/session-summary.test.d.ts +0 -2
  110. package/dist/daemon/session-summary.test.d.ts.map +0 -1
  111. package/dist/daemon/session-summary.test.js +0 -162
  112. package/dist/daemon/session-summary.test.js.map +0 -1
  113. package/dist/daemon/staleness.d.ts.map +0 -1
  114. package/dist/daemon/staleness.js.map +0 -1
  115. package/dist/daemon/staleness.test.d.ts +0 -7
  116. package/dist/daemon/staleness.test.d.ts.map +0 -1
  117. package/dist/daemon/staleness.test.js +0 -128
  118. package/dist/daemon/staleness.test.js.map +0 -1
  119. package/dist/daemon/watcher-health.d.ts.map +0 -1
  120. package/dist/daemon/watcher-health.js.map +0 -1
  121. package/dist/daemon/watcher-health.test.d.ts +0 -5
  122. package/dist/daemon/watcher-health.test.d.ts.map +0 -1
  123. package/dist/daemon/watcher-health.test.js +0 -119
  124. package/dist/daemon/watcher-health.test.js.map +0 -1
  125. package/dist/daemon/watcher.d.ts.map +0 -1
  126. package/dist/daemon/watcher.js.map +0 -1
  127. package/dist/daemon/watcher.test.d.ts +0 -9
  128. package/dist/daemon/watcher.test.d.ts.map +0 -1
  129. package/dist/daemon/watcher.test.js +0 -204
  130. package/dist/daemon/watcher.test.js.map +0 -1
  131. package/dist/daemon/workstream-resolver.d.ts.map +0 -1
  132. package/dist/daemon/workstream-resolver.js.map +0 -1
  133. package/dist/daemon/workstream-resolver.test.d.ts +0 -2
  134. package/dist/daemon/workstream-resolver.test.d.ts.map +0 -1
  135. package/dist/daemon/workstream-resolver.test.js +0 -128
  136. package/dist/daemon/workstream-resolver.test.js.map +0 -1
  137. package/dist/daemon/workstream-summary.d.ts.map +0 -1
  138. package/dist/daemon/workstream-summary.js.map +0 -1
  139. package/dist/daemon/workstream-summary.test.d.ts +0 -2
  140. package/dist/daemon/workstream-summary.test.d.ts.map +0 -1
  141. package/dist/daemon/workstream-summary.test.js +0 -89
  142. package/dist/daemon/workstream-summary.test.js.map +0 -1
  143. package/dist/install.d.ts.map +0 -1
  144. package/dist/install.js.map +0 -1
  145. package/dist/install.test.d.ts +0 -9
  146. package/dist/install.test.d.ts.map +0 -1
  147. package/dist/install.test.js +0 -126
  148. package/dist/install.test.js.map +0 -1
  149. package/dist/lib/qdrant-client.d.ts.map +0 -1
  150. package/dist/lib/qdrant-client.js.map +0 -1
  151. package/dist/lib/qdrant-client.test.d.ts +0 -8
  152. package/dist/lib/qdrant-client.test.d.ts.map +0 -1
  153. package/dist/lib/qdrant-client.test.js +0 -274
  154. package/dist/lib/qdrant-client.test.js.map +0 -1
  155. package/dist/lib/qdrant-pool.d.ts.map +0 -1
  156. package/dist/lib/qdrant-pool.js.map +0 -1
  157. package/dist/lifecycle.d.ts.map +0 -1
  158. package/dist/lifecycle.js.map +0 -1
  159. package/dist/lifecycle.test.d.ts +0 -8
  160. package/dist/lifecycle.test.d.ts.map +0 -1
  161. package/dist/lifecycle.test.js +0 -74
  162. package/dist/lifecycle.test.js.map +0 -1
  163. package/dist/llm/embedding/index.d.ts.map +0 -1
  164. package/dist/llm/embedding/index.js.map +0 -1
  165. package/dist/llm/embedding/index.test.d.ts +0 -8
  166. package/dist/llm/embedding/index.test.d.ts.map +0 -1
  167. package/dist/llm/embedding/index.test.js +0 -100
  168. package/dist/llm/embedding/index.test.js.map +0 -1
  169. package/dist/llm/embedding/providers/bedrock.d.ts.map +0 -1
  170. package/dist/llm/embedding/providers/bedrock.js.map +0 -1
  171. package/dist/llm/embedding/providers/bedrock.test.d.ts +0 -2
  172. package/dist/llm/embedding/providers/bedrock.test.d.ts.map +0 -1
  173. package/dist/llm/embedding/providers/bedrock.test.js +0 -24
  174. package/dist/llm/embedding/providers/bedrock.test.js.map +0 -1
  175. package/dist/llm/embedding/providers/index.d.ts.map +0 -1
  176. package/dist/llm/embedding/providers/index.js.map +0 -1
  177. package/dist/llm/embedding/providers/ollama.d.ts.map +0 -1
  178. package/dist/llm/embedding/providers/ollama.js.map +0 -1
  179. package/dist/llm/embedding/providers/ollama.test.d.ts +0 -2
  180. package/dist/llm/embedding/providers/ollama.test.d.ts.map +0 -1
  181. package/dist/llm/embedding/providers/ollama.test.js +0 -54
  182. package/dist/llm/embedding/providers/ollama.test.js.map +0 -1
  183. package/dist/llm/embedding/providers/openai.d.ts.map +0 -1
  184. package/dist/llm/embedding/providers/openai.js.map +0 -1
  185. package/dist/llm/embedding/providers/openai.test.d.ts +0 -2
  186. package/dist/llm/embedding/providers/openai.test.d.ts.map +0 -1
  187. package/dist/llm/embedding/providers/openai.test.js +0 -48
  188. package/dist/llm/embedding/providers/openai.test.js.map +0 -1
  189. package/dist/llm/embedding/providers/portkey.d.ts.map +0 -1
  190. package/dist/llm/embedding/providers/portkey.js.map +0 -1
  191. package/dist/llm/embedding/providers/portkey.test.d.ts +0 -2
  192. package/dist/llm/embedding/providers/portkey.test.d.ts.map +0 -1
  193. package/dist/llm/embedding/providers/portkey.test.js +0 -56
  194. package/dist/llm/embedding/providers/portkey.test.js.map +0 -1
  195. package/dist/llm/embedding/registry.d.ts.map +0 -1
  196. package/dist/llm/embedding/registry.js.map +0 -1
  197. package/dist/llm/embedding/registry.test.d.ts +0 -7
  198. package/dist/llm/embedding/registry.test.d.ts.map +0 -1
  199. package/dist/llm/embedding/registry.test.js +0 -68
  200. package/dist/llm/embedding/registry.test.js.map +0 -1
  201. package/dist/llm/embedding/types.d.ts.map +0 -1
  202. package/dist/llm/embedding/types.js.map +0 -1
  203. package/dist/llm/errors.d.ts.map +0 -1
  204. package/dist/llm/errors.js.map +0 -1
  205. package/dist/llm/errors.test.d.ts +0 -2
  206. package/dist/llm/errors.test.d.ts.map +0 -1
  207. package/dist/llm/errors.test.js +0 -103
  208. package/dist/llm/errors.test.js.map +0 -1
  209. package/dist/llm/fetch.d.ts.map +0 -1
  210. package/dist/llm/fetch.js.map +0 -1
  211. package/dist/llm/index.d.ts.map +0 -1
  212. package/dist/llm/index.js.map +0 -1
  213. package/dist/llm/inference/index.d.ts.map +0 -1
  214. package/dist/llm/inference/index.js.map +0 -1
  215. package/dist/llm/inference/index.test.d.ts +0 -6
  216. package/dist/llm/inference/index.test.d.ts.map +0 -1
  217. package/dist/llm/inference/index.test.js +0 -150
  218. package/dist/llm/inference/index.test.js.map +0 -1
  219. package/dist/llm/inference/providers/bedrock.d.ts.map +0 -1
  220. package/dist/llm/inference/providers/bedrock.js.map +0 -1
  221. package/dist/llm/inference/providers/bedrock.test.d.ts +0 -2
  222. package/dist/llm/inference/providers/bedrock.test.d.ts.map +0 -1
  223. package/dist/llm/inference/providers/bedrock.test.js +0 -68
  224. package/dist/llm/inference/providers/bedrock.test.js.map +0 -1
  225. package/dist/llm/inference/providers/index.d.ts.map +0 -1
  226. package/dist/llm/inference/providers/index.js.map +0 -1
  227. package/dist/llm/inference/providers/ollama.d.ts.map +0 -1
  228. package/dist/llm/inference/providers/ollama.js.map +0 -1
  229. package/dist/llm/inference/providers/ollama.test.d.ts +0 -2
  230. package/dist/llm/inference/providers/ollama.test.d.ts.map +0 -1
  231. package/dist/llm/inference/providers/ollama.test.js +0 -57
  232. package/dist/llm/inference/providers/ollama.test.js.map +0 -1
  233. package/dist/llm/inference/providers/openai.d.ts.map +0 -1
  234. package/dist/llm/inference/providers/openai.js.map +0 -1
  235. package/dist/llm/inference/providers/openai.test.d.ts +0 -2
  236. package/dist/llm/inference/providers/openai.test.d.ts.map +0 -1
  237. package/dist/llm/inference/providers/openai.test.js +0 -82
  238. package/dist/llm/inference/providers/openai.test.js.map +0 -1
  239. package/dist/llm/inference/providers/portkey.d.ts.map +0 -1
  240. package/dist/llm/inference/providers/portkey.js.map +0 -1
  241. package/dist/llm/inference/providers/portkey.test.d.ts +0 -2
  242. package/dist/llm/inference/providers/portkey.test.d.ts.map +0 -1
  243. package/dist/llm/inference/providers/portkey.test.js +0 -48
  244. package/dist/llm/inference/providers/portkey.test.js.map +0 -1
  245. package/dist/llm/inference/registry.d.ts.map +0 -1
  246. package/dist/llm/inference/registry.js.map +0 -1
  247. package/dist/llm/inference/registry.test.d.ts +0 -6
  248. package/dist/llm/inference/registry.test.d.ts.map +0 -1
  249. package/dist/llm/inference/registry.test.js +0 -63
  250. package/dist/llm/inference/registry.test.js.map +0 -1
  251. package/dist/llm/inference/types.d.ts.map +0 -1
  252. package/dist/llm/inference/types.js.map +0 -1
  253. package/dist/llm/telemetry.d.ts.map +0 -1
  254. package/dist/llm/telemetry.js.map +0 -1
  255. package/dist/llm/telemetry.test.d.ts +0 -5
  256. package/dist/llm/telemetry.test.d.ts.map +0 -1
  257. package/dist/llm/telemetry.test.js +0 -89
  258. package/dist/llm/telemetry.test.js.map +0 -1
  259. package/dist/llm/types.d.ts.map +0 -1
  260. package/dist/llm/types.js.map +0 -1
  261. package/dist/logger.d.ts.map +0 -1
  262. package/dist/logger.js.map +0 -1
  263. package/dist/logger.test.d.ts +0 -5
  264. package/dist/logger.test.d.ts.map +0 -1
  265. package/dist/logger.test.js +0 -103
  266. package/dist/logger.test.js.map +0 -1
  267. package/dist/mcp/api.d.ts.map +0 -1
  268. package/dist/mcp/api.js.map +0 -1
  269. package/dist/mcp/helpers.d.ts.map +0 -1
  270. package/dist/mcp/helpers.js.map +0 -1
  271. package/dist/mcp/helpers.test.d.ts +0 -5
  272. package/dist/mcp/helpers.test.d.ts.map +0 -1
  273. package/dist/mcp/helpers.test.js +0 -530
  274. package/dist/mcp/helpers.test.js.map +0 -1
  275. package/dist/mcp/index.d.ts.map +0 -1
  276. package/dist/mcp/index.js.map +0 -1
  277. package/dist/mcp/taxonomy.d.ts.map +0 -1
  278. package/dist/mcp/taxonomy.js.map +0 -1
  279. package/dist/mcp/taxonomy.test.d.ts +0 -5
  280. package/dist/mcp/taxonomy.test.d.ts.map +0 -1
  281. package/dist/mcp/taxonomy.test.js +0 -215
  282. package/dist/mcp/taxonomy.test.js.map +0 -1
  283. package/dist/mcp/tools.d.ts.map +0 -1
  284. package/dist/mcp/tools.js.map +0 -1
  285. package/dist/mcp/types.d.ts.map +0 -1
  286. package/dist/mcp/types.js.map +0 -1
  287. package/dist/postinstall.d.ts.map +0 -1
  288. package/dist/postinstall.js.map +0 -1
  289. package/dist/privacy/redaction.d.ts.map +0 -1
  290. package/dist/privacy/redaction.js.map +0 -1
  291. package/dist/privacy/redaction.test.d.ts +0 -2
  292. package/dist/privacy/redaction.test.d.ts.map +0 -1
  293. package/dist/privacy/redaction.test.js +0 -51
  294. package/dist/privacy/redaction.test.js.map +0 -1
  295. package/dist/prompts/brief.d.ts.map +0 -1
  296. package/dist/prompts/brief.js.map +0 -1
  297. package/dist/prompts/contradiction.d.ts.map +0 -1
  298. package/dist/prompts/contradiction.js.map +0 -1
  299. package/dist/prompts/distill.d.ts.map +0 -1
  300. package/dist/prompts/distill.js.map +0 -1
  301. package/dist/prompts/entity-typing.d.ts.map +0 -1
  302. package/dist/prompts/entity-typing.js.map +0 -1
  303. package/dist/prompts/episode-summary.d.ts.map +0 -1
  304. package/dist/prompts/episode-summary.js.map +0 -1
  305. package/dist/prompts/extraction.d.ts.map +0 -1
  306. package/dist/prompts/extraction.js.map +0 -1
  307. package/dist/prompts/index.d.ts.map +0 -1
  308. package/dist/prompts/index.js.map +0 -1
  309. package/dist/prompts/prompts.test.d.ts +0 -8
  310. package/dist/prompts/prompts.test.d.ts.map +0 -1
  311. package/dist/prompts/prompts.test.js +0 -140
  312. package/dist/prompts/prompts.test.js.map +0 -1
  313. package/dist/prompts/relations.d.ts.map +0 -1
  314. package/dist/prompts/relations.js.map +0 -1
  315. package/dist/prompts/workstream-summary.d.ts.map +0 -1
  316. package/dist/prompts/workstream-summary.js.map +0 -1
  317. package/dist/provenance/actor.d.ts.map +0 -1
  318. package/dist/provenance/actor.js.map +0 -1
  319. package/dist/provenance/actor.test.d.ts +0 -2
  320. package/dist/provenance/actor.test.d.ts.map +0 -1
  321. package/dist/provenance/actor.test.js +0 -49
  322. package/dist/provenance/actor.test.js.map +0 -1
  323. package/dist/render.d.ts.map +0 -1
  324. package/dist/render.js.map +0 -1
  325. package/dist/render.test.d.ts +0 -8
  326. package/dist/render.test.d.ts.map +0 -1
  327. package/dist/render.test.js +0 -244
  328. package/dist/render.test.js.map +0 -1
  329. package/dist/routing.d.ts.map +0 -1
  330. package/dist/routing.js.map +0 -1
  331. package/dist/routing.test.d.ts +0 -2
  332. package/dist/routing.test.d.ts.map +0 -1
  333. package/dist/routing.test.js +0 -79
  334. package/dist/routing.test.js.map +0 -1
  335. package/dist/status.d.ts.map +0 -1
  336. package/dist/status.js.map +0 -1
  337. package/dist/status.test.d.ts +0 -5
  338. package/dist/status.test.d.ts.map +0 -1
  339. package/dist/status.test.js +0 -203
  340. package/dist/status.test.js.map +0 -1
@@ -1,15 +1,15 @@
1
1
  /**
2
- * Events-based memory extraction — reads Copilot CLI events.jsonl transcripts,
2
+ * Events-based memory extraction — reads supported coding-agent transcripts,
3
3
  * extracts facts via LLM, and stores them in Qdrant with source: "system".
4
4
  *
5
5
  * Uses a JSON file for extraction state (high-water byte offsets) instead of SQLite.
6
- * Active session detection scans ~/.copilot/session-state/ for lock files.
6
+ * Copilot session detection uses lock files. Claude Code detection uses
7
+ * top-level JSONL transcripts under ~/.claude/projects.
7
8
  */
8
- import { readFile, readdir, stat } from "node:fs/promises";
9
9
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
10
10
  import { createHash } from "node:crypto";
11
11
  import { join } from "node:path";
12
- import { loadConfig, STATE_DIR, EXTRACTION_HEALTH_PATH } from "../config.js";
12
+ import { STATE_DIR, EXTRACTION_HEALTH_PATH } from "../config.js";
13
13
  import * as qdrant from "./qdrant.js";
14
14
  import { chatCompletion } from "../llm/index.js";
15
15
  import { detectContradiction } from "./consolidation.js";
@@ -20,6 +20,7 @@ import { shouldSummarizeEvents, updateSessionSummary } from "./session-summary.j
20
20
  import { redactStorageText } from "../privacy/redaction.js";
21
21
  import { compareSubtype, hasTypedToken, verifyGrounding, verifyVolatilityCoherence } from "./extraction-rules.js";
22
22
  import { resolveActorIdentity } from "../provenance/actor.js";
23
+ import { discoverClaudeTranscriptMappings, discoverCopilotTranscriptMappings, extractionStateKey, readNewTranscriptEvents, transcriptLabel, } from "./transcript-sources.js";
23
24
  // ── Module state ─────────────────────────────────────────────────────────────
24
25
  let logFn = (() => { });
25
26
  let lastTickAt = 0;
@@ -27,11 +28,6 @@ export const setLogger = (fn) => {
27
28
  logFn = fn;
28
29
  };
29
30
  // ── Constants ────────────────────────────────────────────────────────────────
30
- const EXTRACTABLE_TYPES = new Set([
31
- "user.message",
32
- "assistant.message",
33
- "session.compaction_complete",
34
- ]);
35
31
  export const DEFAULT_EXTRACTION_PROMPT = `You are Bikky's memory extraction agent for open-source coding agents. Extract durable, reusable facts that help a future agent continue work without rereading the whole transcript.
36
32
 
37
33
  ## Core rule
@@ -123,50 +119,6 @@ const upsertExtractionState = (state) => {
123
119
  states[state.session_id] = state;
124
120
  saveExtractionStates(states);
125
121
  };
126
- /**
127
- * Scan lock files to build PID → Copilot UUID mapping.
128
- * Copilot CLI writes `inuse.<pid>.lock` in each session directory.
129
- */
130
- const resolveLockFiles = async () => {
131
- const cfg = loadConfig();
132
- const copilotStateDir = cfg.watchers.copilot.path;
133
- const mappings = [];
134
- try {
135
- const sessionDirs = await readdir(copilotStateDir, { withFileTypes: true });
136
- for (const sessionDir of sessionDirs) {
137
- if (!sessionDir.isDirectory())
138
- continue;
139
- const uuid = sessionDir.name;
140
- const sessionPath = join(copilotStateDir, uuid);
141
- const entries = await readdir(sessionPath, { withFileTypes: true });
142
- for (const entry of entries) {
143
- if (!entry.isFile())
144
- continue;
145
- const lockFile = entry.name; // inuse.12345.lock
146
- const pidMatch = lockFile.match(/^inuse\.(\d+)\.lock$/);
147
- if (!pidMatch || !uuid)
148
- continue;
149
- const pid = parseInt(pidMatch[1], 10);
150
- const eventsPath = join(copilotStateDir, uuid, "events.jsonl");
151
- // Verify events.jsonl exists
152
- try {
153
- await stat(eventsPath);
154
- mappings.push({ pid, uuid, eventsPath });
155
- }
156
- catch {
157
- // No events.jsonl — skip
158
- }
159
- }
160
- }
161
- }
162
- catch (e) {
163
- logFn("WARN", `Lock file scan failed: ${e.message}`);
164
- }
165
- return mappings;
166
- };
167
- /**
168
- * Check if a process is still running.
169
- */
170
122
  const isProcessAlive = (pid) => {
171
123
  try {
172
124
  process.kill(pid, 0);
@@ -176,59 +128,6 @@ const isProcessAlive = (pid) => {
176
128
  return false;
177
129
  }
178
130
  };
179
- /**
180
- * Read new events from events.jsonl starting at the given byte offset.
181
- * Returns parsed extractable events and the new byte offset.
182
- */
183
- const readNewEvents = async (eventsPath, byteOffset) => {
184
- const fileStat = await stat(eventsPath);
185
- if (fileStat.size <= byteOffset) {
186
- return { events: [], newOffset: byteOffset, totalLines: 0 };
187
- }
188
- // Read from byte offset to end of file
189
- const buf = await readFile(eventsPath);
190
- const newContent = buf.subarray(byteOffset).toString("utf-8");
191
- const events = [];
192
- let totalLines = 0;
193
- for (const line of newContent.split("\n")) {
194
- if (!line.trim())
195
- continue;
196
- totalLines++;
197
- try {
198
- const obj = JSON.parse(line);
199
- if (!EXTRACTABLE_TYPES.has(obj.type))
200
- continue;
201
- const data = obj.data || {};
202
- let content = "";
203
- if (obj.type === "user.message") {
204
- content = data.content || "";
205
- }
206
- else if (obj.type === "assistant.message") {
207
- const parts = [];
208
- if (data.content)
209
- parts.push(data.content);
210
- if (data.reasoningText)
211
- parts.push(data.reasoningText);
212
- // Skip reasoningOpaque — encrypted noise
213
- content = parts.join("\n");
214
- }
215
- else if (obj.type === "session.compaction_complete") {
216
- content = data.summaryContent || "";
217
- }
218
- if (content.length > 0) {
219
- events.push({
220
- type: obj.type,
221
- content,
222
- timestamp: obj.timestamp || new Date().toISOString(),
223
- });
224
- }
225
- }
226
- catch {
227
- // Malformed line — skip
228
- }
229
- }
230
- return { events, newOffset: fileStat.size, totalLines };
231
- };
232
131
  /**
233
132
  * Strip tool-call narration and boilerplate from assistant messages.
234
133
  * Keeps substantive content — decisions, explanations, findings.
@@ -504,7 +403,7 @@ const contentHash = (text) => createHash("sha256").update(text).digest("hex");
504
403
  * Dedup-check and store extracted facts in Qdrant.
505
404
  * Returns count of facts actually inserted.
506
405
  */
507
- const storeFacts = async (facts, sessionId, config) => {
406
+ const storeFacts = async (facts, sessionId, config, source) => {
508
407
  if (!qdrant.isReady()) {
509
408
  logFn("WARN", "Extraction: Qdrant not ready, skipping store");
510
409
  return 0;
@@ -514,6 +413,8 @@ const storeFacts = async (facts, sessionId, config) => {
514
413
  capture_policy_version: CAPTURE_POLICY_VERSION,
515
414
  extracted_by_prompt: `${EXTRACTION_PROMPT_DESCRIPTOR.id}@${EXTRACTION_PROMPT_DESCRIPTOR.version}`,
516
415
  };
416
+ if (source)
417
+ baseMeta.extraction_source = source;
517
418
  const actor = resolveActorIdentity({ config });
518
419
  if (actor.actor_label)
519
420
  baseMeta.actor_label = actor.actor_label;
@@ -729,20 +630,38 @@ export const tick = async (config) => {
729
630
  lastTickAt = now;
730
631
  const minEvents = config.daemon.extract_min_events || CAPTURE_TRIGGERS.factExtraction.minEvents;
731
632
  try {
732
- // Extract from ALL active Copilot sessions with events.jsonl
733
- const lockMappings = await resolveLockFiles();
734
- const aliveMappings = lockMappings.filter(m => isProcessAlive(m.pid));
735
- logFn("INFO", `Extraction tick: ${aliveMappings.length} active copilot session(s) with events.jsonl`);
736
- writeExtractionHealth(aliveMappings.length, config);
737
- for (const mapping of aliveMappings) {
738
- await extractForUuid(mapping, minEvents, config);
633
+ const copilotMappings = await discoverCopilotMappings(config);
634
+ const claudeMappings = await discoverClaudeMappings(config);
635
+ const mappings = [...copilotMappings, ...claudeMappings];
636
+ logFn("INFO", `Extraction tick: ${copilotMappings.length} active copilot session(s), ${claudeMappings.length} claude transcript(s)`);
637
+ writeExtractionHealth({ copilot: copilotMappings.length, claude: claudeMappings.length }, config);
638
+ for (const mapping of mappings) {
639
+ await extractForMapping(mapping, minEvents, config);
739
640
  }
740
641
  }
741
642
  catch (e) {
742
643
  logFn("ERROR", `Extraction tick failed: ${e.message}`);
743
644
  }
744
645
  };
745
- function writeExtractionHealth(activeCount, config) {
646
+ const discoverCopilotMappings = async (config) => {
647
+ try {
648
+ return await discoverCopilotTranscriptMappings(config, isProcessAlive);
649
+ }
650
+ catch (e) {
651
+ logFn("WARN", `Copilot transcript scan failed: ${e.message}`);
652
+ return [];
653
+ }
654
+ };
655
+ const discoverClaudeMappings = async (config) => {
656
+ try {
657
+ return await discoverClaudeTranscriptMappings(config);
658
+ }
659
+ catch (e) {
660
+ logFn("WARN", `Claude transcript scan failed: ${e.message}`);
661
+ return [];
662
+ }
663
+ };
664
+ function writeExtractionHealth(activeCounts, config) {
746
665
  try {
747
666
  let existing = {};
748
667
  if (existsSync(EXTRACTION_HEALTH_PATH)) {
@@ -752,11 +671,33 @@ function writeExtractionHealth(activeCount, config) {
752
671
  catch { /* ignore */ }
753
672
  }
754
673
  const now = new Date().toISOString();
674
+ const activeCount = activeCounts.copilot + activeCounts.claude;
675
+ const existingSources = existing.sources ?? {};
676
+ const copilotLastActive = activeCounts.copilot > 0
677
+ ? now
678
+ : (existingSources.copilot?.last_active_session_at ?? existing.last_active_session_at ?? null);
679
+ const claudeLastActive = activeCounts.claude > 0
680
+ ? now
681
+ : (existingSources.claude?.last_active_session_at ?? null);
755
682
  const health = {
756
683
  last_tick_at: now,
757
684
  last_active_session_at: activeCount > 0 ? now : (existing.last_active_session_at ?? null),
758
685
  active_session_count: activeCount,
759
686
  watcher_path: config.watchers.copilot.path,
687
+ sources: {
688
+ copilot: {
689
+ enabled: config.watchers.copilot.enabled,
690
+ watcher_path: config.watchers.copilot.path,
691
+ active_session_count: activeCounts.copilot,
692
+ last_active_session_at: copilotLastActive,
693
+ },
694
+ claude: {
695
+ enabled: config.watchers.claude.enabled,
696
+ watcher_path: config.watchers.claude.path,
697
+ active_session_count: activeCounts.claude,
698
+ last_active_session_at: claudeLastActive,
699
+ },
700
+ },
760
701
  };
761
702
  mkdirSync(STATE_DIR, { recursive: true });
762
703
  writeFileSync(EXTRACTION_HEALTH_PATH, JSON.stringify(health, null, 2) + "\n");
@@ -765,32 +706,37 @@ function writeExtractionHealth(activeCount, config) {
765
706
  logFn("DEBUG", `Failed to write extraction health: ${e.message}`);
766
707
  }
767
708
  }
768
- /**
769
- * Extract facts for a single Copilot session identified by UUID.
770
- * Uses UUID as the extraction_state key.
771
- * Automatically chunks large transcripts into multiple LLM calls.
772
- */
773
- const extractForUuid = async (mapping, minEvents, config) => {
774
- const { uuid, eventsPath } = mapping;
775
- // Use UUID as session_id in extraction_state (prefix with "uuid:" to avoid collisions)
776
- const stateKey = `uuid:${uuid}`;
709
+ const extractForMapping = async (mapping, minEvents, config) => {
710
+ const { uuid, eventsPath, source } = mapping;
711
+ const stateKey = extractionStateKey(mapping);
712
+ const label = transcriptLabel(mapping);
777
713
  let state = getExtractionState(stateKey);
778
714
  if (!state) {
779
715
  state = {
780
716
  session_id: stateKey,
781
- copilot_uuid: uuid,
717
+ source,
718
+ ...(source === "copilot" ? { copilot_uuid: uuid } : { claude_session_id: uuid }),
782
719
  events_path: eventsPath,
783
720
  byte_offset: 0,
784
721
  last_extracted_at: null,
785
722
  event_count: 0,
786
723
  };
787
724
  }
725
+ state.source = source;
726
+ if (source === "copilot") {
727
+ state.copilot_uuid = uuid;
728
+ }
729
+ else {
730
+ state.claude_session_id = uuid;
731
+ }
732
+ state.events_path = eventsPath;
788
733
  // Read new events
789
- const { events, newOffset, totalLines } = await readNewEvents(eventsPath, state.byte_offset);
734
+ const { events, newOffset, totalLines } = await readNewTranscriptEvents(eventsPath, state.byte_offset, source);
790
735
  const shouldSummarize = shouldSummarizeEvents(events, minEvents);
791
736
  if (events.length < minEvents && !shouldSummarize) {
792
- // Still update offset to avoid re-scanning non-extractable events
793
- if (newOffset > state.byte_offset) {
737
+ // Still advance past pure noise, but keep below-threshold conversation
738
+ // events buffered until enough context accumulates for extraction.
739
+ if (events.length === 0 && newOffset > state.byte_offset) {
794
740
  state.byte_offset = newOffset;
795
741
  state.event_count += totalLines;
796
742
  upsertExtractionState(state);
@@ -802,27 +748,33 @@ const extractForUuid = async (mapping, minEvents, config) => {
802
748
  let totalFacts = 0;
803
749
  for (let i = 0; i < chunks.length; i++) {
804
750
  const transcript = buildTranscript(chunks[i]);
805
- logFn("DEBUG", `Extraction: UUID ${uuid.slice(0, 8)} — chunk ${i + 1}/${chunks.length}, ${chunks[i].length} events, ${transcript.length} chars`);
751
+ logFn("DEBUG", `Extraction: ${label} — chunk ${i + 1}/${chunks.length}, ${chunks[i].length} events, ${transcript.length} chars`);
806
752
  if (events.length >= minEvents) {
807
753
  const facts = await extractFacts(transcript, stateKey, config);
808
754
  if (facts.length > 0) {
809
- const stored = await storeFacts(facts, stateKey, config);
755
+ const stored = await storeFacts(facts, stateKey, config, source);
810
756
  totalFacts += stored;
811
757
  }
812
758
  }
813
759
  await updateSummaryForTranscript(stateKey, transcript, chunks[i].length, config);
814
760
  }
815
761
  if (totalFacts > 0) {
816
- logFn("INFO", `Extraction: UUID ${uuid.slice(0, 8)} — ${totalFacts} facts stored (${events.length} events, ${chunks.length} chunk(s))`);
762
+ logFn("INFO", `Extraction: ${label} — ${totalFacts} facts stored (${events.length} events, ${chunks.length} chunk(s))`);
817
763
  }
818
764
  else {
819
- logFn("DEBUG", `Extraction: UUID ${uuid.slice(0, 8)} — ${events.length} events but 0 facts extracted`);
765
+ logFn("DEBUG", `Extraction: ${label} — ${events.length} events but 0 facts extracted`);
820
766
  }
821
767
  // Update high-water mark
822
768
  state.byte_offset = newOffset;
823
769
  state.event_count += totalLines;
824
770
  state.last_extracted_at = new Date().toISOString();
825
- state.copilot_uuid = uuid;
771
+ state.source = source;
772
+ if (source === "copilot") {
773
+ state.copilot_uuid = uuid;
774
+ }
775
+ else {
776
+ state.claude_session_id = uuid;
777
+ }
826
778
  state.events_path = eventsPath;
827
779
  upsertExtractionState(state);
828
780
  return totalFacts;
@@ -0,0 +1,26 @@
1
+ import type { BikkyConfig } from "../config.js";
2
+ export type TranscriptSource = "copilot" | "claude";
3
+ export interface TranscriptMapping {
4
+ source: TranscriptSource;
5
+ uuid: string;
6
+ eventsPath: string;
7
+ active: boolean;
8
+ pid?: number;
9
+ }
10
+ export interface ParsedEvent {
11
+ type: string;
12
+ content: string;
13
+ timestamp: string;
14
+ }
15
+ export declare const extractionStateKey: (mapping: Pick<TranscriptMapping, "source" | "uuid">) => string;
16
+ export declare const transcriptLabel: (mapping: Pick<TranscriptMapping, "source" | "uuid">) => string;
17
+ export declare const discoverCopilotTranscriptMappings: (config: BikkyConfig, isProcessAlive: (pid: number) => boolean) => Promise<TranscriptMapping[]>;
18
+ export declare const discoverClaudeTranscriptMappings: (config: BikkyConfig) => Promise<TranscriptMapping[]>;
19
+ export declare const parseCopilotTranscriptLine: (line: string) => ParsedEvent | null;
20
+ export declare const parseClaudeTranscriptLine: (line: string) => ParsedEvent | null;
21
+ export declare const readNewTranscriptEvents: (eventsPath: string, byteOffset: number, source: TranscriptSource) => Promise<{
22
+ events: ParsedEvent[];
23
+ newOffset: number;
24
+ totalLines: number;
25
+ }>;
26
+ //# sourceMappingURL=transcript-sources.d.ts.map
@@ -0,0 +1,193 @@
1
+ import { readFile, readdir, stat } from "node:fs/promises";
2
+ import { basename, join } from "node:path";
3
+ const COPILOT_EXTRACTABLE_TYPES = new Set([
4
+ "user.message",
5
+ "assistant.message",
6
+ "session.compaction_complete",
7
+ ]);
8
+ const isNotFoundError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
9
+ const readDirIfExists = async (dir) => {
10
+ try {
11
+ return await readdir(dir, { withFileTypes: true });
12
+ }
13
+ catch (e) {
14
+ if (isNotFoundError(e))
15
+ return [];
16
+ throw e;
17
+ }
18
+ };
19
+ export const extractionStateKey = (mapping) => mapping.source === "copilot" ? `uuid:${mapping.uuid}` : `claude:${mapping.uuid}`;
20
+ export const transcriptLabel = (mapping) => `${mapping.source}:${mapping.uuid.slice(0, 8)}`;
21
+ export const discoverCopilotTranscriptMappings = async (config, isProcessAlive) => {
22
+ if (!config.watchers.copilot.enabled)
23
+ return [];
24
+ const copilotStateDir = config.watchers.copilot.path;
25
+ const mappings = [];
26
+ const sessionDirs = await readDirIfExists(copilotStateDir);
27
+ for (const sessionDir of sessionDirs) {
28
+ if (!sessionDir.isDirectory())
29
+ continue;
30
+ const uuid = sessionDir.name;
31
+ const sessionPath = join(copilotStateDir, uuid);
32
+ const entries = await readDirIfExists(sessionPath);
33
+ for (const entry of entries) {
34
+ if (!entry.isFile())
35
+ continue;
36
+ const pidMatch = entry.name.match(/^inuse\.(\d+)\.lock$/);
37
+ if (!pidMatch || !uuid)
38
+ continue;
39
+ const pid = parseInt(pidMatch[1], 10);
40
+ if (!isProcessAlive(pid))
41
+ continue;
42
+ const eventsPath = join(copilotStateDir, uuid, "events.jsonl");
43
+ try {
44
+ await stat(eventsPath);
45
+ }
46
+ catch (e) {
47
+ if (isNotFoundError(e))
48
+ continue;
49
+ throw e;
50
+ }
51
+ mappings.push({ source: "copilot", pid, uuid, eventsPath, active: true });
52
+ }
53
+ }
54
+ return mappings;
55
+ };
56
+ export const discoverClaudeTranscriptMappings = async (config) => {
57
+ if (!config.watchers.claude.enabled)
58
+ return [];
59
+ const baseDir = config.watchers.claude.path;
60
+ const mappings = [];
61
+ const entries = await readDirIfExists(baseDir);
62
+ for (const entry of entries) {
63
+ const entryPath = join(baseDir, entry.name);
64
+ if (entry.isFile() && entry.name.endsWith(".jsonl")) {
65
+ const fileStat = await stat(entryPath);
66
+ mappings.push({
67
+ source: "claude",
68
+ uuid: basename(entry.name, ".jsonl"),
69
+ eventsPath: entryPath,
70
+ active: true,
71
+ mtimeMs: fileStat.mtimeMs,
72
+ });
73
+ continue;
74
+ }
75
+ if (!entry.isDirectory())
76
+ continue;
77
+ const projectEntries = await readDirIfExists(entryPath);
78
+ for (const projectEntry of projectEntries) {
79
+ if (!projectEntry.isFile() || !projectEntry.name.endsWith(".jsonl"))
80
+ continue;
81
+ const eventsPath = join(entryPath, projectEntry.name);
82
+ const fileStat = await stat(eventsPath);
83
+ mappings.push({
84
+ source: "claude",
85
+ uuid: basename(projectEntry.name, ".jsonl"),
86
+ eventsPath,
87
+ active: true,
88
+ mtimeMs: fileStat.mtimeMs,
89
+ });
90
+ }
91
+ }
92
+ return mappings
93
+ .sort((a, b) => b.mtimeMs - a.mtimeMs)
94
+ .map(({ mtimeMs: _mtimeMs, ...mapping }) => mapping);
95
+ };
96
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
97
+ const stringField = (value) => typeof value === "string" ? value : "";
98
+ const claudeTextContent = (content) => {
99
+ if (typeof content === "string")
100
+ return content;
101
+ if (!Array.isArray(content))
102
+ return "";
103
+ const parts = [];
104
+ for (const block of content) {
105
+ if (typeof block === "string") {
106
+ parts.push(block);
107
+ continue;
108
+ }
109
+ if (!isRecord(block) || block.type !== "text")
110
+ continue;
111
+ const text = stringField(block.text);
112
+ if (text)
113
+ parts.push(text);
114
+ }
115
+ return parts.join("\n");
116
+ };
117
+ export const parseCopilotTranscriptLine = (line) => {
118
+ const obj = JSON.parse(line);
119
+ if (!obj.type || !COPILOT_EXTRACTABLE_TYPES.has(obj.type))
120
+ return null;
121
+ const data = obj.data || {};
122
+ let content = "";
123
+ if (obj.type === "user.message") {
124
+ content = stringField(data.content);
125
+ }
126
+ else if (obj.type === "assistant.message") {
127
+ const parts = [];
128
+ const contentText = stringField(data.content);
129
+ const reasoningText = stringField(data.reasoningText);
130
+ if (contentText)
131
+ parts.push(contentText);
132
+ if (reasoningText)
133
+ parts.push(reasoningText);
134
+ content = parts.join("\n");
135
+ }
136
+ else if (obj.type === "session.compaction_complete") {
137
+ content = stringField(data.summaryContent);
138
+ }
139
+ if (!content)
140
+ return null;
141
+ return {
142
+ type: obj.type,
143
+ content,
144
+ timestamp: obj.timestamp || new Date().toISOString(),
145
+ };
146
+ };
147
+ export const parseClaudeTranscriptLine = (line) => {
148
+ const obj = JSON.parse(line);
149
+ const recordType = obj.type;
150
+ if (recordType !== "user" && recordType !== "assistant")
151
+ return null;
152
+ const message = isRecord(obj.message) ? obj.message : null;
153
+ if (!message)
154
+ return null;
155
+ const role = message.role === "user" || message.role === "assistant" ? message.role : recordType;
156
+ if (role !== "user" && role !== "assistant")
157
+ return null;
158
+ const content = claudeTextContent(message.content).trim();
159
+ if (!content)
160
+ return null;
161
+ return {
162
+ type: role === "user" ? "user.message" : "assistant.message",
163
+ content,
164
+ timestamp: stringField(obj.timestamp) || new Date().toISOString(),
165
+ };
166
+ };
167
+ export const readNewTranscriptEvents = async (eventsPath, byteOffset, source) => {
168
+ const fileStat = await stat(eventsPath);
169
+ if (fileStat.size <= byteOffset) {
170
+ return { events: [], newOffset: byteOffset, totalLines: 0 };
171
+ }
172
+ const buf = await readFile(eventsPath);
173
+ const newContent = buf.subarray(byteOffset).toString("utf-8");
174
+ const events = [];
175
+ let totalLines = 0;
176
+ for (const line of newContent.split("\n")) {
177
+ if (!line.trim())
178
+ continue;
179
+ totalLines++;
180
+ try {
181
+ const event = source === "claude"
182
+ ? parseClaudeTranscriptLine(line)
183
+ : parseCopilotTranscriptLine(line);
184
+ if (event)
185
+ events.push(event);
186
+ }
187
+ catch {
188
+ // Malformed line — skip it.
189
+ }
190
+ }
191
+ return { events, newOffset: fileStat.size, totalLines };
192
+ };
193
+ //# sourceMappingURL=transcript-sources.js.map
@@ -1,11 +1,12 @@
1
1
  /**
2
- * Copilot session watcher detects active sessions by scanning
3
- * ~/.copilot/session-state/ for directories with events.jsonl files.
2
+ * Session watcher helpers for supported coding-agent transcript directories.
4
3
  */
5
4
  export interface WatchedSession {
6
5
  uuid: string;
7
6
  eventsPath: string;
8
7
  active: boolean;
8
+ source?: "copilot" | "claude";
9
9
  }
10
10
  export declare function discoverSessions(): WatchedSession[];
11
+ export declare function discoverClaudeSessions(): WatchedSession[];
11
12
  //# sourceMappingURL=watcher.d.ts.map
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Copilot session watcher detects active sessions by scanning
3
- * ~/.copilot/session-state/ for directories with events.jsonl files.
2
+ * Session watcher helpers for supported coding-agent transcript directories.
4
3
  */
5
4
  import fs from "node:fs";
6
5
  import path from "node:path";
@@ -31,8 +30,58 @@ export function discoverSessions() {
31
30
  uuid: entry,
32
31
  eventsPath,
33
32
  active: lockFiles.length > 0,
33
+ source: "copilot",
34
34
  });
35
35
  }
36
36
  return sessions;
37
37
  }
38
+ export function discoverClaudeSessions() {
39
+ const cfg = loadConfig();
40
+ if (!cfg.watchers.claude.enabled)
41
+ return [];
42
+ const baseDir = cfg.watchers.claude.path;
43
+ if (!fs.existsSync(baseDir))
44
+ return [];
45
+ const sessions = [];
46
+ for (const entry of fs.readdirSync(baseDir)) {
47
+ const projectPath = path.join(baseDir, entry);
48
+ let stat;
49
+ try {
50
+ stat = fs.statSync(projectPath);
51
+ }
52
+ catch {
53
+ continue;
54
+ }
55
+ if (stat.isFile() && entry.endsWith(".jsonl")) {
56
+ sessions.push({
57
+ uuid: path.basename(entry, ".jsonl"),
58
+ eventsPath: projectPath,
59
+ active: true,
60
+ source: "claude",
61
+ });
62
+ continue;
63
+ }
64
+ if (!stat.isDirectory())
65
+ continue;
66
+ for (const file of fs.readdirSync(projectPath)) {
67
+ const transcriptPath = path.join(projectPath, file);
68
+ let transcriptStat;
69
+ try {
70
+ transcriptStat = fs.statSync(transcriptPath);
71
+ }
72
+ catch {
73
+ continue;
74
+ }
75
+ if (!transcriptStat.isFile() || !file.endsWith(".jsonl"))
76
+ continue;
77
+ sessions.push({
78
+ uuid: path.basename(file, ".jsonl"),
79
+ eventsPath: transcriptPath,
80
+ active: true,
81
+ source: "claude",
82
+ });
83
+ }
84
+ }
85
+ return sessions;
86
+ }
38
87
  //# sourceMappingURL=watcher.js.map
package/dist/install.d.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  /**
2
2
  * Write MCP config entries for Copilot and/or Claude Code.
3
3
  */
4
- export declare function writeInstallConfig(): Promise<void>;
4
+ export interface InstallOptions {
5
+ homeDir?: string;
6
+ /**
7
+ * Defaults to `claude`. Set to null to skip the Claude Code CLI and write the
8
+ * user config file directly.
9
+ */
10
+ claudeCommand?: string | null;
11
+ }
12
+ export declare function writeInstallConfig(options?: InstallOptions): Promise<void>;
5
13
  //# sourceMappingURL=install.d.ts.map