@vinaes/succ 1.4.0 → 1.5.37

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 (683) hide show
  1. package/README.md +64 -10
  2. package/dist/cli.js +71 -1
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/agents-md.d.ts.map +1 -1
  5. package/dist/commands/agents-md.js +3 -2
  6. package/dist/commands/agents-md.js.map +1 -1
  7. package/dist/commands/analyze-profile.d.ts.map +1 -1
  8. package/dist/commands/analyze-profile.js +32 -8
  9. package/dist/commands/analyze-profile.js.map +1 -1
  10. package/dist/commands/analyze-recursive.d.ts.map +1 -1
  11. package/dist/commands/analyze-recursive.js +6 -2
  12. package/dist/commands/analyze-recursive.js.map +1 -1
  13. package/dist/commands/analyze-utils.d.ts.map +1 -1
  14. package/dist/commands/analyze-utils.js +17 -4
  15. package/dist/commands/analyze-utils.js.map +1 -1
  16. package/dist/commands/benchmark-quality.d.ts.map +1 -1
  17. package/dist/commands/benchmark-quality.js +11 -4
  18. package/dist/commands/benchmark-quality.js.map +1 -1
  19. package/dist/commands/benchmark-sqlite-vec.d.ts.map +1 -1
  20. package/dist/commands/benchmark-sqlite-vec.js +4 -0
  21. package/dist/commands/benchmark-sqlite-vec.js.map +1 -1
  22. package/dist/commands/benchmark.d.ts.map +1 -1
  23. package/dist/commands/benchmark.js +5 -1
  24. package/dist/commands/benchmark.js.map +1 -1
  25. package/dist/commands/codex-chat.d.ts +8 -0
  26. package/dist/commands/codex-chat.d.ts.map +1 -0
  27. package/dist/commands/codex-chat.js +161 -0
  28. package/dist/commands/codex-chat.js.map +1 -0
  29. package/dist/commands/config.d.ts.map +1 -1
  30. package/dist/commands/config.js +32 -4
  31. package/dist/commands/config.js.map +1 -1
  32. package/dist/commands/daemon.d.ts.map +1 -1
  33. package/dist/commands/daemon.js +13 -4
  34. package/dist/commands/daemon.js.map +1 -1
  35. package/dist/commands/index-code.d.ts +4 -0
  36. package/dist/commands/index-code.d.ts.map +1 -1
  37. package/dist/commands/index-code.js +1 -1
  38. package/dist/commands/index-code.js.map +1 -1
  39. package/dist/commands/init.d.ts.map +1 -1
  40. package/dist/commands/init.js +305 -203
  41. package/dist/commands/init.js.map +1 -1
  42. package/dist/commands/memories.d.ts.map +1 -1
  43. package/dist/commands/memories.js +25 -14
  44. package/dist/commands/memories.js.map +1 -1
  45. package/dist/commands/progress.d.ts.map +1 -1
  46. package/dist/commands/progress.js +3 -2
  47. package/dist/commands/progress.js.map +1 -1
  48. package/dist/commands/reindex.d.ts.map +1 -1
  49. package/dist/commands/reindex.js +54 -36
  50. package/dist/commands/reindex.js.map +1 -1
  51. package/dist/commands/retention.d.ts.map +1 -1
  52. package/dist/commands/retention.js +7 -5
  53. package/dist/commands/retention.js.map +1 -1
  54. package/dist/commands/scan-code.d.ts +76 -0
  55. package/dist/commands/scan-code.d.ts.map +1 -0
  56. package/dist/commands/scan-code.js +385 -0
  57. package/dist/commands/scan-code.js.map +1 -0
  58. package/dist/commands/score.d.ts.map +1 -1
  59. package/dist/commands/score.js +3 -2
  60. package/dist/commands/score.js.map +1 -1
  61. package/dist/commands/session.d.ts +33 -0
  62. package/dist/commands/session.d.ts.map +1 -0
  63. package/dist/commands/session.js +163 -0
  64. package/dist/commands/session.js.map +1 -0
  65. package/dist/commands/setup.d.ts.map +1 -1
  66. package/dist/commands/setup.js +254 -15
  67. package/dist/commands/setup.js.map +1 -1
  68. package/dist/commands/soul.js +3 -2
  69. package/dist/commands/soul.js.map +1 -1
  70. package/dist/commands/status.d.ts.map +1 -1
  71. package/dist/commands/status.js +14 -5
  72. package/dist/commands/status.js.map +1 -1
  73. package/dist/commands/watch.d.ts.map +1 -1
  74. package/dist/commands/watch.js +13 -4
  75. package/dist/commands/watch.js.map +1 -1
  76. package/dist/daemon/analyzer.d.ts.map +1 -1
  77. package/dist/daemon/analyzer.js +21 -5
  78. package/dist/daemon/analyzer.js.map +1 -1
  79. package/dist/daemon/client.d.ts.map +1 -1
  80. package/dist/daemon/client.js +32 -8
  81. package/dist/daemon/client.js.map +1 -1
  82. package/dist/daemon/routes/analyzer.d.ts +3 -0
  83. package/dist/daemon/routes/analyzer.d.ts.map +1 -0
  84. package/dist/daemon/routes/analyzer.js +27 -0
  85. package/dist/daemon/routes/analyzer.js.map +1 -0
  86. package/dist/daemon/routes/hooks.d.ts +14 -0
  87. package/dist/daemon/routes/hooks.d.ts.map +1 -0
  88. package/dist/daemon/routes/hooks.js +1212 -0
  89. package/dist/daemon/routes/hooks.js.map +1 -0
  90. package/dist/daemon/routes/memory.d.ts +4 -0
  91. package/dist/daemon/routes/memory.d.ts.map +1 -0
  92. package/dist/daemon/routes/memory.js +71 -0
  93. package/dist/daemon/routes/memory.js.map +1 -0
  94. package/dist/daemon/routes/reflection.d.ts +10 -0
  95. package/dist/daemon/routes/reflection.d.ts.map +1 -0
  96. package/dist/daemon/routes/reflection.js +397 -0
  97. package/dist/daemon/routes/reflection.js.map +1 -0
  98. package/dist/daemon/routes/search.d.ts +5 -0
  99. package/dist/daemon/routes/search.d.ts.map +1 -0
  100. package/dist/daemon/routes/search.js +93 -0
  101. package/dist/daemon/routes/search.js.map +1 -0
  102. package/dist/daemon/routes/sessions.d.ts +3 -0
  103. package/dist/daemon/routes/sessions.d.ts.map +1 -0
  104. package/dist/daemon/routes/sessions.js +160 -0
  105. package/dist/daemon/routes/sessions.js.map +1 -0
  106. package/dist/daemon/routes/skills.d.ts +3 -0
  107. package/dist/daemon/routes/skills.d.ts.map +1 -0
  108. package/dist/daemon/routes/skills.js +36 -0
  109. package/dist/daemon/routes/skills.js.map +1 -0
  110. package/dist/daemon/routes/status.d.ts +3 -0
  111. package/dist/daemon/routes/status.d.ts.map +1 -0
  112. package/dist/daemon/routes/status.js +47 -0
  113. package/dist/daemon/routes/status.js.map +1 -0
  114. package/dist/daemon/routes/types.d.ts +240 -0
  115. package/dist/daemon/routes/types.d.ts.map +1 -0
  116. package/dist/daemon/routes/types.js +97 -0
  117. package/dist/daemon/routes/types.js.map +1 -0
  118. package/dist/daemon/routes/versioning.d.ts +27 -0
  119. package/dist/daemon/routes/versioning.d.ts.map +1 -0
  120. package/dist/daemon/routes/versioning.js +44 -0
  121. package/dist/daemon/routes/versioning.js.map +1 -0
  122. package/dist/daemon/routes/watcher.d.ts +3 -0
  123. package/dist/daemon/routes/watcher.d.ts.map +1 -0
  124. package/dist/daemon/routes/watcher.js +28 -0
  125. package/dist/daemon/routes/watcher.js.map +1 -0
  126. package/dist/daemon/service.d.ts +5 -23
  127. package/dist/daemon/service.d.ts.map +1 -1
  128. package/dist/daemon/service.js +177 -935
  129. package/dist/daemon/service.js.map +1 -1
  130. package/dist/daemon/session-processor.d.ts +4 -8
  131. package/dist/daemon/session-processor.d.ts.map +1 -1
  132. package/dist/daemon/session-processor.js +39 -38
  133. package/dist/daemon/session-processor.js.map +1 -1
  134. package/dist/lib/ai-readiness.d.ts.map +1 -1
  135. package/dist/lib/ai-readiness.js +33 -8
  136. package/dist/lib/ai-readiness.js.map +1 -1
  137. package/dist/lib/analyze-state.d.ts.map +1 -1
  138. package/dist/lib/analyze-state.js +25 -3
  139. package/dist/lib/analyze-state.js.map +1 -1
  140. package/dist/lib/auto-memory/consolidation.d.ts +41 -0
  141. package/dist/lib/auto-memory/consolidation.d.ts.map +1 -0
  142. package/dist/lib/auto-memory/consolidation.js +151 -0
  143. package/dist/lib/auto-memory/consolidation.js.map +1 -0
  144. package/dist/lib/bpe.d.ts.map +1 -1
  145. package/dist/lib/bpe.js +9 -10
  146. package/dist/lib/bpe.js.map +1 -1
  147. package/dist/lib/brain-export.d.ts +65 -0
  148. package/dist/lib/brain-export.d.ts.map +1 -0
  149. package/dist/lib/brain-export.js +413 -0
  150. package/dist/lib/brain-export.js.map +1 -0
  151. package/dist/lib/checkpoint.d.ts.map +1 -1
  152. package/dist/lib/checkpoint.js +22 -6
  153. package/dist/lib/checkpoint.js.map +1 -1
  154. package/dist/lib/chunker.d.ts.map +1 -1
  155. package/dist/lib/chunker.js +6 -1
  156. package/dist/lib/chunker.js.map +1 -1
  157. package/dist/lib/claude-ws-transport.d.ts.map +1 -1
  158. package/dist/lib/claude-ws-transport.js +12 -4
  159. package/dist/lib/claude-ws-transport.js.map +1 -1
  160. package/dist/lib/command-safety.d.ts +64 -0
  161. package/dist/lib/command-safety.d.ts.map +1 -0
  162. package/dist/lib/command-safety.js +625 -0
  163. package/dist/lib/command-safety.js.map +1 -0
  164. package/dist/lib/compact-briefing.d.ts.map +1 -1
  165. package/dist/lib/compact-briefing.js +10 -13
  166. package/dist/lib/compact-briefing.js.map +1 -1
  167. package/dist/lib/config-defaults.d.ts.map +1 -1
  168. package/dist/lib/config-defaults.js +3 -0
  169. package/dist/lib/config-defaults.js.map +1 -1
  170. package/dist/lib/config-display.d.ts +4 -0
  171. package/dist/lib/config-display.d.ts.map +1 -1
  172. package/dist/lib/config-display.js +6 -1
  173. package/dist/lib/config-display.js.map +1 -1
  174. package/dist/lib/config-types.d.ts +149 -0
  175. package/dist/lib/config-types.d.ts.map +1 -1
  176. package/dist/lib/config-validation.d.ts.map +1 -1
  177. package/dist/lib/config-validation.js +5 -0
  178. package/dist/lib/config-validation.js.map +1 -1
  179. package/dist/lib/config.d.ts.map +1 -1
  180. package/dist/lib/config.js +92 -9
  181. package/dist/lib/config.js.map +1 -1
  182. package/dist/lib/consolidate.d.ts.map +1 -1
  183. package/dist/lib/consolidate.js +66 -47
  184. package/dist/lib/consolidate.js.map +1 -1
  185. package/dist/lib/content-sanitizer.d.ts +29 -0
  186. package/dist/lib/content-sanitizer.d.ts.map +1 -0
  187. package/dist/lib/content-sanitizer.js +72 -0
  188. package/dist/lib/content-sanitizer.js.map +1 -0
  189. package/dist/lib/cross-repo.d.ts +44 -0
  190. package/dist/lib/cross-repo.d.ts.map +1 -0
  191. package/dist/lib/cross-repo.js +108 -0
  192. package/dist/lib/cross-repo.js.map +1 -0
  193. package/dist/lib/daemon-port.d.ts +12 -0
  194. package/dist/lib/daemon-port.d.ts.map +1 -0
  195. package/dist/lib/daemon-port.js +20 -0
  196. package/dist/lib/daemon-port.js.map +1 -0
  197. package/dist/lib/db/auto-memory.d.ts +40 -0
  198. package/dist/lib/db/auto-memory.d.ts.map +1 -0
  199. package/dist/lib/db/auto-memory.js +74 -0
  200. package/dist/lib/db/auto-memory.js.map +1 -0
  201. package/dist/lib/db/bm25-indexes.d.ts.map +1 -1
  202. package/dist/lib/db/bm25-indexes.js +16 -4
  203. package/dist/lib/db/bm25-indexes.js.map +1 -1
  204. package/dist/lib/db/documents.d.ts.map +1 -1
  205. package/dist/lib/db/documents.js +4 -1
  206. package/dist/lib/db/documents.js.map +1 -1
  207. package/dist/lib/db/global-memories.d.ts +2 -10
  208. package/dist/lib/db/global-memories.d.ts.map +1 -1
  209. package/dist/lib/db/global-memories.js +13 -6
  210. package/dist/lib/db/global-memories.js.map +1 -1
  211. package/dist/lib/db/graph.d.ts +5 -1
  212. package/dist/lib/db/graph.d.ts.map +1 -1
  213. package/dist/lib/db/graph.js +38 -8
  214. package/dist/lib/db/graph.js.map +1 -1
  215. package/dist/lib/db/hybrid-search.d.ts +4 -2
  216. package/dist/lib/db/hybrid-search.d.ts.map +1 -1
  217. package/dist/lib/db/hybrid-search.js +29 -11
  218. package/dist/lib/db/hybrid-search.js.map +1 -1
  219. package/dist/lib/db/index.d.ts +6 -1
  220. package/dist/lib/db/index.d.ts.map +1 -1
  221. package/dist/lib/db/index.js +5 -1
  222. package/dist/lib/db/index.js.map +1 -1
  223. package/dist/lib/db/memories.d.ts +19 -14
  224. package/dist/lib/db/memories.d.ts.map +1 -1
  225. package/dist/lib/db/memories.js +100 -37
  226. package/dist/lib/db/memories.js.map +1 -1
  227. package/dist/lib/db/parse-helpers.d.ts +14 -0
  228. package/dist/lib/db/parse-helpers.d.ts.map +1 -0
  229. package/dist/lib/db/parse-helpers.js +59 -0
  230. package/dist/lib/db/parse-helpers.js.map +1 -0
  231. package/dist/lib/db/recall-events.d.ts +49 -0
  232. package/dist/lib/db/recall-events.d.ts.map +1 -0
  233. package/dist/lib/db/recall-events.js +196 -0
  234. package/dist/lib/db/recall-events.js.map +1 -0
  235. package/dist/lib/db/retention.d.ts +4 -3
  236. package/dist/lib/db/retention.d.ts.map +1 -1
  237. package/dist/lib/db/retention.js +12 -1
  238. package/dist/lib/db/retention.js.map +1 -1
  239. package/dist/lib/db/schema.d.ts +2 -0
  240. package/dist/lib/db/schema.d.ts.map +1 -1
  241. package/dist/lib/db/schema.js +140 -80
  242. package/dist/lib/db/schema.js.map +1 -1
  243. package/dist/lib/db/skills.d.ts.map +1 -1
  244. package/dist/lib/db/skills.js +10 -6
  245. package/dist/lib/db/skills.js.map +1 -1
  246. package/dist/lib/diff-brain.d.ts +24 -0
  247. package/dist/lib/diff-brain.d.ts.map +1 -0
  248. package/dist/lib/diff-brain.js +114 -0
  249. package/dist/lib/diff-brain.js.map +1 -0
  250. package/dist/lib/diff-parser.d.ts +74 -0
  251. package/dist/lib/diff-parser.d.ts.map +1 -0
  252. package/dist/lib/diff-parser.js +200 -0
  253. package/dist/lib/diff-parser.js.map +1 -0
  254. package/dist/lib/embedding-pool.d.ts.map +1 -1
  255. package/dist/lib/embedding-pool.js +5 -1
  256. package/dist/lib/embedding-pool.js.map +1 -1
  257. package/dist/lib/embeddings.d.ts +12 -0
  258. package/dist/lib/embeddings.d.ts.map +1 -1
  259. package/dist/lib/embeddings.js +77 -19
  260. package/dist/lib/embeddings.js.map +1 -1
  261. package/dist/lib/errors.d.ts +2 -0
  262. package/dist/lib/errors.d.ts.map +1 -1
  263. package/dist/lib/errors.js +4 -0
  264. package/dist/lib/errors.js.map +1 -1
  265. package/dist/lib/fault-logger.d.ts.map +1 -1
  266. package/dist/lib/fault-logger.js +22 -7
  267. package/dist/lib/fault-logger.js.map +1 -1
  268. package/dist/lib/git/co-change.d.ts +39 -0
  269. package/dist/lib/git/co-change.d.ts.map +1 -0
  270. package/dist/lib/git/co-change.js +139 -0
  271. package/dist/lib/git/co-change.js.map +1 -0
  272. package/dist/lib/graph/bridge-edges.d.ts +93 -0
  273. package/dist/lib/graph/bridge-edges.d.ts.map +1 -0
  274. package/dist/lib/graph/bridge-edges.js +276 -0
  275. package/dist/lib/graph/bridge-edges.js.map +1 -0
  276. package/dist/lib/graph/centrality.d.ts +11 -0
  277. package/dist/lib/graph/centrality.d.ts.map +1 -1
  278. package/dist/lib/graph/centrality.js +51 -3
  279. package/dist/lib/graph/centrality.js.map +1 -1
  280. package/dist/lib/graph/cleanup.d.ts.map +1 -1
  281. package/dist/lib/graph/cleanup.js +2 -1
  282. package/dist/lib/graph/cleanup.js.map +1 -1
  283. package/dist/lib/graph/community-detection.d.ts +17 -2
  284. package/dist/lib/graph/community-detection.d.ts.map +1 -1
  285. package/dist/lib/graph/community-detection.js +147 -48
  286. package/dist/lib/graph/community-detection.js.map +1 -1
  287. package/dist/lib/graph/community-summaries.d.ts +26 -0
  288. package/dist/lib/graph/community-summaries.d.ts.map +1 -0
  289. package/dist/lib/graph/community-summaries.js +130 -0
  290. package/dist/lib/graph/community-summaries.js.map +1 -0
  291. package/dist/lib/graph/contextual-proximity.d.ts.map +1 -1
  292. package/dist/lib/graph/contextual-proximity.js +11 -4
  293. package/dist/lib/graph/contextual-proximity.js.map +1 -1
  294. package/dist/lib/graph/graphology-bridge.d.ts +101 -0
  295. package/dist/lib/graph/graphology-bridge.d.ts.map +1 -0
  296. package/dist/lib/graph/graphology-bridge.js +488 -0
  297. package/dist/lib/graph/graphology-bridge.js.map +1 -0
  298. package/dist/lib/graph/llm-relations.d.ts.map +1 -1
  299. package/dist/lib/graph/llm-relations.js +27 -5
  300. package/dist/lib/graph/llm-relations.js.map +1 -1
  301. package/dist/lib/graph-export.d.ts.map +1 -1
  302. package/dist/lib/graph-export.js +2 -2
  303. package/dist/lib/graph-export.js.map +1 -1
  304. package/dist/lib/graph-scheduler.d.ts +0 -5
  305. package/dist/lib/graph-scheduler.d.ts.map +1 -1
  306. package/dist/lib/graph-scheduler.js +5 -1
  307. package/dist/lib/graph-scheduler.js.map +1 -1
  308. package/dist/lib/guardrails.d.ts +50 -0
  309. package/dist/lib/guardrails.d.ts.map +1 -0
  310. package/dist/lib/guardrails.js +502 -0
  311. package/dist/lib/guardrails.js.map +1 -0
  312. package/dist/lib/hook-rules.d.ts +1 -1
  313. package/dist/lib/hook-rules.d.ts.map +1 -1
  314. package/dist/lib/hook-rules.js +8 -2
  315. package/dist/lib/hook-rules.js.map +1 -1
  316. package/dist/lib/ifc/file-labels.d.ts +35 -0
  317. package/dist/lib/ifc/file-labels.d.ts.map +1 -0
  318. package/dist/lib/ifc/file-labels.js +208 -0
  319. package/dist/lib/ifc/file-labels.js.map +1 -0
  320. package/dist/lib/ifc/label.d.ts +38 -0
  321. package/dist/lib/ifc/label.d.ts.map +1 -0
  322. package/dist/lib/ifc/label.js +80 -0
  323. package/dist/lib/ifc/label.js.map +1 -0
  324. package/dist/lib/ifc/session-ifc.d.ts +92 -0
  325. package/dist/lib/ifc/session-ifc.d.ts.map +1 -0
  326. package/dist/lib/ifc/session-ifc.js +222 -0
  327. package/dist/lib/ifc/session-ifc.js.map +1 -0
  328. package/dist/lib/indexer.js +2 -2
  329. package/dist/lib/indexer.js.map +1 -1
  330. package/dist/lib/injection-detector.d.ts +83 -0
  331. package/dist/lib/injection-detector.d.ts.map +1 -0
  332. package/dist/lib/injection-detector.js +586 -0
  333. package/dist/lib/injection-detector.js.map +1 -0
  334. package/dist/lib/injection-semantic.d.ts +31 -0
  335. package/dist/lib/injection-semantic.d.ts.map +1 -0
  336. package/dist/lib/injection-semantic.js +230 -0
  337. package/dist/lib/injection-semantic.js.map +1 -0
  338. package/dist/lib/llm.d.ts.map +1 -1
  339. package/dist/lib/llm.js +19 -4
  340. package/dist/lib/llm.js.map +1 -1
  341. package/dist/lib/lock.d.ts.map +1 -1
  342. package/dist/lib/lock.js +24 -3
  343. package/dist/lib/lock.js.map +1 -1
  344. package/dist/lib/md-fetch.d.ts.map +1 -1
  345. package/dist/lib/md-fetch.js +9 -2
  346. package/dist/lib/md-fetch.js.map +1 -1
  347. package/dist/lib/observability.d.ts +75 -0
  348. package/dist/lib/observability.d.ts.map +1 -0
  349. package/dist/lib/observability.js +201 -0
  350. package/dist/lib/observability.js.map +1 -0
  351. package/dist/lib/ort-session.d.ts +26 -0
  352. package/dist/lib/ort-session.d.ts.map +1 -1
  353. package/dist/lib/ort-session.js +107 -3
  354. package/dist/lib/ort-session.js.map +1 -1
  355. package/dist/lib/prd/codebase-context.d.ts.map +1 -1
  356. package/dist/lib/prd/codebase-context.js +9 -2
  357. package/dist/lib/prd/codebase-context.js.map +1 -1
  358. package/dist/lib/prd/context.d.ts.map +1 -1
  359. package/dist/lib/prd/context.js +11 -3
  360. package/dist/lib/prd/context.js.map +1 -1
  361. package/dist/lib/prd/export.js +1 -1
  362. package/dist/lib/prd/export.js.map +1 -1
  363. package/dist/lib/prd/generate.d.ts.map +1 -1
  364. package/dist/lib/prd/generate.js +17 -4
  365. package/dist/lib/prd/generate.js.map +1 -1
  366. package/dist/lib/prd/parse.d.ts.map +1 -1
  367. package/dist/lib/prd/parse.js +6 -1
  368. package/dist/lib/prd/parse.js.map +1 -1
  369. package/dist/lib/prd/runner.d.ts +1 -2
  370. package/dist/lib/prd/runner.d.ts.map +1 -1
  371. package/dist/lib/prd/runner.js +43 -32
  372. package/dist/lib/prd/runner.js.map +1 -1
  373. package/dist/lib/prd/worktree.d.ts +1 -2
  374. package/dist/lib/prd/worktree.d.ts.map +1 -1
  375. package/dist/lib/prd/worktree.js +62 -70
  376. package/dist/lib/prd/worktree.js.map +1 -1
  377. package/dist/lib/precompute-context.d.ts.map +1 -1
  378. package/dist/lib/precompute-context.js +15 -34
  379. package/dist/lib/precompute-context.js.map +1 -1
  380. package/dist/lib/pricing.d.ts.map +1 -1
  381. package/dist/lib/pricing.js +5 -1
  382. package/dist/lib/pricing.js.map +1 -1
  383. package/dist/lib/process-registry.js +3 -3
  384. package/dist/lib/process-registry.js.map +1 -1
  385. package/dist/lib/public-api.d.ts +41 -1
  386. package/dist/lib/public-api.d.ts.map +1 -1
  387. package/dist/lib/public-api.js +38 -0
  388. package/dist/lib/public-api.js.map +1 -1
  389. package/dist/lib/quality.d.ts.map +1 -1
  390. package/dist/lib/quality.js +15 -6
  391. package/dist/lib/quality.js.map +1 -1
  392. package/dist/lib/query-expansion.d.ts +32 -0
  393. package/dist/lib/query-expansion.d.ts.map +1 -1
  394. package/dist/lib/query-expansion.js +62 -1
  395. package/dist/lib/query-expansion.js.map +1 -1
  396. package/dist/lib/reference-embeddings.d.ts.map +1 -1
  397. package/dist/lib/reference-embeddings.js +17 -4
  398. package/dist/lib/reference-embeddings.js.map +1 -1
  399. package/dist/lib/reflection-synthesizer.d.ts.map +1 -1
  400. package/dist/lib/reflection-synthesizer.js +7 -1
  401. package/dist/lib/reflection-synthesizer.js.map +1 -1
  402. package/dist/lib/reranker.d.ts +41 -0
  403. package/dist/lib/reranker.d.ts.map +1 -0
  404. package/dist/lib/reranker.js +294 -0
  405. package/dist/lib/reranker.js.map +1 -0
  406. package/dist/lib/retrieval-feedback.d.ts +100 -0
  407. package/dist/lib/retrieval-feedback.d.ts.map +1 -0
  408. package/dist/lib/retrieval-feedback.js +174 -0
  409. package/dist/lib/retrieval-feedback.js.map +1 -0
  410. package/dist/lib/review/context-pack.d.ts +58 -0
  411. package/dist/lib/review/context-pack.d.ts.map +1 -0
  412. package/dist/lib/review/context-pack.js +300 -0
  413. package/dist/lib/review/context-pack.js.map +1 -0
  414. package/dist/lib/search/hierarchical-summaries.d.ts +65 -0
  415. package/dist/lib/search/hierarchical-summaries.d.ts.map +1 -0
  416. package/dist/lib/search/hierarchical-summaries.js +423 -0
  417. package/dist/lib/search/hierarchical-summaries.js.map +1 -0
  418. package/dist/lib/search/hyde.d.ts +27 -0
  419. package/dist/lib/search/hyde.d.ts.map +1 -0
  420. package/dist/lib/search/hyde.js +141 -0
  421. package/dist/lib/search/hyde.js.map +1 -0
  422. package/dist/lib/search/late-chunking.d.ts +53 -0
  423. package/dist/lib/search/late-chunking.d.ts.map +1 -0
  424. package/dist/lib/search/late-chunking.js +230 -0
  425. package/dist/lib/search/late-chunking.js.map +1 -0
  426. package/dist/lib/search/ppr-retrieval.d.ts +49 -0
  427. package/dist/lib/search/ppr-retrieval.d.ts.map +1 -0
  428. package/dist/lib/search/ppr-retrieval.js +135 -0
  429. package/dist/lib/search/ppr-retrieval.js.map +1 -0
  430. package/dist/lib/search/repo-map.d.ts +43 -0
  431. package/dist/lib/search/repo-map.d.ts.map +1 -0
  432. package/dist/lib/search/repo-map.js +165 -0
  433. package/dist/lib/search/repo-map.js.map +1 -0
  434. package/dist/lib/session-analyzer.d.ts +90 -0
  435. package/dist/lib/session-analyzer.d.ts.map +1 -0
  436. package/dist/lib/session-analyzer.js +467 -0
  437. package/dist/lib/session-analyzer.js.map +1 -0
  438. package/dist/lib/session-observations.d.ts.map +1 -1
  439. package/dist/lib/session-observations.js +13 -3
  440. package/dist/lib/session-observations.js.map +1 -1
  441. package/dist/lib/session-summary.d.ts.map +1 -1
  442. package/dist/lib/session-summary.js +57 -50
  443. package/dist/lib/session-summary.js.map +1 -1
  444. package/dist/lib/session-surgeon.d.ts +53 -0
  445. package/dist/lib/session-surgeon.d.ts.map +1 -0
  446. package/dist/lib/session-surgeon.js +501 -0
  447. package/dist/lib/session-surgeon.js.map +1 -0
  448. package/dist/lib/similarity-utils.d.ts +26 -0
  449. package/dist/lib/similarity-utils.d.ts.map +1 -0
  450. package/dist/lib/similarity-utils.js +66 -0
  451. package/dist/lib/similarity-utils.js.map +1 -0
  452. package/dist/lib/skills.d.ts.map +1 -1
  453. package/dist/lib/skills.js +11 -11
  454. package/dist/lib/skills.js.map +1 -1
  455. package/dist/lib/storage/backends/interface.d.ts +13 -3
  456. package/dist/lib/storage/backends/interface.d.ts.map +1 -1
  457. package/dist/lib/storage/backends/postgresql.d.ts +52 -3
  458. package/dist/lib/storage/backends/postgresql.d.ts.map +1 -1
  459. package/dist/lib/storage/backends/postgresql.js +694 -49
  460. package/dist/lib/storage/backends/postgresql.js.map +1 -1
  461. package/dist/lib/storage/benchmark.js +2 -2
  462. package/dist/lib/storage/benchmark.js.map +1 -1
  463. package/dist/lib/storage/dispatcher/base.d.ts +114 -0
  464. package/dist/lib/storage/dispatcher/base.d.ts.map +1 -0
  465. package/dist/lib/storage/dispatcher/base.js +160 -0
  466. package/dist/lib/storage/dispatcher/base.js.map +1 -0
  467. package/dist/lib/storage/dispatcher/documents.d.ts +25 -0
  468. package/dist/lib/storage/dispatcher/documents.d.ts.map +1 -0
  469. package/dist/lib/storage/dispatcher/documents.js +194 -0
  470. package/dist/lib/storage/dispatcher/documents.js.map +1 -0
  471. package/dist/lib/storage/dispatcher/embeddings.d.ts +34 -0
  472. package/dist/lib/storage/dispatcher/embeddings.d.ts.map +1 -0
  473. package/dist/lib/storage/dispatcher/embeddings.js +144 -0
  474. package/dist/lib/storage/dispatcher/embeddings.js.map +1 -0
  475. package/dist/lib/storage/dispatcher/export-import.d.ts +139 -0
  476. package/dist/lib/storage/dispatcher/export-import.d.ts.map +1 -0
  477. package/dist/lib/storage/dispatcher/export-import.js +191 -0
  478. package/dist/lib/storage/dispatcher/export-import.js.map +1 -0
  479. package/dist/lib/storage/dispatcher/file-hashes.d.ts +13 -0
  480. package/dist/lib/storage/dispatcher/file-hashes.d.ts.map +1 -0
  481. package/dist/lib/storage/dispatcher/file-hashes.js +36 -0
  482. package/dist/lib/storage/dispatcher/file-hashes.js.map +1 -0
  483. package/dist/lib/storage/dispatcher/global-memories.d.ts +28 -0
  484. package/dist/lib/storage/dispatcher/global-memories.d.ts.map +1 -0
  485. package/dist/lib/storage/dispatcher/global-memories.js +151 -0
  486. package/dist/lib/storage/dispatcher/global-memories.js.map +1 -0
  487. package/dist/lib/storage/dispatcher/graph.d.ts +32 -0
  488. package/dist/lib/storage/dispatcher/graph.d.ts.map +1 -0
  489. package/dist/lib/storage/dispatcher/graph.js +146 -0
  490. package/dist/lib/storage/dispatcher/graph.js.map +1 -0
  491. package/dist/lib/storage/dispatcher/index.d.ts +34 -0
  492. package/dist/lib/storage/dispatcher/index.d.ts.map +1 -0
  493. package/dist/lib/storage/dispatcher/index.js +139 -0
  494. package/dist/lib/storage/dispatcher/index.js.map +1 -0
  495. package/dist/lib/storage/dispatcher/memories.d.ts +65 -0
  496. package/dist/lib/storage/dispatcher/memories.d.ts.map +1 -0
  497. package/dist/lib/storage/dispatcher/memories.js +466 -0
  498. package/dist/lib/storage/dispatcher/memories.js.map +1 -0
  499. package/dist/lib/storage/dispatcher/mixin-helper.d.ts +6 -0
  500. package/dist/lib/storage/dispatcher/mixin-helper.d.ts.map +1 -0
  501. package/dist/lib/storage/dispatcher/mixin-helper.js +10 -0
  502. package/dist/lib/storage/dispatcher/mixin-helper.js.map +1 -0
  503. package/dist/lib/storage/dispatcher/retention.d.ts +20 -0
  504. package/dist/lib/storage/dispatcher/retention.d.ts.map +1 -0
  505. package/dist/lib/storage/dispatcher/retention.js +123 -0
  506. package/dist/lib/storage/dispatcher/retention.js.map +1 -0
  507. package/dist/lib/storage/dispatcher/search.d.ts +34 -0
  508. package/dist/lib/storage/dispatcher/search.d.ts.map +1 -0
  509. package/dist/lib/storage/dispatcher/search.js +222 -0
  510. package/dist/lib/storage/dispatcher/search.js.map +1 -0
  511. package/dist/lib/storage/dispatcher/skills.d.ts +53 -0
  512. package/dist/lib/storage/dispatcher/skills.d.ts.map +1 -0
  513. package/dist/lib/storage/dispatcher/skills.js +98 -0
  514. package/dist/lib/storage/dispatcher/skills.js.map +1 -0
  515. package/dist/lib/storage/dispatcher/token-stats.d.ts +23 -0
  516. package/dist/lib/storage/dispatcher/token-stats.d.ts.map +1 -0
  517. package/dist/lib/storage/dispatcher/token-stats.js +92 -0
  518. package/dist/lib/storage/dispatcher/token-stats.js.map +1 -0
  519. package/dist/lib/storage/dispatcher/web-search.d.ts +10 -0
  520. package/dist/lib/storage/dispatcher/web-search.d.ts.map +1 -0
  521. package/dist/lib/storage/dispatcher/web-search.js +39 -0
  522. package/dist/lib/storage/dispatcher/web-search.js.map +1 -0
  523. package/dist/lib/storage/dispatcher-export.d.ts.map +1 -1
  524. package/dist/lib/storage/dispatcher-export.js +48 -39
  525. package/dist/lib/storage/dispatcher-export.js.map +1 -1
  526. package/dist/lib/storage/dispatcher.d.ts +1 -468
  527. package/dist/lib/storage/dispatcher.d.ts.map +1 -1
  528. package/dist/lib/storage/dispatcher.js +1 -1931
  529. package/dist/lib/storage/dispatcher.js.map +1 -1
  530. package/dist/lib/storage/index.d.ts +20 -5
  531. package/dist/lib/storage/index.d.ts.map +1 -1
  532. package/dist/lib/storage/index.js +36 -7
  533. package/dist/lib/storage/index.js.map +1 -1
  534. package/dist/lib/storage/migration/export-import.d.ts.map +1 -1
  535. package/dist/lib/storage/migration/export-import.js +9 -2
  536. package/dist/lib/storage/migration/export-import.js.map +1 -1
  537. package/dist/lib/storage/types.d.ts +152 -10
  538. package/dist/lib/storage/types.d.ts.map +1 -1
  539. package/dist/lib/storage/types.js +13 -0
  540. package/dist/lib/storage/types.js.map +1 -1
  541. package/dist/lib/storage/vector/interface.d.ts +4 -0
  542. package/dist/lib/storage/vector/interface.d.ts.map +1 -1
  543. package/dist/lib/storage/vector/qdrant.d.ts +13 -2
  544. package/dist/lib/storage/vector/qdrant.d.ts.map +1 -1
  545. package/dist/lib/storage/vector/qdrant.js +147 -61
  546. package/dist/lib/storage/vector/qdrant.js.map +1 -1
  547. package/dist/lib/token-budget.d.ts.map +1 -1
  548. package/dist/lib/token-budget.js +9 -2
  549. package/dist/lib/token-budget.js.map +1 -1
  550. package/dist/lib/transcript-utils.d.ts +60 -0
  551. package/dist/lib/transcript-utils.d.ts.map +1 -0
  552. package/dist/lib/transcript-utils.js +69 -0
  553. package/dist/lib/transcript-utils.js.map +1 -0
  554. package/dist/lib/tree-sitter/extractor.d.ts +1 -1
  555. package/dist/lib/tree-sitter/extractor.d.ts.map +1 -1
  556. package/dist/lib/tree-sitter/extractor.js +34 -9
  557. package/dist/lib/tree-sitter/extractor.js.map +1 -1
  558. package/dist/lib/tree-sitter/parser.d.ts.map +1 -1
  559. package/dist/lib/tree-sitter/parser.js +45 -11
  560. package/dist/lib/tree-sitter/parser.js.map +1 -1
  561. package/dist/lib/tree-sitter/public.d.ts +12 -0
  562. package/dist/lib/tree-sitter/public.d.ts.map +1 -1
  563. package/dist/lib/tree-sitter/public.js +33 -1
  564. package/dist/lib/tree-sitter/public.js.map +1 -1
  565. package/dist/lib/tree-sitter/queries.d.ts.map +1 -1
  566. package/dist/lib/tree-sitter/queries.js +8 -0
  567. package/dist/lib/tree-sitter/queries.js.map +1 -1
  568. package/dist/lib/working-memory-pipeline.d.ts.map +1 -1
  569. package/dist/lib/working-memory-pipeline.js +12 -3
  570. package/dist/lib/working-memory-pipeline.js.map +1 -1
  571. package/dist/lib/worktree-detect.d.ts +43 -0
  572. package/dist/lib/worktree-detect.d.ts.map +1 -0
  573. package/dist/lib/worktree-detect.js +154 -0
  574. package/dist/lib/worktree-detect.js.map +1 -0
  575. package/dist/lsp/client.d.ts +96 -0
  576. package/dist/lsp/client.d.ts.map +1 -0
  577. package/dist/lsp/client.js +435 -0
  578. package/dist/lsp/client.js.map +1 -0
  579. package/dist/lsp/installer.d.ts +39 -0
  580. package/dist/lsp/installer.d.ts.map +1 -0
  581. package/dist/lsp/installer.js +275 -0
  582. package/dist/lsp/installer.js.map +1 -0
  583. package/dist/lsp/manager.d.ts +62 -0
  584. package/dist/lsp/manager.d.ts.map +1 -0
  585. package/dist/lsp/manager.js +234 -0
  586. package/dist/lsp/manager.js.map +1 -0
  587. package/dist/lsp/servers.d.ts +52 -0
  588. package/dist/lsp/servers.d.ts.map +1 -0
  589. package/dist/lsp/servers.js +162 -0
  590. package/dist/lsp/servers.js.map +1 -0
  591. package/dist/mcp/helpers.d.ts.map +1 -1
  592. package/dist/mcp/helpers.js +8 -2
  593. package/dist/mcp/helpers.js.map +1 -1
  594. package/dist/mcp/profile.js +1 -1
  595. package/dist/mcp/server.d.ts +3 -2
  596. package/dist/mcp/server.d.ts.map +1 -1
  597. package/dist/mcp/server.js +19 -7
  598. package/dist/mcp/server.js.map +1 -1
  599. package/dist/mcp/tools/config.d.ts.map +1 -1
  600. package/dist/mcp/tools/config.js +28 -118
  601. package/dist/mcp/tools/config.js.map +1 -1
  602. package/dist/mcp/tools/dead-end.d.ts.map +1 -1
  603. package/dist/mcp/tools/dead-end.js +4 -3
  604. package/dist/mcp/tools/dead-end.js.map +1 -1
  605. package/dist/mcp/tools/debug.d.ts.map +1 -1
  606. package/dist/mcp/tools/debug.js +27 -112
  607. package/dist/mcp/tools/debug.js.map +1 -1
  608. package/dist/mcp/tools/graph.d.ts.map +1 -1
  609. package/dist/mcp/tools/graph.js +164 -176
  610. package/dist/mcp/tools/graph.js.map +1 -1
  611. package/dist/mcp/tools/indexing.d.ts +1 -1
  612. package/dist/mcp/tools/indexing.d.ts.map +1 -1
  613. package/dist/mcp/tools/indexing.js +63 -164
  614. package/dist/mcp/tools/indexing.js.map +1 -1
  615. package/dist/mcp/tools/memory/forget.d.ts +3 -0
  616. package/dist/mcp/tools/memory/forget.d.ts.map +1 -0
  617. package/dist/mcp/tools/memory/forget.js +175 -0
  618. package/dist/mcp/tools/memory/forget.js.map +1 -0
  619. package/dist/mcp/tools/memory/memory-helpers.d.ts +45 -0
  620. package/dist/mcp/tools/memory/memory-helpers.d.ts.map +1 -0
  621. package/dist/mcp/tools/memory/memory-helpers.js +291 -0
  622. package/dist/mcp/tools/memory/memory-helpers.js.map +1 -0
  623. package/dist/mcp/tools/memory/recall.d.ts +3 -0
  624. package/dist/mcp/tools/memory/recall.d.ts.map +1 -0
  625. package/dist/mcp/tools/memory/recall.js +495 -0
  626. package/dist/mcp/tools/memory/recall.js.map +1 -0
  627. package/dist/mcp/tools/memory/remember.d.ts +3 -0
  628. package/dist/mcp/tools/memory/remember.d.ts.map +1 -0
  629. package/dist/mcp/tools/memory/remember.js +256 -0
  630. package/dist/mcp/tools/memory/remember.js.map +1 -0
  631. package/dist/mcp/tools/memory/temporal-query.d.ts +8 -0
  632. package/dist/mcp/tools/memory/temporal-query.d.ts.map +1 -0
  633. package/dist/mcp/tools/memory/temporal-query.js +68 -0
  634. package/dist/mcp/tools/memory/temporal-query.js.map +1 -0
  635. package/dist/mcp/tools/memory.d.ts +0 -11
  636. package/dist/mcp/tools/memory.d.ts.map +1 -1
  637. package/dist/mcp/tools/memory.js +6 -1228
  638. package/dist/mcp/tools/memory.js.map +1 -1
  639. package/dist/mcp/tools/prd.d.ts.map +1 -1
  640. package/dist/mcp/tools/prd.js +19 -70
  641. package/dist/mcp/tools/prd.js.map +1 -1
  642. package/dist/mcp/tools/review.d.ts +8 -0
  643. package/dist/mcp/tools/review.d.ts.map +1 -0
  644. package/dist/mcp/tools/review.js +133 -0
  645. package/dist/mcp/tools/review.js.map +1 -0
  646. package/dist/mcp/tools/search.d.ts.map +1 -1
  647. package/dist/mcp/tools/search.js +79 -8
  648. package/dist/mcp/tools/search.js.map +1 -1
  649. package/dist/mcp/tools/status.d.ts.map +1 -1
  650. package/dist/mcp/tools/status.js +34 -75
  651. package/dist/mcp/tools/status.js.map +1 -1
  652. package/dist/mcp/tools/web-fetch.d.ts.map +1 -1
  653. package/dist/mcp/tools/web-fetch.js +5 -1
  654. package/dist/mcp/tools/web-fetch.js.map +1 -1
  655. package/dist/mcp/tools/web-search.d.ts.map +1 -1
  656. package/dist/mcp/tools/web-search.js +25 -103
  657. package/dist/mcp/tools/web-search.js.map +1 -1
  658. package/dist/prompts/guardrails.d.ts +14 -0
  659. package/dist/prompts/guardrails.d.ts.map +1 -0
  660. package/dist/prompts/guardrails.js +115 -0
  661. package/dist/prompts/guardrails.js.map +1 -0
  662. package/dist/prompts/index.d.ts +2 -1
  663. package/dist/prompts/index.d.ts.map +1 -1
  664. package/dist/prompts/index.js +3 -1
  665. package/dist/prompts/index.js.map +1 -1
  666. package/dist/prompts/prd.d.ts +0 -2
  667. package/dist/prompts/prd.d.ts.map +1 -1
  668. package/dist/prompts/prd.js +0 -2
  669. package/dist/prompts/prd.js.map +1 -1
  670. package/hooks/core/__tests__/adapter.test.cjs +340 -0
  671. package/hooks/core/adapter.cjs +463 -0
  672. package/hooks/core/config.cjs +83 -0
  673. package/hooks/core/daemon-boot.cjs +140 -0
  674. package/hooks/core/log.cjs +41 -0
  675. package/hooks/core/worktree.cjs +119 -0
  676. package/hooks/succ-post-tool.cjs +198 -134
  677. package/hooks/succ-pre-compact.cjs +262 -0
  678. package/hooks/succ-pre-tool.cjs +526 -182
  679. package/hooks/succ-session-end.cjs +40 -64
  680. package/hooks/succ-session-start.cjs +484 -430
  681. package/hooks/succ-stop-reflection.cjs +36 -62
  682. package/hooks/succ-user-prompt.cjs +137 -180
  683. package/package.json +17 -6
@@ -17,96 +17,43 @@
17
17
  * - Knowledge base stats
18
18
  */
19
19
 
20
- const { execFileSync, spawn } = require('child_process');
21
20
  const fs = require('fs');
22
21
  const path = require('path');
23
- const os = require('os');
24
-
25
- // Logging helper - writes to .succ/.tmp/hooks.log
26
- function log(succDir, message) {
27
- try {
28
- const tmpDir = path.join(succDir, '.tmp');
29
- if (!fs.existsSync(tmpDir)) {
30
- fs.mkdirSync(tmpDir, { recursive: true });
22
+ const adapter = require('./core/adapter.cjs');
23
+ const { ensureDaemon } = require('./core/daemon-boot.cjs');
24
+ const { log: _log } = require('./core/log.cjs');
25
+ const { loadMergedConfig } = require('./core/config.cjs');
26
+
27
+ adapter.runHook('session-start', async ({ agent, hookInput, projectDir, succDir }) => {
28
+ const log = (msg) => _log(succDir, 'session-start', msg);
29
+
30
+ // Load merged config (global defaults, project overrides)
31
+ const config = loadMergedConfig(projectDir);
32
+ let includeCoAuthoredBy = config.includeCoAuthoredBy !== false;
33
+ let communicationAutoAdapt = config.communicationAutoAdapt !== false;
34
+ let communicationTrackHistory = config.communicationTrackHistory === true;
35
+ let hasOpenRouterKey = !!process.env.OPENROUTER_API_KEY;
36
+ if (!hasOpenRouterKey) {
37
+ const keys = [config.llm?.api_key, config.llm?.embeddings?.api_key, config.web_search?.api_key];
38
+ if (keys.some((k) => typeof k === 'string' && k.startsWith('sk-or-'))) {
39
+ hasOpenRouterKey = true;
31
40
  }
32
- const logFile = path.join(tmpDir, 'hooks.log');
33
- const timestamp = new Date().toISOString();
34
- fs.appendFileSync(logFile, `[${timestamp}] [session-start] ${message}\n`);
35
- } catch {
36
- // Logging failed, not critical
37
41
  }
38
- }
39
-
40
- let input = '';
41
- process.stdin.setEncoding('utf8');
42
- process.stdin.on('readable', () => {
43
- let chunk;
44
- while ((chunk = process.stdin.read()) !== null) {
45
- input += chunk;
46
- }
47
- });
48
-
49
- process.stdin.on('end', async () => {
50
- try {
51
- const hookInput = JSON.parse(input);
52
- let projectDir = hookInput.cwd || process.cwd();
53
-
54
- // Windows path fix
55
- if (process.platform === 'win32' && /^\/[a-z]\//.test(projectDir)) {
56
- projectDir = projectDir[1].toUpperCase() + ':' + projectDir.slice(2);
57
- }
58
42
 
59
- const succDir = path.join(projectDir, '.succ');
60
-
61
- // Skip if succ is not initialized in this project
62
- if (!fs.existsSync(succDir)) {
63
- process.exit(0);
64
- }
65
-
66
- // Load config settings (project config overrides global, but both are checked)
67
- let includeCoAuthoredBy = true; // default: true
68
- let communicationAutoAdapt = true; // default: true
69
- let communicationTrackHistory = false; // default: false
70
- let hasOpenRouterKey = !!process.env.OPENROUTER_API_KEY;
71
- const configPaths = [
72
- // Global first, then project overrides
73
- path.join(require('os').homedir(), '.succ', 'config.json'),
74
- path.join(succDir, 'config.json'),
75
- ];
76
- for (const configPath of configPaths) {
77
- if (fs.existsSync(configPath)) {
78
- try {
79
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
80
- if (config.includeCoAuthoredBy === false) {
81
- includeCoAuthoredBy = false;
82
- }
83
- if (config.communicationAutoAdapt === false) {
84
- communicationAutoAdapt = false;
85
- }
86
- if (config.communicationTrackHistory === true) {
87
- communicationTrackHistory = true;
88
- }
89
- // Check for OpenRouter API key: llm.api_key, llm.embeddings.api_key, or web_search.api_key
90
- if (!hasOpenRouterKey) {
91
- const keys = [config.llm?.api_key, config.llm?.embeddings?.api_key, config.web_search?.api_key];
92
- if (keys.some(k => typeof k === 'string' && k.startsWith('sk-or-'))) {
93
- hasOpenRouterKey = true;
94
- }
95
- }
96
- // No break — merge both configs (global defaults, project overrides)
97
- } catch {
98
- // Ignore parse errors
99
- }
100
- }
101
- }
43
+ const contextParts = [];
44
+ const projectName = path.basename(projectDir);
102
45
 
103
- const contextParts = [];
104
- const projectName = path.basename(projectDir);
46
+ // Git Context removed - Claude Code provides native git integration
105
47
 
106
- // Git Context removed - Claude Code provides native git integration
48
+ // Canonical session identifiers derived once, used consistently throughout
49
+ // canonicalSessionId matches pre-compact hook's derivation (session_id-based)
50
+ const transcriptPath = hookInput.transcript_path || '';
51
+ const canonicalSessionId = (hookInput.session_id || 'unknown')
52
+ .replace(/[^a-zA-Z0-9_\-]/g, '_')
53
+ .slice(0, 128);
107
54
 
108
- // succ MCP Tools Reference (hybrid: XML wrapper + markdown examples)
109
- contextParts.push(`<succ-tools>
55
+ // succ MCP Tools Reference (hybrid: XML wrapper + markdown examples)
56
+ contextParts.push(`<succ-tools>
110
57
  <critical>
111
58
  ⚠️ ALWAYS pass project_path="${projectDir}" to ALL succ_* tool calls.
112
59
  Without it, succ works in global-only mode and can't access project data.
@@ -210,12 +157,16 @@ Examples:
210
157
  **succ_prd** action="export" [prd_id="prd_xxx"] — Obsidian Mermaid export
211
158
  </prd>
212
159
 
213
- ${hasOpenRouterKey ? `<web-search hint="Perplexity Sonar via OpenRouter.">
160
+ ${
161
+ hasOpenRouterKey
162
+ ? `<web-search hint="Perplexity Sonar via OpenRouter.">
214
163
  **succ_web** action="quick" query="..." — cheap & fast, simple facts
215
164
  **succ_web** action="search" query="..." [model="perplexity/sonar-pro"] — quality search, complex queries
216
165
  **succ_web** action="deep" query="..." — multi-step research (30-120s, 30+ sources)
217
166
  **succ_web** action="history" [tool_name="..."] [limit=20] — past searches and costs
218
- </web-search>` : ''}
167
+ </web-search>`
168
+ : ''
169
+ }
219
170
 
220
171
  <debug hint="Structured debugging with hypothesis testing. Sessions in .succ/debugs/.">
221
172
  **succ_debug** action="create|hypothesis|instrument|result|resolve|abandon|status|list|log|show_log|detect_lang|gen_log"
@@ -223,8 +174,8 @@ ${hasOpenRouterKey ? `<web-search hint="Perplexity Sonar via OpenRouter.">
223
174
  </debug>
224
175
  </succ-tools>`);
225
176
 
226
- // succ Agents Reference - reminds AI about available subagents
227
- contextParts.push(`<succ-agents hint="Use Task tool with subagent_type to launch these. All have semantic search + memories + brain vault.">
177
+ // succ Agents Reference - reminds AI about available subagents
178
+ contextParts.push(`<succ-agents hint="Use Task tool with subagent_type to launch these. All have semantic search + memories + brain vault.">
228
179
 
229
180
  **MANDATORY: Use succ agents instead of built-in agents.**
230
181
  | Task | Use (succ) | NEVER use (built-in) |
@@ -234,8 +185,12 @@ ${hasOpenRouterKey ? `<web-search hint="Perplexity Sonar via OpenRouter.">
234
185
  | Multi-step tasks, research | succ-general | general-purpose agent |
235
186
  | Code review | succ-code-reviewer | built-in review |
236
187
  | Pre-commit review | succ-diff-reviewer | manual diff reading |
237
- | Web page fetch | succ_fetch | WebFetch |${hasOpenRouterKey ? `
238
- | Web search | succ_web(action="quick") / succ_web(action="search") | WebSearch / Brave |` : ''}
188
+ | Web page fetch | succ_fetch | WebFetch |${
189
+ hasOpenRouterKey
190
+ ? `
191
+ | Web search | succ_web(action="quick") / succ_web(action="search") | WebSearch / Brave |`
192
+ : ''
193
+ }
239
194
 
240
195
  Direct file reads (Read/Grep) are fine when you know the exact path — for discovery, always succ agents.
241
196
 
@@ -249,401 +204,500 @@ Direct file reads (Read/Grep) are fine when you know the exact path — for disc
249
204
  | succ-pattern-detective | After multiple bug fixes — surfaces recurring patterns |
250
205
  </succ-agents>`);
251
206
 
252
- // Commit Guidelines (strict order) - only if includeCoAuthoredBy is enabled
253
- if (includeCoAuthoredBy) {
254
- contextParts.push(`<commit-format>
255
- Footer order (succ always LAST):
256
- 1. Generated with [Claude Code]
257
- 2. via [Happy] (if used)
258
- 3. powered by [succ](https://succ.ai) ← ADD THIS LINE
259
-
260
- Co-Authored-By order (succ always LAST):
261
- 1. Co-Authored-By: Claude <noreply@anthropic.com>
262
- 2. Co-Authored-By: Happy <yesreply@happy.engineering> (if used)
263
- 3. Co-Authored-By: succ <mindpalace@succ.ai> ← ADD THIS LINE
207
+ // Commit Guidelines (strict order) - only if includeCoAuthoredBy is enabled
208
+ if (includeCoAuthoredBy) {
209
+ contextParts.push(`<commit-format>
210
+ RULE: Every commit footer MUST end with the succ lines. Other tools may appear before succ but succ is always LAST.
211
+
212
+ TEMPLATE copy the relevant lines exactly:
213
+ Generated with [Claude Code](https://claude.ai/code)
214
+ powered by [succ](https://succ.ai)
215
+
216
+ Co-Authored-By: Claude <noreply@anthropic.com>
217
+ Co-Authored-By: succ <mindpalace@succ.ai>
218
+
219
+ Other tools (Happy, Cursor, etc.) may add their own "via [Tool]" and "Co-Authored-By: Tool" lines.
220
+ Place them BEFORE the succ lines. The only hard rule: succ is always the last footer line and last Co-Authored-By.
264
221
  </commit-format>`);
265
- }
222
+ }
266
223
 
267
- // Pre-commit review + commit guidelines are now handled by PreToolUse hook (succ-pre-tool.cjs)
268
- // They inject context at the exact moment of git commit, not at session start
269
-
270
- // Soul Document
271
- const soulPaths = [
272
- path.join(succDir, 'soul.md'),
273
- path.join(succDir, 'SOUL.md'),
274
- path.join(projectDir, 'soul.md'),
275
- path.join(projectDir, 'SOUL.md'),
276
- ];
277
-
278
- for (const soulPath of soulPaths) {
279
- if (fs.existsSync(soulPath)) {
280
- let soulContent = fs.readFileSync(soulPath, 'utf8').trim();
281
- if (soulContent) {
282
- // Strip Adaptation rules if auto-adapt is disabled
283
- if (!communicationAutoAdapt) {
284
- soulContent = soulContent.replace(/### Adaptation[\s\S]*?(?=\n## |\n---|\s*$)/, '');
285
- }
286
- // Inject vault tracking hint if enabled (agent handles the actual work)
287
- if (communicationTrackHistory && communicationAutoAdapt) {
288
- soulContent += `\n\n### Vault Tracking\n\ncommunicationTrackHistory is enabled. The succ-style-tracker agent will create brain vault entries in .succ/brain/communication/ when updating preferences.`;
289
- }
290
- contextParts.push('<soul>\n' + soulContent + '\n</soul>');
224
+ // Pre-commit review + commit guidelines are now handled by PreToolUse hook (succ-pre-tool.cjs)
225
+ // They inject context at the exact moment of git commit, not at session start
226
+
227
+ // Soul Document
228
+ const soulPaths = [
229
+ path.join(succDir, 'soul.md'),
230
+ path.join(succDir, 'SOUL.md'),
231
+ path.join(projectDir, 'soul.md'),
232
+ path.join(projectDir, 'SOUL.md'),
233
+ ];
234
+
235
+ for (const soulPath of soulPaths) {
236
+ if (fs.existsSync(soulPath)) {
237
+ let soulContent = fs.readFileSync(soulPath, 'utf8').trim();
238
+ if (soulContent) {
239
+ // Strip Adaptation rules if auto-adapt is disabled
240
+ if (!communicationAutoAdapt) {
241
+ soulContent = soulContent.replace(/### Adaptation[\s\S]*?(?=\n## |\n---|\s*$)/, '');
291
242
  }
292
- break;
243
+ // Inject vault tracking hint if enabled (agent handles the actual work)
244
+ if (communicationTrackHistory && communicationAutoAdapt) {
245
+ soulContent += `\n\n### Vault Tracking\n\ncommunicationTrackHistory is enabled. The succ-style-tracker agent will create brain vault entries in .succ/brain/communication/ when updating preferences.`;
246
+ }
247
+ contextParts.push('<soul>\n' + soulContent + '\n</soul>');
293
248
  }
249
+ break;
294
250
  }
251
+ }
295
252
 
296
- // Architecture / Brain Vault — inline overview + categorized doc index
297
- const brainDir = path.join(succDir, 'brain');
298
- if (fs.existsSync(brainDir)) {
299
- try {
300
- const archParts = [];
301
-
302
- // Phase 1: Find and inline Architecture Overview (compact extract)
303
- const knowledgeDir = path.join(brainDir, 'knowledge');
304
- if (fs.existsSync(knowledgeDir)) {
305
- const archFiles = fs.readdirSync(knowledgeDir)
306
- .filter(f => /architect/i.test(f) && f.endsWith('.md'))
307
- .sort(); // 00_Architecture.md first
308
- if (archFiles.length > 0) {
309
- const archContent = fs.readFileSync(path.join(knowledgeDir, archFiles[0]), 'utf8');
310
- // Strip frontmatter
311
- const body = archContent.replace(/^---[\s\S]*?\n---\s*\n/, '');
312
- // Extract from start to second "---" or "## Tech Stack" — whichever comes first
313
- // This gives us Overview + Core Mission (~15-20 lines)
314
- const overviewEnd = body.search(/\n---\n|\n## Tech Stack|\n## Directory/);
315
- const overview = overviewEnd > 0 ? body.slice(0, overviewEnd).trim() : body.slice(0, 1500).trim();
316
- if (overview) {
317
- archParts.push(overview);
318
- }
253
+ // Architecture / Brain Vault — inline overview + categorized doc index
254
+ const brainDir = path.join(succDir, 'brain');
255
+ if (fs.existsSync(brainDir)) {
256
+ try {
257
+ const archParts = [];
258
+
259
+ // Phase 1: Find and inline Architecture Overview (compact extract)
260
+ const knowledgeDir = path.join(brainDir, 'knowledge');
261
+ if (fs.existsSync(knowledgeDir)) {
262
+ const archFiles = fs
263
+ .readdirSync(knowledgeDir)
264
+ .filter((f) => /architect/i.test(f) && f.endsWith('.md'))
265
+ .sort(); // 00_Architecture.md first
266
+ if (archFiles.length > 0) {
267
+ const archContent = fs.readFileSync(path.join(knowledgeDir, archFiles[0]), 'utf8');
268
+ // Strip frontmatter
269
+ const body = archContent.replace(/^---[\s\S]*?\n---\s*\n/, '');
270
+ // Extract from start to second "---" or "## Tech Stack" — whichever comes first
271
+ // This gives us Overview + Core Mission (~15-20 lines)
272
+ const overviewEnd = body.search(/\n---\n|\n## Tech Stack|\n## Directory/);
273
+ const overview =
274
+ overviewEnd > 0 ? body.slice(0, overviewEnd).trim() : body.slice(0, 1500).trim();
275
+ if (overview) {
276
+ archParts.push(overview);
319
277
  }
320
278
  }
279
+ }
321
280
 
322
- // Phase 2: Collect remaining docs grouped by category
323
- const scanDirs = [
324
- { dir: 'knowledge', label: 'Knowledge & Research' },
325
- { dir: 'project', label: 'Project' },
326
- ];
327
- const docGroups = {};
328
-
329
- for (const { dir, label } of scanDirs) {
330
- const fullDir = path.join(brainDir, dir);
331
- if (!fs.existsSync(fullDir)) continue;
332
-
333
- const files = fs.readdirSync(fullDir, { withFileTypes: true });
334
- for (const entry of files) {
335
- if (entry.isDirectory()) continue;
336
- if (!entry.name.endsWith('.md')) continue;
337
-
338
- const filePath = path.join(fullDir, entry.name);
339
- const stat = fs.statSync(filePath);
340
- if (stat.size < 2048) continue;
341
- // Skip architecture files (already inlined above)
342
- if (/architect/i.test(entry.name)) continue;
343
-
344
- // Read first 500 bytes for frontmatter/H1
345
- const fd = fs.openSync(filePath, 'r');
346
- const buf = Buffer.alloc(500);
347
- fs.readSync(fd, buf, 0, 500, 0);
348
- fs.closeSync(fd);
349
- const head = buf.toString('utf8');
350
-
351
- let description = '';
352
- const fmMatch = head.match(/^---\s*\n([\s\S]*?)\n---/);
353
- if (fmMatch) {
354
- const descMatch = fmMatch[1].match(/description:\s*["']?([^"'\n]+)/);
355
- if (descMatch) description = descMatch[1].trim();
356
- }
357
- if (!description) {
358
- const h1Match = head.match(/^#\s+(.+)/m);
359
- if (h1Match) description = h1Match[1].trim();
360
- }
281
+ // Phase 2: Collect remaining docs grouped by category
282
+ const scanDirs = [
283
+ { dir: 'knowledge', label: 'Knowledge & Research' },
284
+ { dir: 'project', label: 'Project' },
285
+ ];
286
+ const docGroups = {};
287
+
288
+ for (const { dir, label } of scanDirs) {
289
+ const fullDir = path.join(brainDir, dir);
290
+ if (!fs.existsSync(fullDir)) continue;
291
+
292
+ const files = fs.readdirSync(fullDir, { withFileTypes: true });
293
+ for (const entry of files) {
294
+ if (entry.isDirectory()) continue;
295
+ if (!entry.name.endsWith('.md')) continue;
296
+
297
+ const filePath = path.join(fullDir, entry.name);
298
+ const stat = fs.statSync(filePath);
299
+ if (stat.size < 2048) continue;
300
+ // Skip architecture files (already inlined above)
301
+ if (/architect/i.test(entry.name)) continue;
302
+
303
+ // Read first 500 bytes for frontmatter/H1
304
+ const fd = fs.openSync(filePath, 'r');
305
+ const buf = Buffer.alloc(500);
306
+ fs.readSync(fd, buf, 0, 500, 0);
307
+ fs.closeSync(fd);
308
+ const head = buf.toString('utf8');
309
+
310
+ let description = '';
311
+ const fmMatch = head.match(/^---\s*\n([\s\S]*?)\n---/);
312
+ if (fmMatch) {
313
+ const descMatch = fmMatch[1].match(/description:\s*["']?([^"'\n]+)/);
314
+ if (descMatch) description = descMatch[1].trim();
315
+ }
316
+ if (!description) {
317
+ const h1Match = head.match(/^#\s+(.+)/m);
318
+ if (h1Match) description = h1Match[1].trim();
319
+ }
361
320
 
362
- if (description) {
363
- if (!docGroups[label]) docGroups[label] = [];
364
- docGroups[label].push(`${entry.name}: ${description}`);
365
- }
321
+ if (description) {
322
+ if (!docGroups[label]) docGroups[label] = [];
323
+ docGroups[label].push(`${entry.name}: ${description}`);
366
324
  }
367
325
  }
326
+ }
368
327
 
369
- // Phase 3: Format grouped docs
370
- const groupLines = [];
371
- for (const [label, docs] of Object.entries(docGroups)) {
372
- groupLines.push(`**${label}:** ${docs.join(' | ')}`);
373
- }
374
- if (groupLines.length > 0) {
375
- archParts.push(groupLines.join('\n'));
376
- }
328
+ // Phase 3: Format grouped docs
329
+ const groupLines = [];
330
+ for (const [label, docs] of Object.entries(docGroups)) {
331
+ groupLines.push(`**${label}:** ${docs.join(' | ')}`);
332
+ }
333
+ if (groupLines.length > 0) {
334
+ archParts.push(groupLines.join('\n'));
335
+ }
377
336
 
378
- if (archParts.length > 0) {
379
- contextParts.push(`<architecture hint="Use succ_search to read full docs">\n${archParts.join('\n\n')}\n</architecture>`);
380
- }
381
- } catch {
382
- // Brain vault scan failed, not critical
337
+ if (archParts.length > 0) {
338
+ contextParts.push(
339
+ `<architecture hint="Use succ_search to read full docs">\n${archParts.join('\n\n')}\n</architecture>`
340
+ );
383
341
  }
342
+ } catch {
343
+ // intentionally empty — brain vault scan failed, not critical
384
344
  }
345
+ }
385
346
 
386
- // Check if this is a compact event (after /compact)
387
- const isCompactEvent = hookInput.source === 'compact';
347
+ // Check if this is a compact event (after /compact)
348
+ const isCompactEvent = hookInput.source === 'compact';
388
349
 
389
- log(succDir, `source=${hookInput.source}, isCompact=${isCompactEvent}, session=${hookInput.session_id || 'unknown'}`);
350
+ log(
351
+ `source=${hookInput.source}, isCompact=${isCompactEvent}, session=${hookInput.session_id || 'unknown'}`
352
+ );
390
353
 
391
- // Precomputed Context from previous session (only on fresh start, not compact)
392
- if (!isCompactEvent) {
393
- const precomputedContextPath = path.join(succDir, 'next-session-context.md');
394
- if (fs.existsSync(precomputedContextPath)) {
395
- try {
396
- const precomputedContent = fs.readFileSync(precomputedContextPath, 'utf8').trim();
397
- if (precomputedContent) {
398
- contextParts.push('<previous-session>\n' + precomputedContent + '\n</previous-session>');
399
-
400
- // Archive the file after loading
401
- const archiveDir = path.join(succDir, '.context-archive');
402
- if (!fs.existsSync(archiveDir)) {
403
- fs.mkdirSync(archiveDir, { recursive: true });
404
- }
405
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
406
- const archivePath = path.join(archiveDir, `context-${timestamp}.md`);
407
- fs.renameSync(precomputedContextPath, archivePath);
408
-
409
- // Cleanup old archives (keep last 10)
410
- try {
411
- const archives = fs.readdirSync(archiveDir)
412
- .filter(f => f.startsWith('context-') && f.endsWith('.md'))
413
- .sort()
414
- .reverse();
415
- for (const oldArchive of archives.slice(10)) {
416
- fs.unlinkSync(path.join(archiveDir, oldArchive));
417
- }
418
- } catch {
419
- // Cleanup failed, not critical
354
+ // Precomputed Context from previous session (only on fresh start, not compact)
355
+ if (!isCompactEvent) {
356
+ const precomputedContextPath = path.join(succDir, 'next-session-context.md');
357
+ if (fs.existsSync(precomputedContextPath)) {
358
+ try {
359
+ const precomputedContent = fs.readFileSync(precomputedContextPath, 'utf8').trim();
360
+ if (precomputedContent) {
361
+ contextParts.push('<previous-session>\n' + precomputedContent + '\n</previous-session>');
362
+
363
+ // Archive the file after loading
364
+ const archiveDir = path.join(succDir, '.context-archive');
365
+ if (!fs.existsSync(archiveDir)) {
366
+ fs.mkdirSync(archiveDir, { recursive: true });
367
+ }
368
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
369
+ const archivePath = path.join(archiveDir, `context-${timestamp}.md`);
370
+ fs.renameSync(precomputedContextPath, archivePath);
371
+
372
+ // Cleanup old archives (keep last 10)
373
+ try {
374
+ const archives = fs
375
+ .readdirSync(archiveDir)
376
+ .filter((f) => f.startsWith('context-') && f.endsWith('.md'))
377
+ .sort()
378
+ .reverse();
379
+ for (const oldArchive of archives.slice(10)) {
380
+ fs.unlinkSync(path.join(archiveDir, oldArchive));
420
381
  }
382
+ } catch {
383
+ // intentionally empty — cleanup failed, not critical
421
384
  }
422
- } catch {
423
- // Ignore errors
424
385
  }
386
+ } catch {
387
+ // intentionally empty — ignore errors
425
388
  }
426
389
  }
390
+ }
427
391
 
428
- // Helper to get daemon port
429
- const tmpDir = path.join(succDir, '.tmp');
430
- const portFile = path.join(tmpDir, 'daemon.port');
431
-
432
- const getDaemonPort = () => {
433
- try {
434
- if (fs.existsSync(portFile)) {
435
- return parseInt(fs.readFileSync(portFile, 'utf8').trim(), 10);
436
- }
437
- } catch {}
438
- return null;
439
- };
392
+ // Ensure daemon is running and get port (shared module)
393
+ const { port: daemonPort } = await ensureDaemon(projectDir, log);
394
+
395
+ // Propagate daemon port to Bash environment via CLAUDE_ENV_FILE
396
+ // ensureDaemon already verified the port is alive, so no need to re-check
397
+ if (daemonPort && process.env.CLAUDE_ENV_FILE) {
398
+ try {
399
+ fs.appendFileSync(process.env.CLAUDE_ENV_FILE, `export SUCC_DAEMON_PORT=${daemonPort}\n`);
400
+ log(`Wrote SUCC_DAEMON_PORT=${daemonPort} to CLAUDE_ENV_FILE`);
401
+ } catch (err) {
402
+ log(`Failed to write CLAUDE_ENV_FILE: ${err.message || err}`);
403
+ }
404
+ }
440
405
 
441
- const checkDaemon = async (port) => {
442
- try {
443
- const response = await fetch(`http://127.0.0.1:${port}/health`, {
444
- signal: AbortSignal.timeout(2000),
445
- });
446
- const data = await response.json();
447
- return data?.status === 'ok';
448
- } catch {
449
- return false;
450
- }
451
- };
452
-
453
- const startDaemon = () => {
454
- const servicePath = path.join(projectDir, 'dist', 'daemon', 'service.js');
455
- if (fs.existsSync(servicePath)) {
456
- const daemon = spawn(process.execPath, ['--no-warnings', '--no-deprecation', servicePath], {
457
- cwd: projectDir,
458
- detached: true,
459
- stdio: 'ignore',
460
- windowsHide: true,
461
- env: { ...process.env, NODE_OPTIONS: '' },
462
- });
463
- daemon.unref();
464
- return true;
465
- }
466
- return false;
467
- };
468
-
469
- // Ensure daemon is running and get port
470
- let daemonPort = getDaemonPort();
471
- if (!daemonPort || !(await checkDaemon(daemonPort))) {
472
- startDaemon();
473
- // Wait for daemon to start (max 3 seconds)
474
- for (let i = 0; i < 30; i++) {
475
- await new Promise(r => setTimeout(r, 100));
476
- daemonPort = getDaemonPort();
477
- if (daemonPort && await checkDaemon(daemonPort)) {
478
- break;
479
- }
406
+ // Skip for service sessions (reflection subagents)
407
+ const isServiceSession = process.env.SUCC_SERVICE_SESSION === '1';
408
+
409
+ // IMPORTANT: Create compact-pending flag FIRST (before any slow operations)
410
+ // This ensures fallback context is available even if hook times out during briefing
411
+ // Hook timeout is 10s, briefing can take 60s+ → would block flag creation
412
+ if (isCompactEvent && !isServiceSession) {
413
+ const compactPendingFile = path.join(succDir, '.tmp', 'compact-pending');
414
+ try {
415
+ if (!fs.existsSync(path.join(succDir, '.tmp'))) {
416
+ fs.mkdirSync(path.join(succDir, '.tmp'), { recursive: true });
480
417
  }
418
+ // Store the full context that should be injected (without briefing, that comes later)
419
+ const contextForFallback = adapter.adaptContext(agent, contextParts.join('\n\n'));
420
+ fs.writeFileSync(compactPendingFile, contextForFallback, 'utf8');
421
+ log(`Created compact-pending flag (${contextForFallback.length} chars)`);
422
+ } catch (err) {
423
+ log(`Failed to create compact-pending: ${err.message || err}`);
481
424
  }
425
+ }
482
426
 
483
- // Skip for service sessions (reflection subagents)
484
- const isServiceSession = process.env.SUCC_SERVICE_SESSION === '1';
427
+ // Read pre-compact stats (saved by succ-pre-compact.cjs hook) and display delta
428
+ if (isCompactEvent && !isServiceSession) {
429
+ const statsFile = path.join(succDir, '.tmp', `pre-compact-stats-${canonicalSessionId}.json`);
430
+ try {
431
+ if (fs.existsSync(statsFile)) {
432
+ const stats = JSON.parse(fs.readFileSync(statsFile, 'utf8'));
433
+ const bt = stats.tokenTotals || {};
434
+
435
+ // Estimate post-compact token count from transcript (if available).
436
+ // null = transcript not available; skip delta display to avoid bogus "100% freed".
437
+ let postTokens = /** @type {number|null} */ (null);
438
+ const postByType = { text: 0, tool_use: 0, tool_result: 0, thinking: 0, image: 0 };
439
+ if (hookInput.transcript_path && fs.existsSync(hookInput.transcript_path)) {
440
+ try {
441
+ const postContent = fs.readFileSync(hookInput.transcript_path, 'utf8');
442
+ let postChars = 0;
443
+ for (const line of postContent.split('\n')) {
444
+ const trimmed = line.trim();
445
+ if (!trimmed) continue;
446
+ try {
447
+ const entry = JSON.parse(trimmed);
448
+ const msgContent = entry.message && entry.message.content;
449
+ if (typeof msgContent === 'string') {
450
+ postChars += msgContent.length;
451
+ postByType.text += msgContent.length;
452
+ } else if (Array.isArray(msgContent)) {
453
+ for (const block of msgContent) {
454
+ if (typeof block === 'string') {
455
+ postChars += block.length;
456
+ postByType.text += block.length;
457
+ } else if (block && block.type === 'text') {
458
+ const len = (block.text || '').length;
459
+ postChars += len;
460
+ postByType.text += len;
461
+ } else if (block && block.type === 'tool_use') {
462
+ const len = (block.input ? JSON.stringify(block.input).length : 0) +
463
+ (block.name || '').length + (block.id || '').length;
464
+ postChars += len;
465
+ postByType.tool_use += len;
466
+ } else if (block && block.type === 'tool_result') {
467
+ const rc = block.content;
468
+ let len = 0;
469
+ if (typeof rc === 'string') len = rc.length;
470
+ else if (Array.isArray(rc)) len = JSON.stringify(rc).length;
471
+ postChars += len;
472
+ postByType.tool_result += len;
473
+ } else if (block && block.type === 'thinking') {
474
+ const len = (block.thinking || '').length;
475
+ postChars += len;
476
+ postByType.thinking += len;
477
+ } else if (block && block.type === 'image') {
478
+ const len = block.source ? JSON.stringify(block.source).length : 100;
479
+ postChars += len;
480
+ postByType.image += len;
481
+ }
482
+ }
483
+ }
484
+ } catch {
485
+ /* skip malformed lines */
486
+ }
487
+ }
488
+ postTokens = Math.ceil(postChars / 4);
489
+ for (const k of Object.keys(postByType)) {
490
+ postByType[k] = Math.ceil(postByType[k] / 4);
491
+ }
492
+ } catch {
493
+ log('Skipping compact stats delta: failed to analyze post-compact transcript');
494
+ }
495
+ }
485
496
 
486
- // IMPORTANT: Create compact-pending flag FIRST (before any slow operations)
487
- // This ensures fallback context is available even if hook times out during briefing
488
- // Hook timeout is 10s, briefing can take 60s+ → would block flag creation
489
- if (isCompactEvent && !isServiceSession) {
490
- const compactPendingFile = path.join(succDir, '.tmp', 'compact-pending');
491
- try {
492
- if (!fs.existsSync(path.join(succDir, '.tmp'))) {
493
- fs.mkdirSync(path.join(succDir, '.tmp'), { recursive: true });
497
+ // Only display delta if post-compact tokens were actually measured
498
+ if (postTokens !== null) {
499
+ const beforeTotal = bt.total || 0;
500
+ const freed = beforeTotal - postTokens;
501
+ const pct = beforeTotal > 0 ? ((freed / beforeTotal) * 100).toFixed(1) : '0.0';
502
+
503
+ const fk = (n) => (n >= 1000 ? (n / 1000).toFixed(1) + 'K' : String(n || 0));
504
+
505
+ const statsLines = [];
506
+ statsLines.push(`Compact: ${fk(beforeTotal)} → ${fk(postTokens)} tokens (${pct}% freed)`);
507
+ statsLines.push('');
508
+ statsLines.push(` ${'Type'.padEnd(16)} ${'Before'.padStart(8)} ${'After'.padStart(8)} ${'Freed'.padStart(8)}`);
509
+ statsLines.push(` ${'─'.repeat(16)} ${'─'.repeat(8)} ${'─'.repeat(8)} ${'─'.repeat(8)}`);
510
+ for (const key of ['text', 'tool_use', 'tool_result', 'thinking', 'image']) {
511
+ const val = bt[key] || 0;
512
+ const aVal = postByType[key] || 0;
513
+ const f = val - aVal;
514
+ if (val === 0 && f === 0) continue;
515
+ statsLines.push(` ${key.padEnd(16)} ${fk(val).padStart(8)} ${fk(aVal).padStart(8)} ${fk(f).padStart(8)}`);
516
+ }
517
+ statsLines.push(` ${'─'.repeat(16)} ${'─'.repeat(8)} ${'─'.repeat(8)} ${'─'.repeat(8)}`);
518
+ statsLines.push(
519
+ ` ${'TOTAL'.padEnd(16)} ${fk(beforeTotal).padStart(8)} ${fk(postTokens).padStart(8)} ${fk(freed).padStart(8)}`
520
+ );
521
+
522
+ const topTools = (stats.topTools || []).slice(0, 5).filter((t) => t.tokens > 0);
523
+ if (topTools.length > 0) {
524
+ statsLines.push('');
525
+ statsLines.push(' Top tools (pre-compact):');
526
+ statsLines.push(' ' + topTools.map((t) => `${t.name}: ${fk(t.tokens)}`).join(' | '));
527
+ }
528
+
529
+ contextParts.push(`<compact-stats>\n${statsLines.join('\n')}\n</compact-stats>`);
530
+ log(`Compact stats: ${beforeTotal} → ${postTokens} tokens (${pct}% freed)`);
531
+ }
532
+
533
+ // Cleanup stats file
534
+ try {
535
+ fs.unlinkSync(statsFile);
536
+ } catch (e) {
537
+ log(`Failed to cleanup stats file: ${e.message || e}`);
494
538
  }
495
- // Store the full context that should be injected (without briefing, that comes later)
496
- const contextForFallback = contextParts.join('\n\n');
497
- fs.writeFileSync(compactPendingFile, contextForFallback, 'utf8');
498
- log(succDir, `Created compact-pending flag (${contextForFallback.length} chars)`);
499
- } catch (err) {
500
- log(succDir, `Failed to create compact-pending: ${err.message || err}`);
501
539
  }
540
+ } catch (err) {
541
+ log(`Failed to read compact stats: ${err.message || err}`);
502
542
  }
543
+ }
503
544
 
504
- // Generate compact briefing (slow operation - may timeout)
505
- // Even if this times out, we have the compact-pending fallback above
506
- if (isCompactEvent && daemonPort && hookInput.transcript_path && !isServiceSession) {
507
- log(succDir, `Generating compact briefing for ${hookInput.transcript_path}`);
508
- try {
509
- const response = await fetch(`http://127.0.0.1:${daemonPort}/api/briefing`, {
510
- method: 'POST',
511
- headers: { 'Content-Type': 'application/json' },
512
- body: JSON.stringify({ transcript_path: hookInput.transcript_path }),
513
- signal: AbortSignal.timeout(8000), // 8s timeout - must complete before 10s hook timeout
514
- });
515
- if (response.ok) {
516
- const result = await response.json();
517
- if (result.success && result.briefing) {
518
- contextParts.push(`<session-briefing source="compact">\n${result.briefing}\n</session-briefing>`);
519
- log(succDir, `Briefing generated: ${result.briefing.length} chars`);
520
- } else {
521
- log(succDir, `Briefing failed: ${result.error || 'no briefing returned'}`);
522
- }
545
+ // Generate compact briefing (slow operation - may timeout)
546
+ // Even if this times out, we have the compact-pending fallback above
547
+ if (isCompactEvent && daemonPort && hookInput.transcript_path && !isServiceSession) {
548
+ log(`Generating compact briefing for ${hookInput.transcript_path}`);
549
+ try {
550
+ const response = await fetch(`http://127.0.0.1:${daemonPort}/api/briefing`, {
551
+ method: 'POST',
552
+ headers: { 'Content-Type': 'application/json' },
553
+ body: JSON.stringify({ transcript_path: hookInput.transcript_path }),
554
+ signal: AbortSignal.timeout(8000), // 8s timeout - must complete before 10s hook timeout
555
+ });
556
+ if (response.ok) {
557
+ const result = await response.json();
558
+ if (result.success && result.briefing) {
559
+ contextParts.push(
560
+ `<session-briefing source="compact">\n${result.briefing}\n</session-briefing>`
561
+ );
562
+ log(`Briefing generated: ${result.briefing.length} chars`);
523
563
  } else {
524
- log(succDir, `Briefing API error: ${response.status} ${response.statusText}`);
564
+ log(`Briefing failed: ${result.error || 'no briefing returned'}`);
525
565
  }
526
- } catch (err) {
527
- log(succDir, `Briefing exception: ${err.message || err}`);
528
- // Briefing generation failed, continue without it
566
+ } else {
567
+ log(`Briefing API error: ${response.status} ${response.statusText}`);
529
568
  }
569
+ } catch (err) {
570
+ log(`Briefing exception: ${err.message || err}`);
571
+ // Briefing generation failed, continue without it
530
572
  }
573
+ }
531
574
 
532
- // Pinned + recent memories via daemon API (only on fresh start, compact uses briefing instead)
533
- if (daemonPort && !isCompactEvent) {
534
- const pinnedIds = new Set();
535
-
536
- // Phase 1: Pinned memories (Tier 1 — correction_count >= 2 or is_invariant)
537
- // Filter: skip observations (noisy subagent reports), limit to top 10 by priority_score
538
- try {
539
- const response = await fetch(`http://127.0.0.1:${daemonPort}/api/pinned`, {
540
- signal: AbortSignal.timeout(3000),
541
- });
542
- if (response.ok) {
543
- const data = await response.json();
544
- const allPinned = data.results || [];
545
- // Display only non-observation pinned, sorted by priority, top 10
546
- const displayPinned = allPinned
547
- .filter((m) => m.type !== 'observation')
548
- .sort((a, b) => (b.priority_score || 0) - (a.priority_score || 0))
549
- .slice(0, 10);
550
- if (displayPinned.length > 0) {
551
- // Track ALL pinned IDs for dedup with recent (including filtered-out observations)
552
- for (const m of allPinned) pinnedIds.add(m.id);
553
- const lines = displayPinned.map((m) => {
554
- const preview = m.content.slice(0, 100).replace(/\n/g, ' ');
555
- const type = m.type || 'obs';
556
- const reason = m.is_invariant ? 'invariant' : `corrected x${m.correction_count}`;
557
- return `#${m.id} [${type}] (${reason}) ${preview}${m.content.length > 100 ? '...' : ''}`;
558
- });
559
- contextParts.push(`<pinned-memories count="${displayPinned.length}" total="${allPinned.length}" hint="Tier 1: always loaded, high confidence">\n${lines.join('\n')}\n</pinned-memories>`);
560
- }
575
+ // Pinned + recent memories via daemon API (only on fresh start, compact uses briefing instead)
576
+ if (daemonPort && !isCompactEvent) {
577
+ const pinnedIds = new Set();
578
+
579
+ // Phase 1: Pinned memories (Tier 1 — correction_count >= 2 or is_invariant)
580
+ // Filter: skip observations (noisy subagent reports), limit to top 10 by priority_score
581
+ try {
582
+ const response = await fetch(`http://127.0.0.1:${daemonPort}/api/pinned`, {
583
+ signal: AbortSignal.timeout(3000),
584
+ });
585
+ if (response.ok) {
586
+ const data = await response.json();
587
+ const allPinned = data.results || [];
588
+ // Track ALL pinned IDs for dedup with recent (including filtered-out observations)
589
+ for (const m of allPinned) pinnedIds.add(m.id);
590
+ // Display only non-observation pinned, sorted by priority, top 10
591
+ const displayPinned = allPinned
592
+ .filter((m) => m.type !== 'observation')
593
+ .sort((a, b) => (b.priority_score || 0) - (a.priority_score || 0))
594
+ .slice(0, 10);
595
+ if (displayPinned.length > 0) {
596
+ const lines = displayPinned.map((m) => {
597
+ const preview = m.content.slice(0, 100).replace(/\n/g, ' ');
598
+ const type = m.type || 'obs';
599
+ const reason = m.is_invariant ? 'invariant' : `corrected x${m.correction_count}`;
600
+ return `#${m.id} [${type}] (${reason}) ${preview}${m.content.length > 100 ? '...' : ''}`;
601
+ });
602
+ contextParts.push(
603
+ `<pinned-memories count="${displayPinned.length}" total="${allPinned.length}" hint="Tier 1: always loaded, high confidence">\n${lines.join('\n')}\n</pinned-memories>`
604
+ );
561
605
  }
562
- } catch {
563
- // pinned memories not available
564
606
  }
607
+ } catch {
608
+ // intentionally empty — pinned memories not available
609
+ }
565
610
 
566
- // Phase 2: Recent memories (excluding pinned to avoid duplication)
567
- try {
568
- const response = await fetch(`http://127.0.0.1:${daemonPort}/api/recall`, {
569
- method: 'POST',
570
- headers: { 'Content-Type': 'application/json' },
571
- body: JSON.stringify({ query: '', limit: 5 }),
572
- signal: AbortSignal.timeout(3000),
573
- });
574
- if (response.ok) {
575
- const data = await response.json();
576
- const memories = (data.results || []).filter((m) => !pinnedIds.has(m.id));
577
- if (memories.length > 0) {
578
- const lines = memories.map((m) => {
579
- const preview = m.content.slice(0, 50).replace(/\n/g, ' ');
580
- const type = m.type || 'obs';
581
- return `#${m.id} [${type}] ${preview}${m.content.length > 50 ? '...' : ''}`;
582
- });
583
- contextParts.push(`<recent-memories count="${memories.length}" hint="Use succ_recall for details">\n${lines.join('\n')}\n</recent-memories>`);
584
- }
611
+ // Phase 2: Recent memories (excluding pinned to avoid duplication)
612
+ try {
613
+ const response = await fetch(`http://127.0.0.1:${daemonPort}/api/recall`, {
614
+ method: 'POST',
615
+ headers: { 'Content-Type': 'application/json' },
616
+ body: JSON.stringify({ query: '', limit: 5 }),
617
+ signal: AbortSignal.timeout(3000),
618
+ });
619
+ if (response.ok) {
620
+ const data = await response.json();
621
+ const memories = (data.results || []).filter((m) => !pinnedIds.has(m.id));
622
+ if (memories.length > 0) {
623
+ const lines = memories.map((m) => {
624
+ const preview = m.content.slice(0, 50).replace(/\n/g, ' ');
625
+ const type = m.type || 'obs';
626
+ return `#${m.id} [${type}] ${preview}${m.content.length > 50 ? '...' : ''}`;
627
+ });
628
+ contextParts.push(
629
+ `<recent-memories count="${memories.length}" hint="Use succ_recall for details">\n${lines.join('\n')}\n</recent-memories>`
630
+ );
585
631
  }
586
- } catch {
587
- // memories not available
588
632
  }
633
+ } catch {
634
+ // intentionally empty — memories not available
589
635
  }
636
+ }
590
637
 
591
- // Knowledge base stats via daemon API
592
- if (daemonPort) {
593
- try {
594
- const response = await fetch(`http://127.0.0.1:${daemonPort}/api/status`, {
595
- signal: AbortSignal.timeout(3000),
596
- });
597
- if (response.ok) {
598
- const status = await response.json();
599
- const docs = status.documents || 0;
600
- const mems = status.memories || 0;
601
- const code = status.codeChunks || 0;
602
- if (docs > 0 || mems > 0 || code > 0) {
603
- contextParts.push(`<knowledge-base docs="${docs}" memories="${mems}" code-chunks="${code}" />`);
604
- }
638
+ // Knowledge base stats via daemon API
639
+ if (daemonPort) {
640
+ try {
641
+ const response = await fetch(`http://127.0.0.1:${daemonPort}/api/status`, {
642
+ signal: AbortSignal.timeout(3000),
643
+ });
644
+ if (response.ok) {
645
+ const status = await response.json();
646
+ const docs = status.documents || 0;
647
+ const mems = status.memories || 0;
648
+ const code = status.codeChunks || 0;
649
+ if (docs > 0 || mems > 0 || code > 0) {
650
+ contextParts.push(
651
+ `<knowledge-base docs="${docs}" memories="${mems}" code-chunks="${code}" />`
652
+ );
605
653
  }
606
- } catch {
607
- // status not available
608
654
  }
655
+ } catch {
656
+ // intentionally empty — status not available
609
657
  }
658
+ }
610
659
 
611
- // Output context
612
- if (contextParts.length > 0) {
613
- const additionalContext = `<session project="${projectName}">\n${contextParts.join('\n\n')}\n</session>`;
614
- const output = {
615
- hookSpecificOutput: {
616
- hookEventName: 'SessionStart',
617
- additionalContext
618
- }
619
- };
620
- console.log(JSON.stringify(output));
621
- log(succDir, `Output additionalContext: ${additionalContext.length} chars, parts=${contextParts.length}`);
622
- } else {
623
- log(succDir, `No context parts to output`);
660
+ // Output context
661
+ if (contextParts.length > 0) {
662
+ // Sanitize closing wrapper tags from dynamic content to prevent XML injection
663
+ // (e.g. a stored memory or soul.md containing literal "</session>" would break the envelope)
664
+ const body = contextParts.join('\n\n').replace(/<\/session>/gi, '&lt;/session&gt;');
665
+ let additionalContext = `<session project="${projectName}">\n${body}\n</session>`;
666
+ // Strip Claude-only sections for non-Claude agents
667
+ additionalContext = adapter.adaptContext(agent, additionalContext);
668
+ const { json, exitCode } = adapter.formatOutput(agent, 'SessionStart', { additionalContext });
669
+ if (json && Object.keys(json).length > 0) {
670
+ console.log(JSON.stringify(json));
624
671
  }
672
+ log(
673
+ `Output additionalContext: ${additionalContext.length} chars, parts=${contextParts.length}, agent=${agent}`
674
+ );
675
+ if (exitCode) process.exit(exitCode); // non-zero = deny (Cursor/Gemini); 0 falls through to session registration
676
+ } else {
677
+ log(`No context parts to output`);
678
+ }
625
679
 
626
- // Register session with daemon
627
- if (daemonPort) {
628
- const transcriptPath = hookInput.transcript_path || '';
629
- const sessionId = transcriptPath ? path.basename(transcriptPath, '.jsonl') : `session-${Date.now()}`;
630
- // isServiceSession already defined above
631
-
632
- try {
633
- await fetch(`http://127.0.0.1:${daemonPort}/api/session/register`, {
634
- method: 'POST',
635
- headers: { 'Content-Type': 'application/json' },
636
- body: JSON.stringify({ session_id: sessionId, transcript_path: transcriptPath, is_service: isServiceSession }),
637
- signal: AbortSignal.timeout(3000),
638
- });
639
- } catch {
640
- // Registration failed, continue anyway
680
+ // Register session with daemon
681
+ // transcriptPath and canonicalSessionId derived early — reuse here for consistency
682
+ if (daemonPort) {
683
+ try {
684
+ const res = await fetch(`http://127.0.0.1:${daemonPort}/api/session/register`, {
685
+ method: 'POST',
686
+ headers: { 'Content-Type': 'application/json' },
687
+ body: JSON.stringify({
688
+ session_id: canonicalSessionId,
689
+ transcript_path: transcriptPath,
690
+ is_service: isServiceSession,
691
+ }),
692
+ signal: AbortSignal.timeout(3000),
693
+ });
694
+ if (!res.ok) {
695
+ log(`Session register failed: ${res.status} ${res.statusText} session=${canonicalSessionId}`);
641
696
  }
697
+ } catch (err) {
698
+ log(`Session register error for ${canonicalSessionId}: ${err.message || err}`);
642
699
  }
643
-
644
- process.exit(0);
645
-
646
- } catch (err) {
647
- process.exit(0);
648
700
  }
701
+
702
+ process.exit(0);
649
703
  });