cognitive-core 0.2.0 → 0.2.2

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 (397) hide show
  1. package/.claude/settings.json +111 -2
  2. package/.sessionlog/settings.json +4 -0
  3. package/dist/atlas.d.ts +10 -0
  4. package/dist/atlas.d.ts.map +1 -1
  5. package/dist/atlas.js +65 -0
  6. package/dist/atlas.js.map +1 -1
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +5 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/learning/index.d.ts +1 -1
  12. package/dist/learning/index.d.ts.map +1 -1
  13. package/dist/learning/index.js.map +1 -1
  14. package/dist/learning/pipeline.d.ts +4 -31
  15. package/dist/learning/pipeline.d.ts.map +1 -1
  16. package/dist/learning/pipeline.js +12 -64
  17. package/dist/learning/pipeline.js.map +1 -1
  18. package/dist/learning/unified-pipeline.d.ts +30 -0
  19. package/dist/learning/unified-pipeline.d.ts.map +1 -1
  20. package/dist/learning/unified-pipeline.js +207 -0
  21. package/dist/learning/unified-pipeline.js.map +1 -1
  22. package/dist/memory/candidate-retrieval.d.ts.map +1 -1
  23. package/dist/memory/candidate-retrieval.js +3 -1
  24. package/dist/memory/candidate-retrieval.js.map +1 -1
  25. package/dist/memory/curated-loader.d.ts +21 -4
  26. package/dist/memory/curated-loader.d.ts.map +1 -1
  27. package/dist/memory/curated-loader.js +53 -16
  28. package/dist/memory/curated-loader.js.map +1 -1
  29. package/dist/memory/index.d.ts +2 -1
  30. package/dist/memory/index.d.ts.map +1 -1
  31. package/dist/memory/index.js +3 -1
  32. package/dist/memory/index.js.map +1 -1
  33. package/dist/memory/playbook.d.ts +6 -0
  34. package/dist/memory/playbook.d.ts.map +1 -1
  35. package/dist/memory/playbook.js +15 -0
  36. package/dist/memory/playbook.js.map +1 -1
  37. package/dist/memory/source-resolver.d.ts +120 -0
  38. package/dist/memory/source-resolver.d.ts.map +1 -0
  39. package/dist/memory/source-resolver.js +300 -0
  40. package/dist/memory/source-resolver.js.map +1 -0
  41. package/dist/types/config.d.ts +141 -0
  42. package/dist/types/config.d.ts.map +1 -1
  43. package/dist/types/config.js +40 -0
  44. package/dist/types/config.js.map +1 -1
  45. package/dist/types/index.d.ts +1 -1
  46. package/dist/types/index.d.ts.map +1 -1
  47. package/dist/types/index.js +1 -1
  48. package/dist/types/index.js.map +1 -1
  49. package/dist/utils/error-classifier.js +8 -8
  50. package/dist/utils/error-classifier.js.map +1 -1
  51. package/dist/workspace/efficacy-toolkit.d.ts +164 -0
  52. package/dist/workspace/efficacy-toolkit.d.ts.map +1 -0
  53. package/dist/workspace/efficacy-toolkit.js +281 -0
  54. package/dist/workspace/efficacy-toolkit.js.map +1 -0
  55. package/dist/workspace/index.d.ts +2 -1
  56. package/dist/workspace/index.d.ts.map +1 -1
  57. package/dist/workspace/index.js +3 -1
  58. package/dist/workspace/index.js.map +1 -1
  59. package/dist/workspace/templates/index.d.ts +3 -0
  60. package/dist/workspace/templates/index.d.ts.map +1 -1
  61. package/dist/workspace/templates/index.js +6 -0
  62. package/dist/workspace/templates/index.js.map +1 -1
  63. package/dist/workspace/templates/playbook-decay-detection.d.ts +46 -0
  64. package/dist/workspace/templates/playbook-decay-detection.d.ts.map +1 -0
  65. package/dist/workspace/templates/playbook-decay-detection.js +197 -0
  66. package/dist/workspace/templates/playbook-decay-detection.js.map +1 -0
  67. package/dist/workspace/templates/playbook-efficacy-audit.d.ts +46 -0
  68. package/dist/workspace/templates/playbook-efficacy-audit.d.ts.map +1 -0
  69. package/dist/workspace/templates/playbook-efficacy-audit.js +160 -0
  70. package/dist/workspace/templates/playbook-efficacy-audit.js.map +1 -0
  71. package/dist/workspace/templates/playbook-lifecycle-review.d.ts +51 -0
  72. package/dist/workspace/templates/playbook-lifecycle-review.d.ts.map +1 -0
  73. package/dist/workspace/templates/playbook-lifecycle-review.js +187 -0
  74. package/dist/workspace/templates/playbook-lifecycle-review.js.map +1 -0
  75. package/dist/workspace/types.d.ts +12 -54
  76. package/dist/workspace/types.d.ts.map +1 -1
  77. package/dist/workspace/types.js.map +1 -1
  78. package/package.json +8 -2
  79. package/playbooks/compound-engineering/adversarial-review.json +51 -0
  80. package/playbooks/compound-engineering/agent-native-architecture.json +59 -0
  81. package/playbooks/compound-engineering/agent-native-review.json +54 -0
  82. package/playbooks/compound-engineering/api-contract-review.json +52 -0
  83. package/playbooks/compound-engineering/brainstorm-requirements.json +55 -0
  84. package/playbooks/compound-engineering/bug-reproduction.json +62 -0
  85. package/playbooks/compound-engineering/confidence-calibration.json +49 -0
  86. package/playbooks/compound-engineering/correctness-review.json +49 -0
  87. package/playbooks/compound-engineering/data-migration-safety.json +59 -0
  88. package/playbooks/compound-engineering/deployment-verification.json +63 -0
  89. package/playbooks/compound-engineering/error-recovery-patterns.json +53 -0
  90. package/playbooks/compound-engineering/implementation-planning.json +64 -0
  91. package/playbooks/compound-engineering/issue-pattern-analysis.json +53 -0
  92. package/playbooks/compound-engineering/knowledge-compounding.json +63 -0
  93. package/playbooks/compound-engineering/learnings-research.json +54 -0
  94. package/playbooks/compound-engineering/maintainability-review.json +49 -0
  95. package/playbooks/compound-engineering/performance-review.json +54 -0
  96. package/playbooks/compound-engineering/plan-adversarial-review.json +56 -0
  97. package/playbooks/compound-engineering/plan-feasibility-review.json +56 -0
  98. package/playbooks/compound-engineering/project-standards-review.json +52 -0
  99. package/playbooks/compound-engineering/reliability-review.json +53 -0
  100. package/playbooks/compound-engineering/review-orchestration.json +64 -0
  101. package/playbooks/compound-engineering/security-review.json +54 -0
  102. package/playbooks/compound-engineering/systematic-execution.json +64 -0
  103. package/playbooks/compound-engineering/testing-review.json +50 -0
  104. package/src/atlas.ts +96 -0
  105. package/src/index.ts +27 -0
  106. package/src/learning/index.ts +1 -0
  107. package/src/learning/unified-pipeline.ts +271 -1
  108. package/src/memory/candidate-retrieval.ts +2 -1
  109. package/src/memory/curated-loader.ts +69 -16
  110. package/src/memory/index.ts +16 -0
  111. package/src/memory/playbook.ts +19 -0
  112. package/src/memory/source-resolver.ts +422 -0
  113. package/src/types/config.ts +46 -0
  114. package/src/types/index.ts +4 -0
  115. package/src/utils/error-classifier.ts +8 -8
  116. package/src/workspace/efficacy-toolkit.ts +496 -0
  117. package/src/workspace/index.ts +29 -0
  118. package/src/workspace/templates/index.ts +24 -0
  119. package/src/workspace/templates/playbook-decay-detection.ts +272 -0
  120. package/src/workspace/templates/playbook-efficacy-audit.ts +246 -0
  121. package/src/workspace/templates/playbook-lifecycle-review.ts +274 -0
  122. package/src/workspace/types.ts +22 -78
  123. package/tests/fixtures/behavioral-trajectories.ts +210 -0
  124. package/tests/integration/curated-sources-e2e.test.ts +502 -0
  125. package/tests/integration/pipeline-data-correctness.test.ts +794 -0
  126. package/tests/learning/meta-learner.test.ts +418 -0
  127. package/tests/learning/pipeline-memory-updates.test.ts +721 -0
  128. package/tests/learning/unified-pipeline-efficacy.test.ts +232 -0
  129. package/tests/memory/candidate-retrieval.test.ts +167 -0
  130. package/tests/memory/compound-engineering-seed.test.ts +338 -0
  131. package/tests/memory/curated-loader-extended.test.ts +225 -0
  132. package/tests/memory/meta.test.ts +399 -0
  133. package/tests/memory/playbook-quality-validation.test.ts +430 -0
  134. package/tests/memory/source-resolver.test.ts +700 -0
  135. package/tests/search/evaluator.test.ts +257 -0
  136. package/tests/search/verification-runner.test.ts +357 -0
  137. package/tests/utils/error-classifier.test.ts +149 -0
  138. package/tests/utils/trajectory-helpers.test.ts +163 -0
  139. package/tests/workspace/efficacy-toolkit.test.ts +404 -0
  140. package/tests/workspace/templates/playbook-efficacy.test.ts +377 -0
  141. package/.claude/settings.local.json +0 -11
  142. package/dist/learning/llm-extractor.d.ts +0 -88
  143. package/dist/learning/llm-extractor.d.ts.map +0 -1
  144. package/dist/learning/llm-extractor.js +0 -372
  145. package/dist/learning/llm-extractor.js.map +0 -1
  146. package/dist/learning/loop-coordinator.d.ts +0 -61
  147. package/dist/learning/loop-coordinator.d.ts.map +0 -1
  148. package/dist/learning/loop-coordinator.js +0 -96
  149. package/dist/learning/loop-coordinator.js.map +0 -1
  150. package/references/agent-workspace/CLAUDE.md +0 -74
  151. package/references/agent-workspace/README.md +0 -587
  152. package/references/agent-workspace/media/banner.png +0 -0
  153. package/references/agent-workspace/package-lock.json +0 -2061
  154. package/references/agent-workspace/package.json +0 -54
  155. package/references/agent-workspace/src/handle.ts +0 -122
  156. package/references/agent-workspace/src/index.ts +0 -32
  157. package/references/agent-workspace/src/manager.ts +0 -102
  158. package/references/agent-workspace/src/readers/json.ts +0 -71
  159. package/references/agent-workspace/src/readers/markdown.ts +0 -37
  160. package/references/agent-workspace/src/readers/raw.ts +0 -27
  161. package/references/agent-workspace/src/types.ts +0 -68
  162. package/references/agent-workspace/src/validation.ts +0 -93
  163. package/references/agent-workspace/src/writers/json.ts +0 -17
  164. package/references/agent-workspace/src/writers/markdown.ts +0 -27
  165. package/references/agent-workspace/src/writers/raw.ts +0 -22
  166. package/references/agent-workspace/tests/errors.test.ts +0 -652
  167. package/references/agent-workspace/tests/handle.test.ts +0 -144
  168. package/references/agent-workspace/tests/manager.test.ts +0 -124
  169. package/references/agent-workspace/tests/readers.test.ts +0 -205
  170. package/references/agent-workspace/tests/validation.test.ts +0 -196
  171. package/references/agent-workspace/tests/writers.test.ts +0 -108
  172. package/references/agent-workspace/tsconfig.json +0 -20
  173. package/references/agent-workspace/tsup.config.ts +0 -9
  174. package/references/minimem/.claude/settings.json +0 -7
  175. package/references/minimem/.sudocode/issues.jsonl +0 -18
  176. package/references/minimem/.sudocode/specs.jsonl +0 -1
  177. package/references/minimem/CLAUDE.md +0 -310
  178. package/references/minimem/README.md +0 -556
  179. package/references/minimem/claude-plugin/.claude-plugin/plugin.json +0 -10
  180. package/references/minimem/claude-plugin/.mcp.json +0 -7
  181. package/references/minimem/claude-plugin/README.md +0 -158
  182. package/references/minimem/claude-plugin/commands/recall.md +0 -47
  183. package/references/minimem/claude-plugin/commands/remember.md +0 -41
  184. package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +0 -272
  185. package/references/minimem/claude-plugin/hooks/hooks.json +0 -27
  186. package/references/minimem/claude-plugin/hooks/session-end.sh +0 -86
  187. package/references/minimem/claude-plugin/hooks/session-start.sh +0 -85
  188. package/references/minimem/claude-plugin/skills/memory/SKILL.md +0 -108
  189. package/references/minimem/package-lock.json +0 -5373
  190. package/references/minimem/package.json +0 -60
  191. package/references/minimem/scripts/postbuild.js +0 -35
  192. package/references/minimem/src/__tests__/edge-cases.test.ts +0 -371
  193. package/references/minimem/src/__tests__/errors.test.ts +0 -265
  194. package/references/minimem/src/__tests__/helpers.ts +0 -199
  195. package/references/minimem/src/__tests__/internal.test.ts +0 -407
  196. package/references/minimem/src/__tests__/knowledge.test.ts +0 -287
  197. package/references/minimem/src/__tests__/minimem.integration.test.ts +0 -1127
  198. package/references/minimem/src/__tests__/session.test.ts +0 -190
  199. package/references/minimem/src/cli/__tests__/commands.test.ts +0 -759
  200. package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +0 -141
  201. package/references/minimem/src/cli/commands/append.ts +0 -76
  202. package/references/minimem/src/cli/commands/config.ts +0 -262
  203. package/references/minimem/src/cli/commands/conflicts.ts +0 -413
  204. package/references/minimem/src/cli/commands/daemon.ts +0 -169
  205. package/references/minimem/src/cli/commands/index.ts +0 -12
  206. package/references/minimem/src/cli/commands/init.ts +0 -88
  207. package/references/minimem/src/cli/commands/mcp.ts +0 -177
  208. package/references/minimem/src/cli/commands/push-pull.ts +0 -213
  209. package/references/minimem/src/cli/commands/search.ts +0 -158
  210. package/references/minimem/src/cli/commands/status.ts +0 -84
  211. package/references/minimem/src/cli/commands/sync-init.ts +0 -290
  212. package/references/minimem/src/cli/commands/sync.ts +0 -70
  213. package/references/minimem/src/cli/commands/upsert.ts +0 -197
  214. package/references/minimem/src/cli/config.ts +0 -584
  215. package/references/minimem/src/cli/index.ts +0 -264
  216. package/references/minimem/src/cli/shared.ts +0 -161
  217. package/references/minimem/src/cli/sync/__tests__/central.test.ts +0 -152
  218. package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +0 -209
  219. package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +0 -118
  220. package/references/minimem/src/cli/sync/__tests__/detection.test.ts +0 -207
  221. package/references/minimem/src/cli/sync/__tests__/integration.test.ts +0 -476
  222. package/references/minimem/src/cli/sync/__tests__/registry.test.ts +0 -363
  223. package/references/minimem/src/cli/sync/__tests__/state.test.ts +0 -255
  224. package/references/minimem/src/cli/sync/__tests__/validation.test.ts +0 -193
  225. package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +0 -178
  226. package/references/minimem/src/cli/sync/central.ts +0 -292
  227. package/references/minimem/src/cli/sync/conflicts.ts +0 -204
  228. package/references/minimem/src/cli/sync/daemon.ts +0 -407
  229. package/references/minimem/src/cli/sync/detection.ts +0 -138
  230. package/references/minimem/src/cli/sync/index.ts +0 -107
  231. package/references/minimem/src/cli/sync/operations.ts +0 -373
  232. package/references/minimem/src/cli/sync/registry.ts +0 -279
  233. package/references/minimem/src/cli/sync/state.ts +0 -355
  234. package/references/minimem/src/cli/sync/validation.ts +0 -206
  235. package/references/minimem/src/cli/sync/watcher.ts +0 -234
  236. package/references/minimem/src/cli/version.ts +0 -34
  237. package/references/minimem/src/core/index.ts +0 -9
  238. package/references/minimem/src/core/indexer.ts +0 -628
  239. package/references/minimem/src/core/searcher.ts +0 -221
  240. package/references/minimem/src/db/schema.ts +0 -183
  241. package/references/minimem/src/db/sqlite-vec.ts +0 -24
  242. package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +0 -431
  243. package/references/minimem/src/embeddings/batch-gemini.ts +0 -392
  244. package/references/minimem/src/embeddings/batch-openai.ts +0 -409
  245. package/references/minimem/src/embeddings/embeddings.ts +0 -434
  246. package/references/minimem/src/index.ts +0 -109
  247. package/references/minimem/src/internal.ts +0 -299
  248. package/references/minimem/src/minimem.ts +0 -1276
  249. package/references/minimem/src/search/__tests__/hybrid.test.ts +0 -247
  250. package/references/minimem/src/search/graph.ts +0 -234
  251. package/references/minimem/src/search/hybrid.ts +0 -151
  252. package/references/minimem/src/search/search.ts +0 -256
  253. package/references/minimem/src/server/__tests__/mcp.test.ts +0 -341
  254. package/references/minimem/src/server/__tests__/tools.test.ts +0 -364
  255. package/references/minimem/src/server/mcp.ts +0 -326
  256. package/references/minimem/src/server/tools.ts +0 -720
  257. package/references/minimem/src/session.ts +0 -460
  258. package/references/minimem/tsconfig.json +0 -19
  259. package/references/minimem/tsup.config.ts +0 -26
  260. package/references/minimem/vitest.config.ts +0 -24
  261. package/references/sessionlog/.husky/pre-commit +0 -1
  262. package/references/sessionlog/.lintstagedrc.json +0 -4
  263. package/references/sessionlog/.prettierignore +0 -4
  264. package/references/sessionlog/.prettierrc.json +0 -11
  265. package/references/sessionlog/LICENSE +0 -21
  266. package/references/sessionlog/README.md +0 -453
  267. package/references/sessionlog/eslint.config.js +0 -58
  268. package/references/sessionlog/package-lock.json +0 -3672
  269. package/references/sessionlog/package.json +0 -65
  270. package/references/sessionlog/src/__tests__/agent-hooks.test.ts +0 -570
  271. package/references/sessionlog/src/__tests__/agent-registry.test.ts +0 -127
  272. package/references/sessionlog/src/__tests__/claude-code-hooks.test.ts +0 -225
  273. package/references/sessionlog/src/__tests__/claude-generator.test.ts +0 -46
  274. package/references/sessionlog/src/__tests__/commit-msg.test.ts +0 -86
  275. package/references/sessionlog/src/__tests__/cursor-agent.test.ts +0 -224
  276. package/references/sessionlog/src/__tests__/e2e-live.test.ts +0 -890
  277. package/references/sessionlog/src/__tests__/event-log.test.ts +0 -183
  278. package/references/sessionlog/src/__tests__/flush-sentinel.test.ts +0 -105
  279. package/references/sessionlog/src/__tests__/gemini-agent.test.ts +0 -375
  280. package/references/sessionlog/src/__tests__/git-hooks.test.ts +0 -78
  281. package/references/sessionlog/src/__tests__/hook-managers.test.ts +0 -121
  282. package/references/sessionlog/src/__tests__/lifecycle-tasks.test.ts +0 -759
  283. package/references/sessionlog/src/__tests__/opencode-agent.test.ts +0 -338
  284. package/references/sessionlog/src/__tests__/redaction.test.ts +0 -136
  285. package/references/sessionlog/src/__tests__/session-repo.test.ts +0 -353
  286. package/references/sessionlog/src/__tests__/session-store.test.ts +0 -166
  287. package/references/sessionlog/src/__tests__/setup-ccweb.test.ts +0 -466
  288. package/references/sessionlog/src/__tests__/skill-live.test.ts +0 -461
  289. package/references/sessionlog/src/__tests__/summarize.test.ts +0 -348
  290. package/references/sessionlog/src/__tests__/task-plan-e2e.test.ts +0 -610
  291. package/references/sessionlog/src/__tests__/task-plan-live.test.ts +0 -632
  292. package/references/sessionlog/src/__tests__/transcript-timestamp.test.ts +0 -121
  293. package/references/sessionlog/src/__tests__/types.test.ts +0 -166
  294. package/references/sessionlog/src/__tests__/utils.test.ts +0 -333
  295. package/references/sessionlog/src/__tests__/validation.test.ts +0 -103
  296. package/references/sessionlog/src/__tests__/worktree.test.ts +0 -57
  297. package/references/sessionlog/src/agent/agents/claude-code.ts +0 -1089
  298. package/references/sessionlog/src/agent/agents/cursor.ts +0 -361
  299. package/references/sessionlog/src/agent/agents/gemini-cli.ts +0 -632
  300. package/references/sessionlog/src/agent/agents/opencode.ts +0 -540
  301. package/references/sessionlog/src/agent/registry.ts +0 -143
  302. package/references/sessionlog/src/agent/session-types.ts +0 -113
  303. package/references/sessionlog/src/agent/types.ts +0 -220
  304. package/references/sessionlog/src/cli.ts +0 -597
  305. package/references/sessionlog/src/commands/clean.ts +0 -133
  306. package/references/sessionlog/src/commands/disable.ts +0 -84
  307. package/references/sessionlog/src/commands/doctor.ts +0 -145
  308. package/references/sessionlog/src/commands/enable.ts +0 -202
  309. package/references/sessionlog/src/commands/explain.ts +0 -261
  310. package/references/sessionlog/src/commands/reset.ts +0 -105
  311. package/references/sessionlog/src/commands/resume.ts +0 -180
  312. package/references/sessionlog/src/commands/rewind.ts +0 -195
  313. package/references/sessionlog/src/commands/setup-ccweb.ts +0 -275
  314. package/references/sessionlog/src/commands/status.ts +0 -172
  315. package/references/sessionlog/src/config.ts +0 -165
  316. package/references/sessionlog/src/events/event-log.ts +0 -126
  317. package/references/sessionlog/src/git-operations.ts +0 -558
  318. package/references/sessionlog/src/hooks/git-hooks.ts +0 -165
  319. package/references/sessionlog/src/hooks/lifecycle.ts +0 -391
  320. package/references/sessionlog/src/index.ts +0 -650
  321. package/references/sessionlog/src/security/redaction.ts +0 -283
  322. package/references/sessionlog/src/session/state-machine.ts +0 -452
  323. package/references/sessionlog/src/store/checkpoint-store.ts +0 -509
  324. package/references/sessionlog/src/store/native-store.ts +0 -173
  325. package/references/sessionlog/src/store/provider-types.ts +0 -99
  326. package/references/sessionlog/src/store/session-store.ts +0 -266
  327. package/references/sessionlog/src/strategy/attribution.ts +0 -296
  328. package/references/sessionlog/src/strategy/common.ts +0 -207
  329. package/references/sessionlog/src/strategy/content-overlap.ts +0 -228
  330. package/references/sessionlog/src/strategy/manual-commit.ts +0 -988
  331. package/references/sessionlog/src/strategy/types.ts +0 -279
  332. package/references/sessionlog/src/summarize/claude-generator.ts +0 -115
  333. package/references/sessionlog/src/summarize/summarize.ts +0 -432
  334. package/references/sessionlog/src/types.ts +0 -508
  335. package/references/sessionlog/src/utils/chunk-files.ts +0 -49
  336. package/references/sessionlog/src/utils/commit-message.ts +0 -65
  337. package/references/sessionlog/src/utils/detect-agent.ts +0 -36
  338. package/references/sessionlog/src/utils/hook-managers.ts +0 -125
  339. package/references/sessionlog/src/utils/ide-tags.ts +0 -32
  340. package/references/sessionlog/src/utils/paths.ts +0 -79
  341. package/references/sessionlog/src/utils/preview-rewind.ts +0 -80
  342. package/references/sessionlog/src/utils/rewind-conflict.ts +0 -121
  343. package/references/sessionlog/src/utils/shadow-branch.ts +0 -109
  344. package/references/sessionlog/src/utils/string-utils.ts +0 -46
  345. package/references/sessionlog/src/utils/todo-extract.ts +0 -188
  346. package/references/sessionlog/src/utils/trailers.ts +0 -187
  347. package/references/sessionlog/src/utils/transcript-parse.ts +0 -177
  348. package/references/sessionlog/src/utils/transcript-timestamp.ts +0 -59
  349. package/references/sessionlog/src/utils/tree-ops.ts +0 -219
  350. package/references/sessionlog/src/utils/tty.ts +0 -72
  351. package/references/sessionlog/src/utils/validation.ts +0 -65
  352. package/references/sessionlog/src/utils/worktree.ts +0 -58
  353. package/references/sessionlog/src/wire-types.ts +0 -59
  354. package/references/sessionlog/templates/setup-env.sh +0 -153
  355. package/references/sessionlog/tsconfig.json +0 -18
  356. package/references/sessionlog/vitest.config.ts +0 -12
  357. package/references/skill-tree/.claude/settings.json +0 -6
  358. package/references/skill-tree/.sudocode/issues.jsonl +0 -19
  359. package/references/skill-tree/.sudocode/specs.jsonl +0 -3
  360. package/references/skill-tree/CLAUDE.md +0 -126
  361. package/references/skill-tree/README.md +0 -372
  362. package/references/skill-tree/docs/GAPS_v1.md +0 -221
  363. package/references/skill-tree/docs/INTEGRATION_PLAN.md +0 -467
  364. package/references/skill-tree/docs/TODOS.md +0 -91
  365. package/references/skill-tree/docs/anthropic_skill_guide.md +0 -1364
  366. package/references/skill-tree/docs/design/federated-skill-trees.md +0 -524
  367. package/references/skill-tree/docs/design/multi-agent-sync.md +0 -759
  368. package/references/skill-tree/docs/scraper/BRAINSTORM.md +0 -583
  369. package/references/skill-tree/docs/scraper/POC_PLAN.md +0 -420
  370. package/references/skill-tree/docs/scraper/README.md +0 -170
  371. package/references/skill-tree/examples/basic-usage.ts +0 -164
  372. package/references/skill-tree/package-lock.json +0 -1852
  373. package/references/skill-tree/package.json +0 -66
  374. package/references/skill-tree/scraper/README.md +0 -123
  375. package/references/skill-tree/scraper/docs/DESIGN.md +0 -683
  376. package/references/skill-tree/scraper/docs/PLAN.md +0 -336
  377. package/references/skill-tree/scraper/drizzle.config.ts +0 -10
  378. package/references/skill-tree/scraper/package-lock.json +0 -6329
  379. package/references/skill-tree/scraper/package.json +0 -68
  380. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +0 -7
  381. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +0 -7
  382. package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +0 -27
  383. package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +0 -21
  384. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +0 -54
  385. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +0 -24
  386. package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +0 -93
  387. package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +0 -22
  388. package/references/skill-tree/scraper/tsup.config.ts +0 -14
  389. package/references/skill-tree/scraper/vitest.config.ts +0 -17
  390. package/references/skill-tree/scripts/convert-to-vitest.ts +0 -166
  391. package/references/skill-tree/skills/skill-writer/SKILL.md +0 -339
  392. package/references/skill-tree/skills/skill-writer/references/examples.md +0 -326
  393. package/references/skill-tree/skills/skill-writer/references/patterns.md +0 -210
  394. package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +0 -123
  395. package/references/skill-tree/test/run-all.ts +0 -106
  396. package/references/skill-tree/test/utils.ts +0 -128
  397. package/references/skill-tree/vitest.config.ts +0 -16
@@ -1,1089 +0,0 @@
1
- /**
2
- * Claude Code Agent
3
- *
4
- * Implementation of the Sessionlog agent interface for Anthropic's Claude Code.
5
- * Handles JSONL transcript format, Claude-specific hook installation,
6
- * and session lifecycle management.
7
- */
8
-
9
- import * as fs from 'node:fs';
10
- import * as path from 'node:path';
11
- import * as os from 'node:os';
12
- import * as crypto from 'node:crypto';
13
- import {
14
- AGENT_NAMES,
15
- AGENT_TYPES,
16
- type HookInput,
17
- type Event,
18
- type TokenUsage,
19
- EventType,
20
- emptyTokenUsage,
21
- } from '../../types.js';
22
- import type {
23
- Agent,
24
- HookSupport,
25
- TranscriptAnalyzer,
26
- TokenCalculator,
27
- TranscriptChunker,
28
- TranscriptPreparer,
29
- SubagentAwareExtractor,
30
- } from '../types.js';
31
- import { registerAgent } from '../registry.js';
32
-
33
- // ============================================================================
34
- // Constants
35
- // ============================================================================
36
-
37
- const CLAUDE_DIR = '.claude';
38
- const CLAUDE_SETTINGS_FILE = '.claude/settings.json';
39
-
40
- const HOOK_NAMES = [
41
- 'session-start',
42
- 'session-end',
43
- 'stop',
44
- 'user-prompt-submit',
45
- 'pre-task',
46
- 'post-task',
47
- 'post-todo',
48
- 'post-task-create',
49
- 'post-task-update',
50
- 'post-plan-enter',
51
- 'post-plan-exit',
52
- 'post-skill',
53
- ] as const;
54
-
55
- /** Tools that modify files (detected in transcript) */
56
- const FILE_MODIFICATION_TOOLS = new Set([
57
- 'Write',
58
- 'Edit',
59
- 'NotebookEdit',
60
- 'mcp__acp__Write',
61
- 'mcp__acp__Edit',
62
- ]);
63
-
64
- /** Deny rule to prevent agents from reading metadata */
65
- const METADATA_DENY_RULE = 'Read(./.sessionlog/metadata/**)';
66
-
67
- // ============================================================================
68
- // Transcript Types (JSONL)
69
- // ============================================================================
70
-
71
- export interface TranscriptLine {
72
- type: 'user' | 'assistant';
73
- uuid?: string;
74
- message: unknown;
75
- }
76
-
77
- export interface AssistantContent {
78
- type: string;
79
- text?: string;
80
- name?: string;
81
- input?: Record<string, unknown>;
82
- id?: string;
83
- }
84
-
85
- interface MessageUsage {
86
- input_tokens?: number;
87
- cache_creation_input_tokens?: number;
88
- cache_read_input_tokens?: number;
89
- output_tokens?: number;
90
- }
91
-
92
- // ============================================================================
93
- // Claude Code Settings Types
94
- // ============================================================================
95
-
96
- interface ClaudeHookEntry {
97
- type: string;
98
- command: string;
99
- }
100
-
101
- interface ClaudeHookMatcher {
102
- matcher: string;
103
- hooks: ClaudeHookEntry[];
104
- }
105
-
106
- interface ClaudeSettings {
107
- hooks?: {
108
- SessionStart?: ClaudeHookMatcher[];
109
- SessionEnd?: ClaudeHookMatcher[];
110
- UserPromptSubmit?: ClaudeHookMatcher[];
111
- Stop?: ClaudeHookMatcher[];
112
- PreToolUse?: ClaudeHookMatcher[];
113
- PostToolUse?: ClaudeHookMatcher[];
114
- };
115
- permissions?: {
116
- deny?: string[];
117
- };
118
- [key: string]: unknown;
119
- }
120
-
121
- // ============================================================================
122
- // Claude Code Agent Implementation
123
- // ============================================================================
124
-
125
- class ClaudeCodeAgent
126
- implements
127
- Agent,
128
- HookSupport,
129
- TranscriptAnalyzer,
130
- TokenCalculator,
131
- TranscriptChunker,
132
- TranscriptPreparer,
133
- SubagentAwareExtractor
134
- {
135
- readonly name = AGENT_NAMES.CLAUDE_CODE;
136
- readonly type = AGENT_TYPES.CLAUDE_CODE;
137
- readonly description = 'Anthropic Claude Code CLI';
138
- readonly isPreview = false;
139
- readonly protectedDirs = [CLAUDE_DIR];
140
-
141
- async detectPresence(cwd?: string): Promise<boolean> {
142
- const repoRoot = cwd ?? process.cwd();
143
- const claudeDir = path.join(repoRoot, CLAUDE_DIR);
144
- try {
145
- const stat = await fs.promises.stat(claudeDir);
146
- return stat.isDirectory();
147
- } catch {
148
- return false;
149
- }
150
- }
151
-
152
- async getSessionDir(repoPath: string): Promise<string> {
153
- // Claude Code stores sessions in ~/.claude/projects/<sanitized-path>/
154
- const sanitized = sanitizePathForClaude(repoPath);
155
- return path.join(os.homedir(), '.claude', 'projects', sanitized);
156
- }
157
-
158
- getSessionID(input: HookInput): string {
159
- return input.sessionID;
160
- }
161
-
162
- resolveSessionFile(sessionDir: string, agentSessionID: string): string {
163
- return path.join(sessionDir, `${agentSessionID}.jsonl`);
164
- }
165
-
166
- async readTranscript(sessionRef: string): Promise<Buffer> {
167
- return fs.promises.readFile(sessionRef);
168
- }
169
-
170
- formatResumeCommand(sessionID: string): string {
171
- return `claude --resume ${sessionID}`;
172
- }
173
-
174
- // ===========================================================================
175
- // HookSupport
176
- // ===========================================================================
177
-
178
- hookNames(): string[] {
179
- return [...HOOK_NAMES];
180
- }
181
-
182
- parseHookEvent(hookName: string, stdin: string): Event | null {
183
- try {
184
- const data = JSON.parse(stdin) as Record<string, unknown>;
185
-
186
- switch (hookName) {
187
- case 'session-start':
188
- return {
189
- type: EventType.SessionStart,
190
- sessionID: String(data.session_id ?? data.sessionID ?? ''),
191
- sessionRef: String(data.transcript_path ?? data.transcriptPath ?? ''),
192
- timestamp: new Date(),
193
- };
194
-
195
- case 'user-prompt-submit':
196
- return {
197
- type: EventType.TurnStart,
198
- sessionID: String(data.session_id ?? data.sessionID ?? ''),
199
- sessionRef: String(data.transcript_path ?? data.transcriptPath ?? ''),
200
- prompt: String(data.prompt ?? ''),
201
- timestamp: new Date(),
202
- };
203
-
204
- case 'stop':
205
- return {
206
- type: EventType.TurnEnd,
207
- sessionID: String(data.session_id ?? data.sessionID ?? ''),
208
- sessionRef: String(data.transcript_path ?? data.transcriptPath ?? ''),
209
- timestamp: new Date(),
210
- };
211
-
212
- case 'session-end':
213
- return {
214
- type: EventType.SessionEnd,
215
- sessionID: String(data.session_id ?? data.sessionID ?? ''),
216
- sessionRef: String(data.transcript_path ?? data.transcriptPath ?? ''),
217
- timestamp: new Date(),
218
- };
219
-
220
- case 'pre-task':
221
- return {
222
- type: EventType.SubagentStart,
223
- sessionID: String(data.session_id ?? data.sessionID ?? ''),
224
- sessionRef: String(data.transcript_path ?? data.transcriptPath ?? ''),
225
- toolUseID: String(data.tool_use_id ?? data.toolUseID ?? ''),
226
- toolInput: data.tool_input ?? data.toolInput,
227
- timestamp: new Date(),
228
- };
229
-
230
- case 'post-task':
231
- return {
232
- type: EventType.SubagentEnd,
233
- sessionID: String(data.session_id ?? data.sessionID ?? ''),
234
- sessionRef: String(data.transcript_path ?? data.transcriptPath ?? ''),
235
- toolUseID: String(data.tool_use_id ?? data.toolUseID ?? ''),
236
- subagentID: (data.tool_response as Record<string, unknown>)?.agentId as string,
237
- timestamp: new Date(),
238
- };
239
-
240
- case 'post-todo':
241
- return {
242
- type: EventType.Compaction,
243
- sessionID: String(data.session_id ?? data.sessionID ?? ''),
244
- sessionRef: String(data.transcript_path ?? data.transcriptPath ?? ''),
245
- timestamp: new Date(),
246
- };
247
-
248
- case 'post-task-create': {
249
- const tcInput = (data.tool_input ?? data.toolInput) as
250
- | Record<string, unknown>
251
- | undefined;
252
- const tcResponse = (data.tool_response ?? data.toolResponse) as
253
- | Record<string, unknown>
254
- | undefined;
255
- return {
256
- type: EventType.TaskCreate,
257
- sessionID: String(data.session_id ?? data.sessionID ?? ''),
258
- sessionRef: String(data.transcript_path ?? data.transcriptPath ?? ''),
259
- toolUseID: String(data.tool_use_id ?? data.toolUseID ?? ''),
260
- taskID: String(tcResponse?.taskId ?? tcResponse?.id ?? ''),
261
- taskSubject: String(tcInput?.subject ?? ''),
262
- taskActiveForm: tcInput?.activeForm as string | undefined,
263
- taskDescription: tcInput?.description as string | undefined,
264
- toolInput: tcInput,
265
- timestamp: new Date(),
266
- };
267
- }
268
-
269
- case 'post-task-update': {
270
- const tuInput = (data.tool_input ?? data.toolInput) as
271
- | Record<string, unknown>
272
- | undefined;
273
- return {
274
- type: EventType.TaskUpdate,
275
- sessionID: String(data.session_id ?? data.sessionID ?? ''),
276
- sessionRef: String(data.transcript_path ?? data.transcriptPath ?? ''),
277
- toolUseID: String(data.tool_use_id ?? data.toolUseID ?? ''),
278
- taskID: String(tuInput?.taskId ?? ''),
279
- taskStatus: tuInput?.status as string | undefined,
280
- taskSubject: tuInput?.subject as string | undefined,
281
- taskDescription: tuInput?.description as string | undefined,
282
- toolInput: tuInput,
283
- timestamp: new Date(),
284
- };
285
- }
286
-
287
- case 'post-plan-enter':
288
- return {
289
- type: EventType.PlanModeEnter,
290
- sessionID: String(data.session_id ?? data.sessionID ?? ''),
291
- sessionRef: String(data.transcript_path ?? data.transcriptPath ?? ''),
292
- timestamp: new Date(),
293
- };
294
-
295
- case 'post-plan-exit': {
296
- const peInput = (data.tool_input ?? data.toolInput) as
297
- | Record<string, unknown>
298
- | undefined;
299
- const peResponse = (data.tool_response ?? data.toolResponse) as
300
- | Record<string, unknown>
301
- | string
302
- | undefined;
303
- // Extract plan file path from tool_response
304
- let planFilePath: string | undefined;
305
- if (typeof peResponse === 'object' && peResponse !== null) {
306
- planFilePath = (peResponse.planFilePath ??
307
- peResponse.planFile ??
308
- peResponse.file_path) as string | undefined;
309
- // Also try parsing from a message like "Your plan has been saved to: <path>"
310
- if (!planFilePath && typeof peResponse.content === 'string') {
311
- const match = peResponse.content.match(/saved to:\s*(.+)/i);
312
- if (match) planFilePath = match[1].trim();
313
- }
314
- } else if (typeof peResponse === 'string') {
315
- const match = peResponse.match(/saved to:\s*(.+)/i);
316
- if (match) planFilePath = match[1].trim();
317
- }
318
- return {
319
- type: EventType.PlanModeExit,
320
- sessionID: String(data.session_id ?? data.sessionID ?? ''),
321
- sessionRef: String(data.transcript_path ?? data.transcriptPath ?? ''),
322
- planAllowedPrompts: peInput?.allowedPrompts as
323
- | Array<{ tool: string; prompt: string }>
324
- | undefined,
325
- planFilePath,
326
- timestamp: new Date(),
327
- };
328
- }
329
-
330
- case 'post-skill': {
331
- const skInput = (data.tool_input ?? data.toolInput) as
332
- | Record<string, unknown>
333
- | undefined;
334
- return {
335
- type: EventType.SkillUse,
336
- sessionID: String(data.session_id ?? data.sessionID ?? ''),
337
- sessionRef: String(data.transcript_path ?? data.transcriptPath ?? ''),
338
- toolUseID: String(data.tool_use_id ?? data.toolUseID ?? ''),
339
- skillName: String(skInput?.skill ?? ''),
340
- skillArgs: skInput?.args as string | undefined,
341
- toolInput: skInput,
342
- timestamp: new Date(),
343
- };
344
- }
345
-
346
- default:
347
- return null;
348
- }
349
- } catch {
350
- return null;
351
- }
352
- }
353
-
354
- async installHooks(repoPath: string, force = false): Promise<number> {
355
- const settingsPath = path.join(repoPath, CLAUDE_SETTINGS_FILE);
356
- let settings: ClaudeSettings = {};
357
-
358
- // Read existing settings
359
- try {
360
- const content = await fs.promises.readFile(settingsPath, 'utf-8');
361
- settings = JSON.parse(content) as ClaudeSettings;
362
- } catch {
363
- // No existing settings
364
- }
365
-
366
- if (!settings.hooks) settings.hooks = {};
367
-
368
- let installed = 0;
369
-
370
- // Install lifecycle hooks
371
- const hookMappings: Array<{
372
- settingsKey: keyof NonNullable<ClaudeSettings['hooks']>;
373
- hookName: string;
374
- }> = [
375
- { settingsKey: 'SessionStart', hookName: 'session-start' },
376
- { settingsKey: 'SessionEnd', hookName: 'session-end' },
377
- { settingsKey: 'UserPromptSubmit', hookName: 'user-prompt-submit' },
378
- { settingsKey: 'Stop', hookName: 'stop' },
379
- ];
380
-
381
- // Task hooks (pre/post tool use for Task tool)
382
- const taskHookMappings: Array<{
383
- settingsKey: keyof NonNullable<ClaudeSettings['hooks']>;
384
- hookName: string;
385
- matcher: string;
386
- }> = [
387
- { settingsKey: 'PreToolUse', hookName: 'pre-task', matcher: 'Task' },
388
- { settingsKey: 'PostToolUse', hookName: 'post-task', matcher: 'Task' },
389
- { settingsKey: 'PostToolUse', hookName: 'post-todo', matcher: 'TodoWrite' },
390
- { settingsKey: 'PostToolUse', hookName: 'post-task-create', matcher: 'TaskCreate' },
391
- { settingsKey: 'PostToolUse', hookName: 'post-task-update', matcher: 'TaskUpdate' },
392
- { settingsKey: 'PostToolUse', hookName: 'post-plan-enter', matcher: 'EnterPlanMode' },
393
- { settingsKey: 'PostToolUse', hookName: 'post-plan-exit', matcher: 'ExitPlanMode' },
394
- { settingsKey: 'PostToolUse', hookName: 'post-skill', matcher: 'Skill' },
395
- ];
396
-
397
- for (const { settingsKey, hookName } of hookMappings) {
398
- const existing = settings.hooks[settingsKey] ?? [];
399
-
400
- if (force) {
401
- // Remove existing sessionlog hooks
402
- const filtered = existing.filter(
403
- (m) => !m.hooks.some((h) => h.command.includes('sessionlog ')),
404
- );
405
- settings.hooks[settingsKey] = filtered;
406
- }
407
-
408
- // Check if already installed
409
- const hasSessionlogHook = (settings.hooks[settingsKey] ?? []).some((m) =>
410
- m.hooks.some((h) => h.command.includes('sessionlog ')),
411
- );
412
-
413
- if (!hasSessionlogHook) {
414
- const matchers = settings.hooks[settingsKey] ?? [];
415
- matchers.push({
416
- matcher: '',
417
- hooks: [
418
- {
419
- type: 'command',
420
- command: `sessionlog hooks claude-code ${hookName}`,
421
- },
422
- ],
423
- });
424
- settings.hooks[settingsKey] = matchers;
425
- installed++;
426
- }
427
- }
428
-
429
- for (const { settingsKey, hookName, matcher } of taskHookMappings) {
430
- const existing = settings.hooks[settingsKey] ?? [];
431
-
432
- if (force) {
433
- const filtered = existing.filter(
434
- (m) => !(m.matcher === matcher && m.hooks.some((h) => h.command.includes('sessionlog '))),
435
- );
436
- settings.hooks[settingsKey] = filtered;
437
- }
438
-
439
- const hasSessionlogHook = (settings.hooks[settingsKey] ?? []).some(
440
- (m) => m.matcher === matcher && m.hooks.some((h) => h.command.includes('sessionlog ')),
441
- );
442
-
443
- if (!hasSessionlogHook) {
444
- const matchers = settings.hooks[settingsKey] ?? [];
445
- matchers.push({
446
- matcher,
447
- hooks: [
448
- {
449
- type: 'command',
450
- command: `sessionlog hooks claude-code ${hookName}`,
451
- },
452
- ],
453
- });
454
- settings.hooks[settingsKey] = matchers;
455
- installed++;
456
- }
457
- }
458
-
459
- // Add metadata deny rule
460
- if (!settings.permissions) settings.permissions = {};
461
- if (!settings.permissions.deny) settings.permissions.deny = [];
462
- if (!settings.permissions.deny.includes(METADATA_DENY_RULE)) {
463
- settings.permissions.deny.push(METADATA_DENY_RULE);
464
- }
465
-
466
- // Write settings
467
- const dir = path.dirname(settingsPath);
468
- await fs.promises.mkdir(dir, { recursive: true });
469
- await fs.promises.writeFile(settingsPath, JSON.stringify(settings, null, 2));
470
-
471
- return installed;
472
- }
473
-
474
- async uninstallHooks(repoPath: string): Promise<void> {
475
- const settingsPath = path.join(repoPath, CLAUDE_SETTINGS_FILE);
476
-
477
- try {
478
- const content = await fs.promises.readFile(settingsPath, 'utf-8');
479
- const settings = JSON.parse(content) as ClaudeSettings;
480
-
481
- if (settings.hooks) {
482
- for (const key of Object.keys(settings.hooks) as Array<
483
- keyof NonNullable<ClaudeSettings['hooks']>
484
- >) {
485
- const matchers = settings.hooks[key];
486
- if (!matchers) continue;
487
-
488
- settings.hooks[key] = matchers.filter(
489
- (m) => !m.hooks.some((h) => h.command.includes('sessionlog ')),
490
- );
491
-
492
- if (settings.hooks[key]!.length === 0) {
493
- delete settings.hooks[key];
494
- }
495
- }
496
-
497
- if (Object.keys(settings.hooks).length === 0) {
498
- delete settings.hooks;
499
- }
500
- }
501
-
502
- // Remove deny rule
503
- if (settings.permissions?.deny) {
504
- settings.permissions.deny = settings.permissions.deny.filter(
505
- (d) => d !== METADATA_DENY_RULE,
506
- );
507
- if (settings.permissions.deny.length === 0) {
508
- delete settings.permissions.deny;
509
- }
510
- if (Object.keys(settings.permissions).length === 0) {
511
- delete settings.permissions;
512
- }
513
- }
514
-
515
- await fs.promises.writeFile(settingsPath, JSON.stringify(settings, null, 2));
516
- } catch {
517
- // No settings to modify
518
- }
519
- }
520
-
521
- async areHooksInstalled(repoPath: string): Promise<boolean> {
522
- const settingsPath = path.join(repoPath, CLAUDE_SETTINGS_FILE);
523
-
524
- try {
525
- const content = await fs.promises.readFile(settingsPath, 'utf-8');
526
- const settings = JSON.parse(content) as ClaudeSettings;
527
-
528
- if (!settings.hooks) return false;
529
-
530
- // Check for at least the session-start hook
531
- const sessionStart = settings.hooks.SessionStart ?? [];
532
- return sessionStart.some((m) => m.hooks.some((h) => h.command.includes('sessionlog ')));
533
- } catch {
534
- return false;
535
- }
536
- }
537
-
538
- // ===========================================================================
539
- // TranscriptPreparer — wait for Claude Code's async transcript flush
540
- // ===========================================================================
541
-
542
- async prepareTranscript(sessionRef: string): Promise<void> {
543
- await waitForTranscriptFlush(sessionRef);
544
- }
545
-
546
- // ===========================================================================
547
- // TranscriptAnalyzer
548
- // ===========================================================================
549
-
550
- async getTranscriptPosition(transcriptPath: string): Promise<number> {
551
- try {
552
- const content = await fs.promises.readFile(transcriptPath, 'utf-8');
553
- const lines = content.split('\n').filter(Boolean);
554
- return lines.length;
555
- } catch {
556
- return 0;
557
- }
558
- }
559
-
560
- async extractModifiedFilesFromOffset(
561
- transcriptPath: string,
562
- startOffset: number,
563
- ): Promise<{ files: string[]; currentPosition: number }> {
564
- const content = await fs.promises.readFile(transcriptPath, 'utf-8');
565
- const lines = content.split('\n').filter(Boolean);
566
- const files = new Set<string>();
567
-
568
- for (let i = startOffset; i < lines.length; i++) {
569
- try {
570
- const line = JSON.parse(lines[i]) as TranscriptLine;
571
- if (line.type === 'assistant') {
572
- const contentBlocks = extractContentBlocks(line.message);
573
- for (const block of contentBlocks) {
574
- if (
575
- block.type === 'tool_use' &&
576
- block.name &&
577
- FILE_MODIFICATION_TOOLS.has(block.name)
578
- ) {
579
- const filePath = (block.input as Record<string, unknown>)?.file_path as string;
580
- if (filePath) files.add(filePath);
581
- }
582
- }
583
- }
584
- } catch {
585
- // Skip malformed lines
586
- }
587
- }
588
-
589
- return { files: Array.from(files), currentPosition: lines.length };
590
- }
591
-
592
- async extractPrompts(sessionRef: string, fromOffset: number): Promise<string[]> {
593
- const content = await fs.promises.readFile(sessionRef, 'utf-8');
594
- const lines = content.split('\n').filter(Boolean);
595
- const prompts: string[] = [];
596
-
597
- for (let i = fromOffset; i < lines.length; i++) {
598
- try {
599
- const line = JSON.parse(lines[i]) as TranscriptLine;
600
- if (line.type === 'user') {
601
- const text = extractUserText(line.message);
602
- if (text) prompts.push(text);
603
- }
604
- } catch {
605
- // Skip malformed lines
606
- }
607
- }
608
-
609
- return prompts;
610
- }
611
-
612
- async extractSummary(sessionRef: string): Promise<string> {
613
- const prompts = await this.extractPrompts(sessionRef, 0);
614
- if (prompts.length === 0) return '';
615
- // Return the first prompt as a summary, truncated
616
- return prompts[0].slice(0, 200);
617
- }
618
-
619
- // ===========================================================================
620
- // TokenCalculator
621
- // ===========================================================================
622
-
623
- async calculateTokenUsage(transcriptData: Buffer, fromOffset: number): Promise<TokenUsage> {
624
- const content = transcriptData.toString('utf-8');
625
- const lines = content.split('\n').filter(Boolean);
626
-
627
- // Deduplicate by message ID — streaming may produce multiple rows per message.
628
- // Keep the entry with the highest output_tokens (final streaming state).
629
- const usageByMessageID = new Map<string, MessageUsage>();
630
-
631
- for (let i = fromOffset; i < lines.length; i++) {
632
- try {
633
- const raw = JSON.parse(lines[i]) as Record<string, unknown>;
634
- if (raw.type === 'assistant') {
635
- const msg = raw.message as Record<string, unknown> | undefined;
636
- const msgID = msg?.id as string | undefined;
637
- const msgUsage = msg?.usage as MessageUsage | undefined;
638
- if (msgUsage && msgID) {
639
- const existing = usageByMessageID.get(msgID);
640
- if (!existing || (msgUsage.output_tokens ?? 0) > (existing.output_tokens ?? 0)) {
641
- usageByMessageID.set(msgID, msgUsage);
642
- }
643
- } else if (msgUsage) {
644
- // No message ID — count each occurrence
645
- usageByMessageID.set(`_anon_${i}`, msgUsage);
646
- }
647
- }
648
- } catch {
649
- // Skip malformed lines
650
- }
651
- }
652
-
653
- const usage = emptyTokenUsage();
654
- usage.apiCallCount = usageByMessageID.size;
655
- for (const u of usageByMessageID.values()) {
656
- usage.inputTokens += u.input_tokens ?? 0;
657
- usage.cacheCreationTokens += u.cache_creation_input_tokens ?? 0;
658
- usage.cacheReadTokens += u.cache_read_input_tokens ?? 0;
659
- usage.outputTokens += u.output_tokens ?? 0;
660
- }
661
-
662
- return usage;
663
- }
664
-
665
- // ===========================================================================
666
- // SubagentAwareExtractor
667
- // ===========================================================================
668
-
669
- async extractAllModifiedFiles(
670
- transcriptData: Buffer,
671
- fromOffset: number,
672
- subagentsDir: string,
673
- ): Promise<string[]> {
674
- if (transcriptData.length === 0) return [];
675
-
676
- const content = transcriptData.toString('utf-8');
677
- const allLines = content.split('\n').filter(Boolean);
678
- const sliced = allLines.slice(fromOffset);
679
- const parsed = sliced
680
- .map((line) => {
681
- try {
682
- return JSON.parse(line) as TranscriptLine;
683
- } catch {
684
- return null;
685
- }
686
- })
687
- .filter((l): l is TranscriptLine => l !== null);
688
-
689
- // Collect modified files from main agent
690
- const fileSet = new Set<string>();
691
- for (const f of extractModifiedFiles(parsed)) {
692
- fileSet.add(f);
693
- }
694
-
695
- // Find spawned subagents and collect their modified files
696
- const agentIDs = extractSpawnedAgentIDs(parsed);
697
- if (subagentsDir) {
698
- for (const agentID of agentIDs.keys()) {
699
- const agentPath = path.join(subagentsDir, `agent-${agentID}.jsonl`);
700
- try {
701
- const agentContent = await fs.promises.readFile(agentPath, 'utf-8');
702
- const agentLines = agentContent
703
- .split('\n')
704
- .filter(Boolean)
705
- .map((line) => {
706
- try {
707
- return JSON.parse(line) as TranscriptLine;
708
- } catch {
709
- return null;
710
- }
711
- })
712
- .filter((l): l is TranscriptLine => l !== null);
713
- for (const f of extractModifiedFiles(agentLines)) {
714
- fileSet.add(f);
715
- }
716
- } catch {
717
- // Subagent transcript may not exist yet
718
- }
719
- }
720
- }
721
-
722
- return Array.from(fileSet);
723
- }
724
-
725
- async calculateTotalTokenUsage(
726
- transcriptData: Buffer,
727
- fromOffset: number,
728
- subagentsDir: string,
729
- ): Promise<TokenUsage> {
730
- if (transcriptData.length === 0) return emptyTokenUsage();
731
-
732
- // Calculate main session token usage
733
- const mainUsage = await this.calculateTokenUsage(transcriptData, fromOffset);
734
-
735
- // Extract spawned agent IDs from the transcript
736
- const content = transcriptData.toString('utf-8');
737
- const allLines = content.split('\n').filter(Boolean);
738
- const sliced = allLines.slice(fromOffset);
739
- const parsed = sliced
740
- .map((line) => {
741
- try {
742
- return JSON.parse(line) as TranscriptLine;
743
- } catch {
744
- return null;
745
- }
746
- })
747
- .filter((l): l is TranscriptLine => l !== null);
748
-
749
- const agentIDs = extractSpawnedAgentIDs(parsed);
750
-
751
- // Calculate subagent token usage
752
- if (agentIDs.size > 0 && subagentsDir) {
753
- const subagentUsage = emptyTokenUsage();
754
- let hasSubagentUsage = false;
755
-
756
- for (const agentID of agentIDs.keys()) {
757
- const agentPath = path.join(subagentsDir, `agent-${agentID}.jsonl`);
758
- try {
759
- const agentData = await fs.promises.readFile(agentPath);
760
- const agentUsage = await this.calculateTokenUsage(agentData, 0);
761
- subagentUsage.inputTokens += agentUsage.inputTokens;
762
- subagentUsage.cacheCreationTokens += agentUsage.cacheCreationTokens;
763
- subagentUsage.cacheReadTokens += agentUsage.cacheReadTokens;
764
- subagentUsage.outputTokens += agentUsage.outputTokens;
765
- subagentUsage.apiCallCount += agentUsage.apiCallCount;
766
- hasSubagentUsage = true;
767
- } catch {
768
- // Agent transcript may not exist yet
769
- }
770
- }
771
-
772
- if (hasSubagentUsage && subagentUsage.apiCallCount > 0) {
773
- mainUsage.subagentTokens = subagentUsage;
774
- }
775
- }
776
-
777
- return mainUsage;
778
- }
779
-
780
- // ===========================================================================
781
- // TranscriptChunker
782
- // ===========================================================================
783
-
784
- async chunkTranscript(content: Buffer, maxSize: number): Promise<Buffer[]> {
785
- return chunkJSONL(content, maxSize);
786
- }
787
-
788
- async reassembleTranscript(chunks: Buffer[]): Promise<Buffer> {
789
- return Buffer.concat(chunks);
790
- }
791
- }
792
-
793
- // ============================================================================
794
- // Transcript Parsing Helpers
795
- // ============================================================================
796
-
797
- function extractContentBlocks(message: unknown): AssistantContent[] {
798
- if (!message || typeof message !== 'object') return [];
799
-
800
- const msg = message as Record<string, unknown>;
801
- const content = msg.content;
802
-
803
- if (Array.isArray(content)) {
804
- return content as AssistantContent[];
805
- }
806
-
807
- return [];
808
- }
809
-
810
- function extractUserText(message: unknown): string {
811
- if (typeof message === 'string') return message;
812
-
813
- if (!message || typeof message !== 'object') return '';
814
-
815
- const msg = message as Record<string, unknown>;
816
-
817
- // Content can be string or array of blocks
818
- if (typeof msg.content === 'string') return msg.content;
819
-
820
- if (Array.isArray(msg.content)) {
821
- return (msg.content as Array<{ type: string; text?: string }>)
822
- .filter((b) => b.type === 'text' && b.text)
823
- .map((b) => b.text!)
824
- .join('\n');
825
- }
826
-
827
- return '';
828
- }
829
-
830
- /**
831
- * Parse a JSONL transcript into structured lines
832
- */
833
- export function parseTranscript(content: string): TranscriptLine[] {
834
- const lines: TranscriptLine[] = [];
835
-
836
- for (const rawLine of content.split('\n').filter(Boolean)) {
837
- try {
838
- const parsed = JSON.parse(rawLine) as TranscriptLine;
839
- if (parsed.type === 'user' || parsed.type === 'assistant') {
840
- lines.push(parsed);
841
- }
842
- } catch {
843
- // Skip malformed lines
844
- }
845
- }
846
-
847
- return lines;
848
- }
849
-
850
- /**
851
- * Extract all modified files from transcript lines
852
- */
853
- export function extractModifiedFiles(lines: TranscriptLine[]): string[] {
854
- const files = new Set<string>();
855
-
856
- for (const line of lines) {
857
- if (line.type !== 'assistant') continue;
858
- const blocks = extractContentBlocks(line.message);
859
- for (const block of blocks) {
860
- if (block.type === 'tool_use' && block.name && FILE_MODIFICATION_TOOLS.has(block.name)) {
861
- const input = block.input as Record<string, unknown> | undefined;
862
- const filePath = (input?.file_path ?? input?.notebook_path) as string | undefined;
863
- if (filePath) files.add(filePath);
864
- }
865
- }
866
- }
867
-
868
- return Array.from(files);
869
- }
870
-
871
- /**
872
- * Extract the last user prompt from transcript lines
873
- */
874
- export function extractLastUserPrompt(lines: TranscriptLine[]): string {
875
- for (let i = lines.length - 1; i >= 0; i--) {
876
- if (lines[i].type === 'user') {
877
- return extractUserText(lines[i].message);
878
- }
879
- }
880
- return '';
881
- }
882
-
883
- // ============================================================================
884
- // Subagent ID Extraction
885
- // ============================================================================
886
-
887
- /**
888
- * Extract spawned agent IDs from Task tool results in a transcript.
889
- * When a Task tool completes, the tool_result contains "agentId: <id>".
890
- * Returns a map of agentID → toolUseID.
891
- */
892
- export function extractSpawnedAgentIDs(lines: TranscriptLine[]): Map<string, string> {
893
- const agentIDs = new Map<string, string>();
894
-
895
- for (const line of lines) {
896
- if (line.type !== 'user') continue;
897
-
898
- const msg = line.message as Record<string, unknown> | undefined;
899
- if (!msg) continue;
900
-
901
- const content = msg.content;
902
- if (!Array.isArray(content)) continue;
903
-
904
- for (const block of content as Array<Record<string, unknown>>) {
905
- if (block.type !== 'tool_result') continue;
906
-
907
- const toolUseID = String(block.tool_use_id ?? '');
908
- let textContent = '';
909
-
910
- // Content can be a string or array of text blocks
911
- if (typeof block.content === 'string') {
912
- textContent = block.content;
913
- } else if (Array.isArray(block.content)) {
914
- for (const tb of block.content as Array<Record<string, unknown>>) {
915
- if (tb.type === 'text' && typeof tb.text === 'string') {
916
- textContent += tb.text + '\n';
917
- }
918
- }
919
- }
920
-
921
- const agentID = extractAgentIDFromText(textContent);
922
- if (agentID) {
923
- agentIDs.set(agentID, toolUseID);
924
- }
925
- }
926
- }
927
-
928
- return agentIDs;
929
- }
930
-
931
- /**
932
- * Extract an agent ID from text containing "agentId: <id>".
933
- */
934
- function extractAgentIDFromText(text: string): string {
935
- const prefix = 'agentId: ';
936
- const idx = text.indexOf(prefix);
937
- if (idx === -1) return '';
938
-
939
- const start = idx + prefix.length;
940
- let end = start;
941
- while (end < text.length && /[a-zA-Z0-9]/.test(text[end])) {
942
- end++;
943
- }
944
-
945
- return end > start ? text.slice(start, end) : '';
946
- }
947
-
948
- // ============================================================================
949
- // JSONL Chunking
950
- // ============================================================================
951
-
952
- function chunkJSONL(content: Buffer, maxSize: number): Buffer[] {
953
- if (content.length <= maxSize) return [content];
954
-
955
- const str = content.toString('utf-8');
956
- const lines = str.split('\n');
957
- const chunks: Buffer[] = [];
958
- let current: string[] = [];
959
- let currentSize = 0;
960
-
961
- for (const line of lines) {
962
- const lineSize = Buffer.byteLength(line + '\n');
963
-
964
- if (currentSize + lineSize > maxSize && current.length > 0) {
965
- chunks.push(Buffer.from(current.join('\n') + '\n'));
966
- current = [];
967
- currentSize = 0;
968
- }
969
-
970
- current.push(line);
971
- currentSize += lineSize;
972
- }
973
-
974
- if (current.length > 0) {
975
- const remaining = current.join('\n');
976
- if (remaining.trim()) {
977
- chunks.push(Buffer.from(remaining + '\n'));
978
- }
979
- }
980
-
981
- return chunks;
982
- }
983
-
984
- // ============================================================================
985
- // Transcript Flush Sentinel
986
- // ============================================================================
987
-
988
- /**
989
- * String that appears in Claude Code's hook_progress entry when the stop hook
990
- * has been invoked, indicating the transcript is fully flushed.
991
- */
992
- const STOP_HOOK_SENTINEL = 'hooks claude-code stop';
993
-
994
- const FLUSH_MAX_WAIT_MS = 3000;
995
- const FLUSH_POLL_INTERVAL_MS = 50;
996
- const FLUSH_TAIL_BYTES = 4096;
997
- const FLUSH_MAX_SKEW_MS = 2000;
998
-
999
- /**
1000
- * Poll the transcript file for the stop hook sentinel.
1001
- * Falls back silently after a timeout.
1002
- */
1003
- async function waitForTranscriptFlush(transcriptPath: string): Promise<void> {
1004
- const hookStartTime = Date.now();
1005
- const deadline = hookStartTime + FLUSH_MAX_WAIT_MS;
1006
-
1007
- while (Date.now() < deadline) {
1008
- if (checkStopSentinel(transcriptPath, hookStartTime)) {
1009
- return;
1010
- }
1011
- await sleep(FLUSH_POLL_INTERVAL_MS);
1012
- }
1013
- // Timeout — proceed anyway
1014
- }
1015
-
1016
- /**
1017
- * Read the tail of the transcript file and look for the stop hook sentinel
1018
- * with a timestamp within the acceptable skew window.
1019
- */
1020
- function checkStopSentinel(filePath: string, hookStartTime: number): boolean {
1021
- let fd: number;
1022
- try {
1023
- fd = fs.openSync(filePath, 'r');
1024
- } catch {
1025
- return false;
1026
- }
1027
-
1028
- try {
1029
- const stat = fs.fstatSync(fd);
1030
- const offset = Math.max(0, stat.size - FLUSH_TAIL_BYTES);
1031
- const buf = Buffer.alloc(stat.size - offset);
1032
- fs.readSync(fd, buf, 0, buf.length, offset);
1033
-
1034
- const lines = buf.toString('utf-8').split('\n');
1035
- for (const line of lines) {
1036
- const trimmed = line.trim();
1037
- if (!trimmed || !trimmed.includes(STOP_HOOK_SENTINEL)) continue;
1038
-
1039
- try {
1040
- const entry = JSON.parse(trimmed) as { timestamp?: string };
1041
- if (!entry.timestamp) continue;
1042
-
1043
- const ts = new Date(entry.timestamp).getTime();
1044
- if (isNaN(ts)) continue;
1045
-
1046
- const lowerBound = hookStartTime - FLUSH_MAX_SKEW_MS;
1047
- const upperBound = hookStartTime + FLUSH_MAX_SKEW_MS;
1048
- if (ts > lowerBound && ts < upperBound) {
1049
- return true;
1050
- }
1051
- } catch {
1052
- continue;
1053
- }
1054
- }
1055
- return false;
1056
- } finally {
1057
- fs.closeSync(fd);
1058
- }
1059
- }
1060
-
1061
- function sleep(ms: number): Promise<void> {
1062
- return new Promise((resolve) => setTimeout(resolve, ms));
1063
- }
1064
-
1065
- // ============================================================================
1066
- // Path Helpers
1067
- // ============================================================================
1068
-
1069
- /**
1070
- * Sanitize a filesystem path for use as a Claude project directory name
1071
- */
1072
- function sanitizePathForClaude(repoPath: string): string {
1073
- // Claude uses a hash-based directory naming scheme
1074
- return crypto.createHash('sha256').update(repoPath).digest('hex').slice(0, 16);
1075
- }
1076
-
1077
- // ============================================================================
1078
- // Registration
1079
- // ============================================================================
1080
-
1081
- /**
1082
- * Create and return a new Claude Code agent instance
1083
- */
1084
- export function createClaudeCodeAgent(): ClaudeCodeAgent {
1085
- return new ClaudeCodeAgent();
1086
- }
1087
-
1088
- // Auto-register when imported
1089
- registerAgent(AGENT_NAMES.CLAUDE_CODE, () => new ClaudeCodeAgent());