@flyingrobots/graft 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (563) hide show
  1. package/ADVANCED_GUIDE.md +52 -0
  2. package/CHANGELOG.md +142 -0
  3. package/GUIDE.md +53 -0
  4. package/README.md +1 -1
  5. package/dist/adapters/json-text-decoder.d.ts +3 -0
  6. package/dist/adapters/json-text-decoder.d.ts.map +1 -0
  7. package/dist/adapters/json-text-decoder.js +12 -0
  8. package/dist/adapters/json-text-decoder.js.map +1 -0
  9. package/dist/adapters/node-paths.d.ts +3 -0
  10. package/dist/adapters/node-paths.d.ts.map +1 -0
  11. package/dist/adapters/node-paths.js +29 -0
  12. package/dist/adapters/node-paths.js.map +1 -0
  13. package/dist/adapters/repo-paths.d.ts.map +1 -1
  14. package/dist/adapters/repo-paths.js +40 -5
  15. package/dist/adapters/repo-paths.js.map +1 -1
  16. package/dist/api/tool-bridge.d.ts +1 -1
  17. package/dist/api/tool-bridge.d.ts.map +1 -1
  18. package/dist/api/tool-bridge.js +1 -1
  19. package/dist/api/tool-bridge.js.map +1 -1
  20. package/dist/cli/cli-error.d.ts.map +1 -1
  21. package/dist/cli/cli-error.js +47 -10
  22. package/dist/cli/cli-error.js.map +1 -1
  23. package/dist/cli/command-parser.d.ts +12 -3
  24. package/dist/cli/command-parser.d.ts.map +1 -1
  25. package/dist/cli/command-parser.js +161 -2
  26. package/dist/cli/command-parser.js.map +1 -1
  27. package/dist/cli/daemon-status-model.d.ts +87 -0
  28. package/dist/cli/daemon-status-model.d.ts.map +1 -0
  29. package/dist/cli/daemon-status-model.js +142 -0
  30. package/dist/cli/daemon-status-model.js.map +1 -0
  31. package/dist/cli/daemon-status-render.d.ts +3 -0
  32. package/dist/cli/daemon-status-render.d.ts.map +1 -0
  33. package/dist/cli/daemon-status-render.js +100 -0
  34. package/dist/cli/daemon-status-render.js.map +1 -0
  35. package/dist/cli/daemon-status.d.ts +7 -0
  36. package/dist/cli/daemon-status.d.ts.map +1 -0
  37. package/dist/cli/daemon-status.js +79 -0
  38. package/dist/cli/daemon-status.js.map +1 -0
  39. package/dist/cli/git-graft-enhance-model.d.ts +67 -0
  40. package/dist/cli/git-graft-enhance-model.d.ts.map +1 -0
  41. package/dist/cli/git-graft-enhance-model.js +55 -0
  42. package/dist/cli/git-graft-enhance-model.js.map +1 -0
  43. package/dist/cli/git-graft-enhance-render.d.ts +3 -0
  44. package/dist/cli/git-graft-enhance-render.d.ts.map +1 -0
  45. package/dist/cli/git-graft-enhance-render.js +29 -0
  46. package/dist/cli/git-graft-enhance-render.js.map +1 -0
  47. package/dist/cli/git-graft-enhance.d.ts +14 -0
  48. package/dist/cli/git-graft-enhance.d.ts.map +1 -0
  49. package/dist/cli/git-graft-enhance.js +29 -0
  50. package/dist/cli/git-graft-enhance.js.map +1 -0
  51. package/dist/cli/index-cmd.d.ts.map +1 -1
  52. package/dist/cli/index-cmd.js +18 -21
  53. package/dist/cli/index-cmd.js.map +1 -1
  54. package/dist/cli/index-model.d.ts +5 -9
  55. package/dist/cli/index-model.d.ts.map +1 -1
  56. package/dist/cli/index-model.js +20 -18
  57. package/dist/cli/index-model.js.map +1 -1
  58. package/dist/cli/init-client-config.d.ts +12 -7
  59. package/dist/cli/init-client-config.d.ts.map +1 -1
  60. package/dist/cli/init-client-config.js +105 -43
  61. package/dist/cli/init-client-config.js.map +1 -1
  62. package/dist/cli/init-model.d.ts +9 -2
  63. package/dist/cli/init-model.d.ts.map +1 -1
  64. package/dist/cli/init-model.js +52 -4
  65. package/dist/cli/init-model.js.map +1 -1
  66. package/dist/cli/init-render.d.ts.map +1 -1
  67. package/dist/cli/init-render.js +2 -2
  68. package/dist/cli/init-render.js.map +1 -1
  69. package/dist/cli/init.d.ts.map +1 -1
  70. package/dist/cli/init.js +11 -9
  71. package/dist/cli/init.js.map +1 -1
  72. package/dist/cli/json-document.d.ts +5 -0
  73. package/dist/cli/json-document.d.ts.map +1 -1
  74. package/dist/cli/json-document.js +9 -0
  75. package/dist/cli/json-document.js.map +1 -1
  76. package/dist/cli/main.d.ts +7 -0
  77. package/dist/cli/main.d.ts.map +1 -1
  78. package/dist/cli/main.js +49 -7
  79. package/dist/cli/main.js.map +1 -1
  80. package/dist/cli/migrate-local-history.d.ts.map +1 -1
  81. package/dist/cli/migrate-local-history.js +3 -2
  82. package/dist/cli/migrate-local-history.js.map +1 -1
  83. package/dist/cli/peer-command.d.ts +1 -1
  84. package/dist/cli/peer-command.d.ts.map +1 -1
  85. package/dist/cli/peer-command.js +1 -1
  86. package/dist/cli/peer-command.js.map +1 -1
  87. package/dist/contracts/capabilities.d.ts +5 -5
  88. package/dist/contracts/capabilities.d.ts.map +1 -1
  89. package/dist/contracts/capabilities.js +89 -0
  90. package/dist/contracts/capabilities.js.map +1 -1
  91. package/dist/contracts/causal-ontology.d.ts +17 -17
  92. package/dist/contracts/json-object.d.ts +0 -1
  93. package/dist/contracts/json-object.d.ts.map +1 -1
  94. package/dist/contracts/json-object.js +0 -10
  95. package/dist/contracts/json-object.js.map +1 -1
  96. package/dist/contracts/output-schema-cli.d.ts +277 -25
  97. package/dist/contracts/output-schema-cli.d.ts.map +1 -1
  98. package/dist/contracts/output-schema-cli.js +35 -3
  99. package/dist/contracts/output-schema-cli.js.map +1 -1
  100. package/dist/contracts/output-schema-fragments.d.ts +212 -87
  101. package/dist/contracts/output-schema-fragments.d.ts.map +1 -1
  102. package/dist/contracts/output-schema-fragments.js +49 -2
  103. package/dist/contracts/output-schema-fragments.js.map +1 -1
  104. package/dist/contracts/output-schema-mcp.d.ts +372 -47
  105. package/dist/contracts/output-schema-mcp.d.ts.map +1 -1
  106. package/dist/contracts/output-schema-mcp.js +217 -1
  107. package/dist/contracts/output-schema-mcp.js.map +1 -1
  108. package/dist/contracts/output-schema-meta.d.ts +2 -2
  109. package/dist/contracts/output-schema-meta.d.ts.map +1 -1
  110. package/dist/contracts/output-schemas.d.ts +7 -7
  111. package/dist/contracts/output-schemas.d.ts.map +1 -1
  112. package/dist/contracts/output-schemas.js +294 -6
  113. package/dist/contracts/output-schemas.js.map +1 -1
  114. package/dist/git/diff.d.ts +11 -0
  115. package/dist/git/diff.d.ts.map +1 -1
  116. package/dist/git/diff.js +50 -6
  117. package/dist/git/diff.js.map +1 -1
  118. package/dist/mcp/burden.d.ts.map +1 -1
  119. package/dist/mcp/burden.js +9 -0
  120. package/dist/mcp/burden.js.map +1 -1
  121. package/dist/mcp/context.d.ts +3 -2
  122. package/dist/mcp/context.d.ts.map +1 -1
  123. package/dist/mcp/context.js +2 -37
  124. package/dist/mcp/context.js.map +1 -1
  125. package/dist/mcp/daemon-server.d.ts +2 -0
  126. package/dist/mcp/daemon-server.d.ts.map +1 -1
  127. package/dist/mcp/daemon-server.js +6 -1
  128. package/dist/mcp/daemon-server.js.map +1 -1
  129. package/dist/mcp/daemon-session-host.d.ts +1 -0
  130. package/dist/mcp/daemon-session-host.d.ts.map +1 -1
  131. package/dist/mcp/daemon-session-host.js +3 -0
  132. package/dist/mcp/daemon-session-host.js.map +1 -1
  133. package/dist/mcp/daemon-stdio-bridge.d.ts +1 -0
  134. package/dist/mcp/daemon-stdio-bridge.d.ts.map +1 -1
  135. package/dist/mcp/daemon-stdio-bridge.js +1 -0
  136. package/dist/mcp/daemon-stdio-bridge.js.map +1 -1
  137. package/dist/mcp/monitor-tick-job.d.ts +1 -0
  138. package/dist/mcp/monitor-tick-job.d.ts.map +1 -1
  139. package/dist/mcp/monitor-tick-job.js +84 -29
  140. package/dist/mcp/monitor-tick-job.js.map +1 -1
  141. package/dist/mcp/monitor-types.d.ts +1 -1
  142. package/dist/mcp/monitor-types.d.ts.map +1 -1
  143. package/dist/mcp/persisted-local-history-graph.d.ts +2 -2
  144. package/dist/mcp/persisted-local-history-graph.d.ts.map +1 -1
  145. package/dist/mcp/persisted-local-history-graph.js +3 -2
  146. package/dist/mcp/persisted-local-history-graph.js.map +1 -1
  147. package/dist/mcp/persisted-local-history.d.ts +5 -5
  148. package/dist/mcp/persisted-local-history.js +2 -2
  149. package/dist/mcp/persisted-local-history.js.map +1 -1
  150. package/dist/mcp/persistent-monitor-runtime.d.ts +1 -0
  151. package/dist/mcp/persistent-monitor-runtime.d.ts.map +1 -1
  152. package/dist/mcp/persistent-monitor-runtime.js +35 -0
  153. package/dist/mcp/persistent-monitor-runtime.js.map +1 -1
  154. package/dist/mcp/repo-tool-worker-context.d.ts.map +1 -1
  155. package/dist/mcp/repo-tool-worker-context.js +5 -2
  156. package/dist/mcp/repo-tool-worker-context.js.map +1 -1
  157. package/dist/mcp/server-context.d.ts.map +1 -1
  158. package/dist/mcp/server-context.js +13 -0
  159. package/dist/mcp/server-context.js.map +1 -1
  160. package/dist/mcp/server-tool-access.d.ts +1 -1
  161. package/dist/mcp/server-tool-access.d.ts.map +1 -1
  162. package/dist/mcp/server-tool-access.js +3 -1
  163. package/dist/mcp/server-tool-access.js.map +1 -1
  164. package/dist/mcp/server.d.ts +3 -0
  165. package/dist/mcp/server.d.ts.map +1 -1
  166. package/dist/mcp/server.js +5 -3
  167. package/dist/mcp/server.js.map +1 -1
  168. package/dist/mcp/tool-registry.d.ts.map +1 -1
  169. package/dist/mcp/tool-registry.js +18 -0
  170. package/dist/mcp/tool-registry.js.map +1 -1
  171. package/dist/mcp/tools/code-find.d.ts.map +1 -1
  172. package/dist/mcp/tools/code-find.js +20 -0
  173. package/dist/mcp/tools/code-find.js.map +1 -1
  174. package/dist/mcp/tools/code-show.d.ts.map +1 -1
  175. package/dist/mcp/tools/code-show.js +24 -0
  176. package/dist/mcp/tools/code-show.js.map +1 -1
  177. package/dist/mcp/tools/diagnostic-models.d.ts +2 -0
  178. package/dist/mcp/tools/diagnostic-models.d.ts.map +1 -1
  179. package/dist/mcp/tools/doctor.d.ts.map +1 -1
  180. package/dist/mcp/tools/doctor.js +18 -1
  181. package/dist/mcp/tools/doctor.js.map +1 -1
  182. package/dist/mcp/tools/export-surface-diff.d.ts +3 -0
  183. package/dist/mcp/tools/export-surface-diff.d.ts.map +1 -0
  184. package/dist/mcp/tools/export-surface-diff.js +24 -0
  185. package/dist/mcp/tools/export-surface-diff.js.map +1 -0
  186. package/dist/mcp/tools/file-outline.js +1 -1
  187. package/dist/mcp/tools/file-outline.js.map +1 -1
  188. package/dist/mcp/tools/graft-edit.d.ts +3 -0
  189. package/dist/mcp/tools/graft-edit.d.ts.map +1 -0
  190. package/dist/mcp/tools/graft-edit.js +192 -0
  191. package/dist/mcp/tools/graft-edit.js.map +1 -0
  192. package/dist/mcp/tools/knowledge-map.d.ts +3 -0
  193. package/dist/mcp/tools/knowledge-map.d.ts.map +1 -0
  194. package/dist/mcp/tools/knowledge-map.js +31 -0
  195. package/dist/mcp/tools/knowledge-map.js.map +1 -0
  196. package/dist/mcp/tools/monitor-nudge.d.ts +3 -0
  197. package/dist/mcp/tools/monitor-nudge.d.ts.map +1 -0
  198. package/dist/mcp/tools/monitor-nudge.js +17 -0
  199. package/dist/mcp/tools/monitor-nudge.js.map +1 -0
  200. package/dist/mcp/tools/precision-warp.d.ts +3 -3
  201. package/dist/mcp/tools/precision-warp.d.ts.map +1 -1
  202. package/dist/mcp/tools/precision-warp.js +24 -15
  203. package/dist/mcp/tools/precision-warp.js.map +1 -1
  204. package/dist/mcp/tools/refactor-difficulty.d.ts +3 -0
  205. package/dist/mcp/tools/refactor-difficulty.d.ts.map +1 -0
  206. package/dist/mcp/tools/refactor-difficulty.js +36 -0
  207. package/dist/mcp/tools/refactor-difficulty.js.map +1 -0
  208. package/dist/mcp/tools/safe-read.js +2 -2
  209. package/dist/mcp/tools/safe-read.js.map +1 -1
  210. package/dist/mcp/tools/structural-blame.d.ts +3 -0
  211. package/dist/mcp/tools/structural-blame.d.ts.map +1 -0
  212. package/dist/mcp/tools/structural-blame.js +44 -0
  213. package/dist/mcp/tools/structural-blame.js.map +1 -0
  214. package/dist/mcp/tools/structural-churn.d.ts +3 -0
  215. package/dist/mcp/tools/structural-churn.d.ts.map +1 -0
  216. package/dist/mcp/tools/structural-churn.js +28 -0
  217. package/dist/mcp/tools/structural-churn.js.map +1 -0
  218. package/dist/mcp/tools/structural-log.d.ts +3 -0
  219. package/dist/mcp/tools/structural-log.d.ts.map +1 -0
  220. package/dist/mcp/tools/structural-log.js +38 -0
  221. package/dist/mcp/tools/structural-log.js.map +1 -0
  222. package/dist/mcp/tools/structural-review.d.ts +3 -0
  223. package/dist/mcp/tools/structural-review.d.ts.map +1 -0
  224. package/dist/mcp/tools/structural-review.js +55 -0
  225. package/dist/mcp/tools/structural-review.js.map +1 -0
  226. package/dist/mcp/warp-pool.d.ts +4 -4
  227. package/dist/mcp/warp-pool.d.ts.map +1 -1
  228. package/dist/mcp/warp-pool.js.map +1 -1
  229. package/dist/mcp/workspace-router-model.d.ts +2 -2
  230. package/dist/mcp/workspace-router-model.d.ts.map +1 -1
  231. package/dist/mcp/workspace-router-runtime.d.ts +3 -3
  232. package/dist/mcp/workspace-router-runtime.d.ts.map +1 -1
  233. package/dist/mcp/workspace-router-runtime.js +4 -1
  234. package/dist/mcp/workspace-router-runtime.js.map +1 -1
  235. package/dist/mcp/workspace-router.d.ts +3 -2
  236. package/dist/mcp/workspace-router.d.ts.map +1 -1
  237. package/dist/mcp/workspace-router.js +6 -0
  238. package/dist/mcp/workspace-router.js.map +1 -1
  239. package/dist/operations/adaptive-projection.d.ts +22 -0
  240. package/dist/operations/adaptive-projection.d.ts.map +1 -0
  241. package/dist/operations/adaptive-projection.js +36 -0
  242. package/dist/operations/adaptive-projection.js.map +1 -0
  243. package/dist/operations/agent-handoff.d.ts +25 -0
  244. package/dist/operations/agent-handoff.d.ts.map +1 -0
  245. package/dist/operations/agent-handoff.js +36 -0
  246. package/dist/operations/agent-handoff.js.map +1 -0
  247. package/dist/operations/capture-range.d.ts +33 -0
  248. package/dist/operations/capture-range.d.ts.map +1 -0
  249. package/dist/operations/capture-range.js +45 -0
  250. package/dist/operations/capture-range.js.map +1 -0
  251. package/dist/operations/conversation-primer.d.ts +27 -0
  252. package/dist/operations/conversation-primer.d.ts.map +1 -0
  253. package/dist/operations/conversation-primer.js +52 -0
  254. package/dist/operations/conversation-primer.js.map +1 -0
  255. package/dist/operations/cross-session-resume.d.ts +28 -0
  256. package/dist/operations/cross-session-resume.d.ts.map +1 -0
  257. package/dist/operations/cross-session-resume.js +79 -0
  258. package/dist/operations/cross-session-resume.js.map +1 -0
  259. package/dist/operations/deterministic-replay.d.ts +39 -0
  260. package/dist/operations/deterministic-replay.d.ts.map +1 -0
  261. package/dist/operations/deterministic-replay.js +90 -0
  262. package/dist/operations/deterministic-replay.js.map +1 -0
  263. package/dist/operations/export-surface-diff.d.ts +36 -0
  264. package/dist/operations/export-surface-diff.d.ts.map +1 -0
  265. package/dist/operations/export-surface-diff.js +298 -0
  266. package/dist/operations/export-surface-diff.js.map +1 -0
  267. package/dist/operations/footprint-parallelism.d.ts +25 -0
  268. package/dist/operations/footprint-parallelism.d.ts.map +1 -0
  269. package/dist/operations/footprint-parallelism.js +79 -0
  270. package/dist/operations/footprint-parallelism.js.map +1 -0
  271. package/dist/operations/graft-diff.d.ts.map +1 -1
  272. package/dist/operations/graft-diff.js +14 -7
  273. package/dist/operations/graft-diff.js.map +1 -1
  274. package/dist/operations/horizon-of-readability.d.ts +23 -0
  275. package/dist/operations/horizon-of-readability.d.ts.map +1 -0
  276. package/dist/operations/horizon-of-readability.js +39 -0
  277. package/dist/operations/horizon-of-readability.js.map +1 -0
  278. package/dist/operations/import-reference-impact.d.ts +13 -0
  279. package/dist/operations/import-reference-impact.d.ts.map +1 -0
  280. package/dist/operations/import-reference-impact.js +101 -0
  281. package/dist/operations/import-reference-impact.js.map +1 -0
  282. package/dist/operations/knowledge-map.d.ts +30 -0
  283. package/dist/operations/knowledge-map.d.ts.map +1 -0
  284. package/dist/operations/knowledge-map.js +64 -0
  285. package/dist/operations/knowledge-map.js.map +1 -0
  286. package/dist/operations/observation-cache.d.ts +8 -2
  287. package/dist/operations/observation-cache.d.ts.map +1 -1
  288. package/dist/operations/observation-cache.js +17 -5
  289. package/dist/operations/observation-cache.js.map +1 -1
  290. package/dist/operations/projection-safety.d.ts +31 -0
  291. package/dist/operations/projection-safety.d.ts.map +1 -0
  292. package/dist/operations/projection-safety.js +77 -0
  293. package/dist/operations/projection-safety.js.map +1 -0
  294. package/dist/operations/repo-workspace.js +3 -3
  295. package/dist/operations/repo-workspace.js.map +1 -1
  296. package/dist/operations/semantic-drift.d.ts +27 -0
  297. package/dist/operations/semantic-drift.d.ts.map +1 -0
  298. package/dist/operations/semantic-drift.js +46 -0
  299. package/dist/operations/semantic-drift.js.map +1 -0
  300. package/dist/operations/session-filtration.d.ts +36 -0
  301. package/dist/operations/session-filtration.d.ts.map +1 -0
  302. package/dist/operations/session-filtration.js +48 -0
  303. package/dist/operations/session-filtration.js.map +1 -0
  304. package/dist/operations/session-replay.d.ts +17 -0
  305. package/dist/operations/session-replay.d.ts.map +1 -0
  306. package/dist/operations/session-replay.js +59 -0
  307. package/dist/operations/session-replay.js.map +1 -0
  308. package/dist/operations/sludge-detector.d.ts +45 -0
  309. package/dist/operations/sludge-detector.d.ts.map +1 -0
  310. package/dist/operations/sludge-detector.js +278 -0
  311. package/dist/operations/sludge-detector.js.map +1 -0
  312. package/dist/operations/structural-blame.d.ts +67 -0
  313. package/dist/operations/structural-blame.d.ts.map +1 -0
  314. package/dist/operations/structural-blame.js +111 -0
  315. package/dist/operations/structural-blame.js.map +1 -0
  316. package/dist/operations/structural-review.d.ts +47 -0
  317. package/dist/operations/structural-review.d.ts.map +1 -0
  318. package/dist/operations/structural-review.js +225 -0
  319. package/dist/operations/structural-review.js.map +1 -0
  320. package/dist/operations/teaching-hints.d.ts +18 -0
  321. package/dist/operations/teaching-hints.d.ts.map +1 -0
  322. package/dist/operations/teaching-hints.js +35 -0
  323. package/dist/operations/teaching-hints.js.map +1 -0
  324. package/dist/parser/diff.d.ts +2 -0
  325. package/dist/parser/diff.d.ts.map +1 -1
  326. package/dist/parser/diff.js +7 -0
  327. package/dist/parser/diff.js.map +1 -1
  328. package/dist/ports/paths.d.ts +17 -0
  329. package/dist/ports/paths.d.ts.map +1 -0
  330. package/dist/ports/{warp.js → paths.js} +2 -2
  331. package/dist/ports/paths.js.map +1 -0
  332. package/dist/warp/ast-emitter.d.ts +28 -0
  333. package/dist/warp/ast-emitter.d.ts.map +1 -0
  334. package/dist/warp/ast-emitter.js +90 -0
  335. package/dist/warp/ast-emitter.js.map +1 -0
  336. package/dist/warp/ast-import-resolver.d.ts +13 -0
  337. package/dist/warp/ast-import-resolver.d.ts.map +1 -0
  338. package/dist/warp/ast-import-resolver.js +259 -0
  339. package/dist/warp/ast-import-resolver.js.map +1 -0
  340. package/dist/warp/commit-meta.d.ts +8 -0
  341. package/dist/warp/commit-meta.d.ts.map +1 -0
  342. package/dist/warp/commit-meta.js +13 -0
  343. package/dist/warp/commit-meta.js.map +1 -0
  344. package/dist/warp/context.d.ts +10 -0
  345. package/dist/warp/context.d.ts.map +1 -0
  346. package/dist/warp/context.js +26 -0
  347. package/dist/warp/context.js.map +1 -0
  348. package/dist/warp/dead-symbols.d.ts +40 -0
  349. package/dist/warp/dead-symbols.d.ts.map +1 -0
  350. package/dist/warp/dead-symbols.js +127 -0
  351. package/dist/warp/dead-symbols.js.map +1 -0
  352. package/dist/warp/drift-sentinel.d.ts +30 -0
  353. package/dist/warp/drift-sentinel.d.ts.map +1 -0
  354. package/dist/warp/drift-sentinel.js +65 -0
  355. package/dist/warp/drift-sentinel.js.map +1 -0
  356. package/dist/warp/index-head.d.ts +32 -0
  357. package/dist/warp/index-head.d.ts.map +1 -0
  358. package/dist/warp/index-head.js +245 -0
  359. package/dist/warp/index-head.js.map +1 -0
  360. package/dist/warp/observers.d.ts +6 -6
  361. package/dist/warp/observers.d.ts.map +1 -1
  362. package/dist/warp/observers.js +5 -4
  363. package/dist/warp/observers.js.map +1 -1
  364. package/dist/warp/open.d.ts +5 -4
  365. package/dist/warp/open.d.ts.map +1 -1
  366. package/dist/warp/open.js +24 -49
  367. package/dist/warp/open.js.map +1 -1
  368. package/dist/warp/outline-diff-trailer.d.ts +26 -0
  369. package/dist/warp/outline-diff-trailer.d.ts.map +1 -0
  370. package/dist/warp/outline-diff-trailer.js +68 -0
  371. package/dist/warp/outline-diff-trailer.js.map +1 -0
  372. package/dist/warp/refactor-difficulty.d.ts +38 -0
  373. package/dist/warp/refactor-difficulty.d.ts.map +1 -0
  374. package/dist/warp/refactor-difficulty.js +169 -0
  375. package/dist/warp/refactor-difficulty.js.map +1 -0
  376. package/dist/warp/references.d.ts +22 -0
  377. package/dist/warp/references.d.ts.map +1 -0
  378. package/dist/warp/references.js +57 -0
  379. package/dist/warp/references.js.map +1 -0
  380. package/dist/warp/stale-docs.d.ts +56 -0
  381. package/dist/warp/stale-docs.d.ts.map +1 -0
  382. package/dist/warp/stale-docs.js +149 -0
  383. package/dist/warp/stale-docs.js.map +1 -0
  384. package/dist/warp/structural-drift-detection.d.ts +49 -0
  385. package/dist/warp/structural-drift-detection.d.ts.map +1 -0
  386. package/dist/warp/structural-drift-detection.js +51 -0
  387. package/dist/warp/structural-drift-detection.js.map +1 -0
  388. package/dist/warp/structural-queries.d.ts +49 -0
  389. package/dist/warp/structural-queries.d.ts.map +1 -0
  390. package/dist/warp/structural-queries.js +175 -0
  391. package/dist/warp/structural-queries.js.map +1 -0
  392. package/dist/warp/symbol-timeline.d.ts +34 -0
  393. package/dist/warp/symbol-timeline.d.ts.map +1 -0
  394. package/dist/warp/symbol-timeline.js +127 -0
  395. package/dist/warp/symbol-timeline.js.map +1 -0
  396. package/dist/warp/traverse-hydrate.d.ts +23 -0
  397. package/dist/warp/traverse-hydrate.d.ts.map +1 -0
  398. package/dist/warp/traverse-hydrate.js +39 -0
  399. package/dist/warp/traverse-hydrate.js.map +1 -0
  400. package/dist/warp/warp-reference-count.d.ts +19 -0
  401. package/dist/warp/warp-reference-count.d.ts.map +1 -0
  402. package/dist/warp/warp-reference-count.js +32 -0
  403. package/dist/warp/warp-reference-count.js.map +1 -0
  404. package/dist/warp/warp-structural-blame.d.ts +20 -0
  405. package/dist/warp/warp-structural-blame.d.ts.map +1 -0
  406. package/dist/warp/warp-structural-blame.js +43 -0
  407. package/dist/warp/warp-structural-blame.js.map +1 -0
  408. package/dist/warp/warp-structural-churn.d.ts +30 -0
  409. package/dist/warp/warp-structural-churn.d.ts.map +1 -0
  410. package/dist/warp/warp-structural-churn.js +213 -0
  411. package/dist/warp/warp-structural-churn.js.map +1 -0
  412. package/dist/warp/warp-structural-log.d.ts +35 -0
  413. package/dist/warp/warp-structural-log.d.ts.map +1 -0
  414. package/dist/warp/warp-structural-log.js +104 -0
  415. package/dist/warp/warp-structural-log.js.map +1 -0
  416. package/docs/CLI.md +68 -4
  417. package/docs/MCP.md +23 -0
  418. package/docs/SETUP.md +779 -0
  419. package/package.json +10 -7
  420. package/src/adapters/json-text-decoder.ts +11 -0
  421. package/src/adapters/node-paths.ts +35 -0
  422. package/src/adapters/repo-paths.ts +45 -4
  423. package/src/api/tool-bridge.ts +2 -1
  424. package/src/cli/cli-error.ts +49 -10
  425. package/src/cli/command-parser.ts +185 -3
  426. package/src/cli/daemon-status-model.ts +238 -0
  427. package/src/cli/daemon-status-render.ts +102 -0
  428. package/src/cli/daemon-status.ts +100 -0
  429. package/src/cli/git-graft-enhance-model.ts +131 -0
  430. package/src/cli/git-graft-enhance-render.ts +33 -0
  431. package/src/cli/git-graft-enhance.ts +54 -0
  432. package/src/cli/index-cmd.ts +18 -22
  433. package/src/cli/index-model.ts +21 -22
  434. package/src/cli/init-client-config.ts +167 -38
  435. package/src/cli/init-model.ts +57 -2
  436. package/src/cli/init-render.ts +2 -2
  437. package/src/cli/init.ts +11 -9
  438. package/src/cli/json-document.ts +11 -0
  439. package/src/cli/main.ts +53 -6
  440. package/src/cli/migrate-local-history.ts +3 -2
  441. package/src/cli/peer-command.ts +2 -1
  442. package/src/contracts/capabilities.ts +90 -0
  443. package/src/contracts/json-object.ts +1 -11
  444. package/src/contracts/output-schema-cli.ts +36 -3
  445. package/src/contracts/output-schema-fragments.ts +52 -2
  446. package/src/contracts/output-schema-mcp.ts +219 -0
  447. package/src/contracts/output-schemas.ts +300 -6
  448. package/src/git/diff.ts +61 -0
  449. package/src/mcp/burden.ts +9 -0
  450. package/src/mcp/context.ts +5 -42
  451. package/src/mcp/daemon-server.ts +8 -1
  452. package/src/mcp/daemon-session-host.ts +4 -0
  453. package/src/mcp/daemon-stdio-bridge.ts +2 -0
  454. package/src/mcp/monitor-tick-job.ts +90 -39
  455. package/src/mcp/monitor-types.ts +1 -1
  456. package/src/mcp/persisted-local-history-graph.ts +5 -4
  457. package/src/mcp/persisted-local-history.ts +2 -2
  458. package/src/mcp/persistent-monitor-runtime.ts +37 -0
  459. package/src/mcp/repo-tool-worker-context.ts +5 -2
  460. package/src/mcp/server-context.ts +15 -2
  461. package/src/mcp/server-tool-access.ts +4 -1
  462. package/src/mcp/server.ts +8 -3
  463. package/src/mcp/tool-registry.ts +18 -0
  464. package/src/mcp/tools/code-find.ts +23 -0
  465. package/src/mcp/tools/code-show.ts +27 -0
  466. package/src/mcp/tools/diagnostic-models.ts +2 -0
  467. package/src/mcp/tools/doctor.ts +18 -1
  468. package/src/mcp/tools/export-surface-diff.ts +26 -0
  469. package/src/mcp/tools/file-outline.ts +1 -1
  470. package/src/mcp/tools/graft-edit.ts +276 -0
  471. package/src/mcp/tools/knowledge-map.ts +34 -0
  472. package/src/mcp/tools/monitor-nudge.ts +19 -0
  473. package/src/mcp/tools/precision-warp.ts +27 -16
  474. package/src/mcp/tools/refactor-difficulty.ts +42 -0
  475. package/src/mcp/tools/safe-read.ts +2 -2
  476. package/src/mcp/tools/structural-blame.ts +55 -0
  477. package/src/mcp/tools/structural-churn.ts +28 -0
  478. package/src/mcp/tools/structural-log.ts +41 -0
  479. package/src/mcp/tools/structural-review.ts +63 -0
  480. package/src/mcp/warp-pool.ts +6 -6
  481. package/src/mcp/workspace-router-model.ts +2 -2
  482. package/src/mcp/workspace-router-runtime.ts +11 -8
  483. package/src/mcp/workspace-router.ts +9 -2
  484. package/src/operations/adaptive-projection.ts +54 -0
  485. package/src/operations/agent-handoff.ts +59 -0
  486. package/src/operations/capture-range.ts +64 -0
  487. package/src/operations/conversation-primer.ts +82 -0
  488. package/src/operations/cross-session-resume.ts +104 -0
  489. package/src/operations/deterministic-replay.ts +143 -0
  490. package/src/operations/export-surface-diff.ts +355 -0
  491. package/src/operations/footprint-parallelism.ts +98 -0
  492. package/src/operations/graft-diff.ts +14 -7
  493. package/src/operations/horizon-of-readability.ts +60 -0
  494. package/src/operations/import-reference-impact.ts +143 -0
  495. package/src/operations/knowledge-map.ts +96 -0
  496. package/src/operations/observation-cache.ts +20 -4
  497. package/src/operations/projection-safety.ts +111 -0
  498. package/src/operations/repo-workspace.ts +3 -3
  499. package/src/operations/semantic-drift.ts +70 -0
  500. package/src/operations/session-filtration.ts +76 -0
  501. package/src/operations/session-replay.ts +73 -0
  502. package/src/operations/sludge-detector.ts +356 -0
  503. package/src/operations/structural-blame.ts +197 -0
  504. package/src/operations/structural-review.ts +317 -0
  505. package/src/operations/teaching-hints.ts +52 -0
  506. package/src/parser/diff.ts +7 -0
  507. package/src/ports/paths.ts +22 -0
  508. package/src/warp/ast-emitter.ts +133 -0
  509. package/src/warp/ast-import-resolver.ts +333 -0
  510. package/src/warp/commit-meta.ts +19 -0
  511. package/src/warp/context.ts +48 -0
  512. package/src/warp/dead-symbols.ts +174 -0
  513. package/src/warp/drift-sentinel.ts +101 -0
  514. package/src/warp/index-head.ts +348 -0
  515. package/src/warp/observers.ts +8 -7
  516. package/src/warp/open.ts +40 -66
  517. package/src/warp/outline-diff-trailer.ts +100 -0
  518. package/src/warp/plumbing.d.ts +11 -2
  519. package/src/warp/refactor-difficulty.ts +257 -0
  520. package/src/warp/references.ts +77 -0
  521. package/src/warp/stale-docs.ts +210 -0
  522. package/src/warp/structural-drift-detection.ts +97 -0
  523. package/src/warp/structural-queries.ts +258 -0
  524. package/src/warp/symbol-timeline.ts +209 -0
  525. package/src/warp/traverse-hydrate.ts +61 -0
  526. package/src/warp/warp-reference-count.ts +47 -0
  527. package/src/warp/warp-structural-blame.ts +65 -0
  528. package/src/warp/warp-structural-churn.ts +309 -0
  529. package/src/warp/warp-structural-log.ts +149 -0
  530. package/dist/ports/warp.d.ts +0 -45
  531. package/dist/ports/warp.d.ts.map +0 -1
  532. package/dist/ports/warp.js.map +0 -1
  533. package/dist/warp/identity-resolver.d.ts +0 -14
  534. package/dist/warp/identity-resolver.d.ts.map +0 -1
  535. package/dist/warp/identity-resolver.js +0 -31
  536. package/dist/warp/identity-resolver.js.map +0 -1
  537. package/dist/warp/indexer-git.d.ts +0 -22
  538. package/dist/warp/indexer-git.d.ts.map +0 -1
  539. package/dist/warp/indexer-git.js +0 -148
  540. package/dist/warp/indexer-git.js.map +0 -1
  541. package/dist/warp/indexer-graph.d.ts +0 -17
  542. package/dist/warp/indexer-graph.d.ts.map +0 -1
  543. package/dist/warp/indexer-graph.js +0 -179
  544. package/dist/warp/indexer-graph.js.map +0 -1
  545. package/dist/warp/indexer-model.d.ts +0 -51
  546. package/dist/warp/indexer-model.d.ts.map +0 -1
  547. package/dist/warp/indexer-model.js +0 -21
  548. package/dist/warp/indexer-model.js.map +0 -1
  549. package/dist/warp/indexer.d.ts +0 -9
  550. package/dist/warp/indexer.d.ts.map +0 -1
  551. package/dist/warp/indexer.js +0 -112
  552. package/dist/warp/indexer.js.map +0 -1
  553. package/dist/warp/symbol-identity.d.ts +0 -11
  554. package/dist/warp/symbol-identity.d.ts.map +0 -1
  555. package/dist/warp/symbol-identity.js +0 -67
  556. package/dist/warp/symbol-identity.js.map +0 -1
  557. package/src/ports/warp.ts +0 -55
  558. package/src/warp/identity-resolver.ts +0 -35
  559. package/src/warp/indexer-git.ts +0 -182
  560. package/src/warp/indexer-graph.ts +0 -276
  561. package/src/warp/indexer-model.ts +0 -69
  562. package/src/warp/indexer.ts +0 -186
  563. package/src/warp/symbol-identity.ts +0 -100
@@ -1,6 +1,6 @@
1
1
  import type { FileSystem } from "../ports/filesystem.js";
2
2
  import type { GitClient } from "../ports/git.js";
3
- import { getChangedFiles, getFileAtRef } from "../git/diff.js";
3
+ import { getChangedFilesWithStatus, getFileAtRef } from "../git/diff.js";
4
4
  import { detectLang } from "../parser/lang.js";
5
5
  import { extractOutline } from "../parser/outline.js";
6
6
  import { diffOutlines, OutlineDiff } from "../parser/diff.js";
@@ -82,7 +82,7 @@ export async function graftDiff(opts: GraftDiffOptions): Promise<GraftDiffResult
82
82
  const headLabel = opts.head ?? "working tree";
83
83
  const cwd = opts.cwd;
84
84
 
85
- let changedFiles = await getChangedFiles({
85
+ let changedEntries = await getChangedFilesWithStatus({
86
86
  cwd,
87
87
  git: opts.git,
88
88
  base,
@@ -91,15 +91,19 @@ export async function graftDiff(opts: GraftDiffOptions): Promise<GraftDiffResult
91
91
 
92
92
  // Filter by path if provided
93
93
  if (opts.path !== undefined) {
94
- changedFiles = changedFiles.filter((f) => f === opts.path);
94
+ changedEntries = changedEntries.filter((f) => f.path === opts.path);
95
95
  }
96
96
 
97
97
  const files: FileDiff[] = [];
98
98
  const refused: GraftDiffRefusal[] = [];
99
99
 
100
- for (const filePath of changedFiles) {
100
+ for (const entry of changedEntries) {
101
+ const filePath = entry.path;
102
+ // For renames, read base content from the old path
103
+ const basePath = entry.oldPath ?? filePath;
104
+
101
105
  // Get content at base (null = file absent at ref)
102
- const baseContent = await getFileAtRef(base, filePath, { cwd, git: opts.git });
106
+ const baseContent = await getFileAtRef(base, basePath, { cwd, git: opts.git });
103
107
 
104
108
  // Get content at head (null = file absent at ref/worktree)
105
109
  let headContent: string | null;
@@ -113,9 +117,12 @@ export async function graftDiff(opts: GraftDiffOptions): Promise<GraftDiffResult
113
117
  }
114
118
  }
115
119
 
116
- // Determine status
120
+ // Determine status — trust git's rename detection for renames,
121
+ // fall back to content-based detection for other statuses
117
122
  let status: "modified" | "added" | "deleted";
118
- if (baseContent === null && headContent === null) {
123
+ if (entry.status === "renamed") {
124
+ status = "modified";
125
+ } else if (baseContent === null && headContent === null) {
119
126
  // Both absent — file listed in diff but unreadable at both refs. Skip.
120
127
  continue;
121
128
  } else if (baseContent === null) {
@@ -0,0 +1,60 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Horizon of Readability — detect when projection can't reduce further
3
+ // ---------------------------------------------------------------------------
4
+
5
+ /** Input metrics for horizon detection. */
6
+ export interface HorizonInput {
7
+ readonly contentLines: number;
8
+ readonly contentBytes: number;
9
+ readonly outlineLines: number;
10
+ readonly outlineBytes: number;
11
+ }
12
+
13
+ /** Result of horizon detection. */
14
+ export interface HorizonResult {
15
+ readonly horizonReached: boolean;
16
+ readonly compressionRatio: number;
17
+ readonly recommendation: "outline" | "content";
18
+ readonly message?: string;
19
+ }
20
+
21
+ /** Compression ratio threshold above which outline provides negligible savings. */
22
+ const HORIZON_THRESHOLD = 0.7;
23
+
24
+ /**
25
+ * Detect whether the readability horizon has been reached.
26
+ *
27
+ * When the outline is nearly as large as the content (compression
28
+ * ratio > threshold), the file is irreducibly complex for the
29
+ * outline representation. Return full content instead.
30
+ */
31
+ export function detectReadabilityHorizon(input: HorizonInput): HorizonResult {
32
+ const { contentBytes, outlineBytes } = input;
33
+
34
+ // Zero-size files are at the horizon (nothing to simplify)
35
+ if (contentBytes === 0) {
36
+ return {
37
+ horizonReached: true,
38
+ compressionRatio: 1,
39
+ recommendation: "content",
40
+ message: "Empty file — full content provided.",
41
+ };
42
+ }
43
+
44
+ const compressionRatio = outlineBytes / contentBytes;
45
+
46
+ if (compressionRatio >= HORIZON_THRESHOLD) {
47
+ return {
48
+ horizonReached: true,
49
+ compressionRatio,
50
+ recommendation: "content",
51
+ message: `Outline is ${String(Math.round(compressionRatio * 100))}% of content — cannot simplify further. Full content provided.`,
52
+ };
53
+ }
54
+
55
+ return {
56
+ horizonReached: false,
57
+ compressionRatio,
58
+ recommendation: "outline",
59
+ };
60
+ }
@@ -0,0 +1,143 @@
1
+ import { getFileAtRef } from "../git/diff.js";
2
+ import { detectLang } from "../parser/lang.js";
3
+ import type { GitClient } from "../ports/git.js";
4
+ import type { PathOps } from "../ports/paths.js";
5
+ import type { ReferenceCountResult } from "./structural-review.js";
6
+
7
+ const NAMED_IMPORT_OR_REEXPORT =
8
+ /\b(?:import|export)\s+(?:type\s+)?\{([\s\S]*?)\}\s+from\s+["']([^"']+)["']/g;
9
+
10
+ export interface ImportReferenceImpactOptions {
11
+ readonly cwd: string;
12
+ readonly git: GitClient;
13
+ readonly pathOps: PathOps;
14
+ readonly symbolName: string;
15
+ readonly filePath: string;
16
+ readonly ref: string;
17
+ }
18
+
19
+ async function listFilesAtRef(opts: Pick<ImportReferenceImpactOptions, "cwd" | "git" | "ref">): Promise<readonly string[]> {
20
+ const result = await opts.git.run({
21
+ args: ["ls-tree", "-r", "--name-only", opts.ref],
22
+ cwd: opts.cwd,
23
+ });
24
+ if (result.error !== undefined || result.status !== 0) {
25
+ throw result.error ?? new Error(result.stderr.trim() || `git ls-tree failed with status ${String(result.status)}`);
26
+ }
27
+ const output = result.stdout.trim();
28
+ return output.length === 0 ? [] : output.split("\n");
29
+ }
30
+
31
+ function compiledSpecifierSourceCandidates(resolvedImportSource: string): readonly string[] {
32
+ const compiledToSourceExtensions: readonly [string, readonly string[]][] = [
33
+ [".js", [".ts", ".tsx"]],
34
+ [".jsx", [".tsx", ".ts"]],
35
+ [".mjs", [".mts", ".ts"]],
36
+ [".cjs", [".cts", ".ts"]],
37
+ ];
38
+
39
+ for (const [compiledExtension, sourceExtensions] of compiledToSourceExtensions) {
40
+ if (!resolvedImportSource.endsWith(compiledExtension)) continue;
41
+
42
+ const base = resolvedImportSource.slice(0, -compiledExtension.length);
43
+ return sourceExtensions.map((sourceExtension) => `${base}${sourceExtension}`);
44
+ }
45
+
46
+ return [];
47
+ }
48
+
49
+ function resolveModulePath(
50
+ importSource: string,
51
+ importingFilePath: string,
52
+ knownFiles: ReadonlySet<string>,
53
+ pathOps: PathOps,
54
+ ): string | null {
55
+ if (!importSource.startsWith(".") && !importSource.startsWith("/")) {
56
+ return null;
57
+ }
58
+
59
+ const dir = importingFilePath.includes("/")
60
+ ? importingFilePath.slice(0, importingFilePath.lastIndexOf("/"))
61
+ : "";
62
+ const raw = dir.length > 0
63
+ ? pathOps.join(dir, importSource)
64
+ : pathOps.normalize(importSource);
65
+ const sourceExtensionCandidates = compiledSpecifierSourceCandidates(raw);
66
+ const candidates = [
67
+ raw,
68
+ ...sourceExtensionCandidates,
69
+ `${raw}.ts`,
70
+ `${raw}.tsx`,
71
+ `${raw}.js`,
72
+ `${raw}.jsx`,
73
+ `${raw}/index.ts`,
74
+ `${raw}/index.tsx`,
75
+ `${raw}/index.js`,
76
+ ];
77
+
78
+ return candidates.find((candidate) => knownFiles.has(candidate)) ?? null;
79
+ }
80
+
81
+ function importsSymbol(namedImports: string, symbolName: string): boolean {
82
+ return namedImports
83
+ .split(",")
84
+ .map((part) => part.trim().replace(/^type\s+/, ""))
85
+ .some((part) => {
86
+ const importedName = part.split(/\s+as\s+/u)[0]?.trim();
87
+ return importedName === symbolName;
88
+ });
89
+ }
90
+
91
+ function fileImportsSymbolFromTarget(
92
+ content: string,
93
+ importingFilePath: string,
94
+ symbolName: string,
95
+ targetFilePath: string,
96
+ knownFiles: ReadonlySet<string>,
97
+ pathOps: PathOps,
98
+ ): boolean {
99
+ NAMED_IMPORT_OR_REEXPORT.lastIndex = 0;
100
+ let match: RegExpExecArray | null;
101
+ while ((match = NAMED_IMPORT_OR_REEXPORT.exec(content)) !== null) {
102
+ const namedImports = match[1];
103
+ const importSource = match[2];
104
+ if (namedImports === undefined || importSource === undefined) continue;
105
+ if (!importsSymbol(namedImports, symbolName)) continue;
106
+ if (resolveModulePath(importSource, importingFilePath, knownFiles, pathOps) === targetFilePath) {
107
+ return true;
108
+ }
109
+ }
110
+ return false;
111
+ }
112
+
113
+ export async function countNamedImportReferencesAtRef(
114
+ opts: ImportReferenceImpactOptions,
115
+ ): Promise<ReferenceCountResult> {
116
+ const files = await listFilesAtRef(opts);
117
+ const knownFiles = new Set(files);
118
+ const referencingFiles: string[] = [];
119
+
120
+ for (const candidate of files) {
121
+ if (candidate === opts.filePath || detectLang(candidate) === null) continue;
122
+ const content = await getFileAtRef(opts.ref, candidate, {
123
+ cwd: opts.cwd,
124
+ git: opts.git,
125
+ });
126
+ if (content === null) continue;
127
+ if (fileImportsSymbolFromTarget(
128
+ content,
129
+ candidate,
130
+ opts.symbolName,
131
+ opts.filePath,
132
+ knownFiles,
133
+ opts.pathOps,
134
+ )) {
135
+ referencingFiles.push(candidate);
136
+ }
137
+ }
138
+
139
+ return {
140
+ referenceCount: referencingFiles.length,
141
+ referencingFiles,
142
+ };
143
+ }
@@ -0,0 +1,96 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Knowledge map — "what do I already know?" query over the observation cache
3
+ // ---------------------------------------------------------------------------
4
+
5
+ import type { ObservationCache } from "./observation-cache.js";
6
+ import { hashContent } from "./observation-cache.js";
7
+ import type { FileSystem } from "../ports/filesystem.js";
8
+
9
+ export interface KnowledgeFileEntry {
10
+ readonly path: string;
11
+ readonly symbols: readonly string[];
12
+ readonly readCount: number;
13
+ readonly lastReadAt: string;
14
+ readonly stale: boolean;
15
+ }
16
+
17
+ export interface KnowledgeMapResult {
18
+ readonly totalFiles: number;
19
+ readonly totalSymbols: number;
20
+ readonly files: readonly KnowledgeFileEntry[];
21
+ readonly staleFiles: readonly string[];
22
+ readonly directoryCoverage: Readonly<Record<string, number>>;
23
+ }
24
+
25
+ export interface KnowledgeMapOptions {
26
+ readonly cache: ObservationCache;
27
+ readonly fs: FileSystem;
28
+ readonly projectRoot: string;
29
+ }
30
+
31
+ /**
32
+ * Build a knowledge map from the current observation cache.
33
+ *
34
+ * For each observed file, checks whether the file has changed on disk
35
+ * since it was last read (staleness). Aggregates symbols and directory
36
+ * coverage.
37
+ */
38
+ export async function buildKnowledgeMap(options: KnowledgeMapOptions): Promise<KnowledgeMapResult> {
39
+ const { cache, fs: fileSystem, projectRoot } = options;
40
+ const files: KnowledgeFileEntry[] = [];
41
+ const staleFiles: string[] = [];
42
+ const dirCounts = new Map<string, number>();
43
+ let totalSymbols = 0;
44
+
45
+ for (const [filePath, observation] of cache.allEntries()) {
46
+ const symbols = observation.outline.map((entry) => entry.name);
47
+ totalSymbols += symbols.length;
48
+
49
+ // Check staleness by reading current content and comparing hash
50
+ let stale: boolean;
51
+ try {
52
+ const currentContent = await fileSystem.readFile(filePath, "utf-8");
53
+ stale = observation.isStale(hashContent(currentContent));
54
+ } catch {
55
+ // File no longer exists — mark as stale
56
+ stale = true;
57
+ }
58
+
59
+ if (stale) {
60
+ staleFiles.push(filePath);
61
+ }
62
+
63
+ files.push({
64
+ path: filePath,
65
+ symbols,
66
+ readCount: observation.readCount,
67
+ lastReadAt: observation.lastReadAt,
68
+ stale,
69
+ });
70
+
71
+ // Directory coverage — use relative path from project root
72
+ const relative = filePath.startsWith(projectRoot + "/")
73
+ ? filePath.slice(projectRoot.length + 1)
74
+ : filePath;
75
+ const lastSlash = relative.lastIndexOf("/");
76
+ const dir = lastSlash === -1 ? "." : relative.slice(0, lastSlash);
77
+ dirCounts.set(dir, (dirCounts.get(dir) ?? 0) + 1);
78
+ }
79
+
80
+ // Sort files by path for deterministic output
81
+ files.sort((a, b) => a.path.localeCompare(b.path));
82
+ staleFiles.sort();
83
+
84
+ const directoryCoverage: Record<string, number> = {};
85
+ for (const [dir, count] of [...dirCounts.entries()].sort(([a], [b]) => a.localeCompare(b))) {
86
+ directoryCoverage[dir] = count;
87
+ }
88
+
89
+ return {
90
+ totalFiles: files.length,
91
+ totalSymbols,
92
+ files,
93
+ staleFiles,
94
+ directoryCoverage,
95
+ };
96
+ }
@@ -62,9 +62,9 @@ export class Observation {
62
62
  return this.contentHash !== currentContentHash;
63
63
  }
64
64
 
65
- touch(): void {
65
+ touch(now: string): void {
66
66
  this._readCount++;
67
- this._lastReadAt = new Date().toISOString();
67
+ this._lastReadAt = now;
68
68
  }
69
69
 
70
70
  snapshot(): ObservationSnapshot {
@@ -90,11 +90,17 @@ export type CacheResult =
90
90
 
91
91
  export class ObservationCache {
92
92
  private readonly entries = new Map<string, Observation>();
93
+ readonly now: () => string;
94
+
95
+ constructor(now?: () => string) {
96
+ this.now = now ?? (() => new Date().toISOString());
97
+ }
93
98
 
94
99
  static fromSnapshots(
95
100
  snapshots: Readonly<Record<string, ObservationSnapshot>> | undefined,
101
+ now?: () => string,
96
102
  ): ObservationCache {
97
- const cache = new ObservationCache();
103
+ const cache = new ObservationCache(now);
98
104
  if (snapshots === undefined) {
99
105
  return cache;
100
106
  }
@@ -112,7 +118,7 @@ export class ObservationCache {
112
118
  actual: Readonly<{ lines: number; bytes: number }>,
113
119
  ): void {
114
120
  const existing = this.entries.get(filePath);
115
- const now = new Date().toISOString();
121
+ const now = this.now();
116
122
  this.entries.set(filePath, new Observation({
117
123
  contentHash,
118
124
  outline,
@@ -146,4 +152,14 @@ export class ObservationCache {
146
152
  }
147
153
  this.entries.set(filePath, Observation.fromSnapshot(snapshot));
148
154
  }
155
+
156
+ /** Iterate all observed file paths and their observations. */
157
+ allEntries(): IterableIterator<[string, Observation]> {
158
+ return this.entries.entries();
159
+ }
160
+
161
+ /** Number of observed files. */
162
+ get size(): number {
163
+ return this.entries.size;
164
+ }
149
165
  }
@@ -0,0 +1,111 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Projection Safety Classes — what can each projection level answer?
3
+ // ---------------------------------------------------------------------------
4
+
5
+ /** Question classes ordered by information requirement. */
6
+ export type QuestionClass = "existence" | "structure" | "signature" | "behavior";
7
+
8
+ /** Projection levels ordered by detail. */
9
+ export type ProjectionLevel = "outline" | "signature" | "content";
10
+
11
+ /** Warning when a projection is insufficient for the inferred question. */
12
+ export interface SafetyWarning {
13
+ readonly questionClass: QuestionClass;
14
+ readonly currentProjection: ProjectionLevel;
15
+ readonly requiredProjection: ProjectionLevel;
16
+ readonly message: string;
17
+ }
18
+
19
+ /** Metadata about what a projection level can answer. */
20
+ export interface SafetyClassMetadata {
21
+ readonly projection: ProjectionLevel;
22
+ readonly answerable: readonly QuestionClass[];
23
+ }
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Question class taxonomy
27
+ // ---------------------------------------------------------------------------
28
+
29
+ /** What each projection level can safely answer. */
30
+ const ANSWERABLE: Readonly<Record<ProjectionLevel, readonly QuestionClass[]>> = {
31
+ outline: ["existence", "structure"],
32
+ signature: ["existence", "structure", "signature"],
33
+ content: ["existence", "structure", "signature", "behavior"],
34
+ };
35
+
36
+ /** Minimum projection needed for each question class. */
37
+ const MINIMUM_PROJECTION: Readonly<Record<QuestionClass, ProjectionLevel>> = {
38
+ existence: "outline",
39
+ structure: "outline",
40
+ signature: "signature",
41
+ behavior: "content",
42
+ };
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Tool → question class mapping
46
+ // ---------------------------------------------------------------------------
47
+
48
+ const TOOL_QUESTION_CLASS: Readonly<Record<string, QuestionClass>> = {
49
+ code_find: "existence",
50
+ graft_map: "structure",
51
+ file_outline: "structure",
52
+ code_refs: "existence",
53
+ code_show: "behavior",
54
+ safe_read: "behavior",
55
+ read_range: "behavior",
56
+ graft_diff: "structure",
57
+ graft_review: "structure",
58
+ changed_since: "structure",
59
+ };
60
+
61
+ /**
62
+ * Infer the question class from a tool name.
63
+ * Defaults to "behavior" for unknown tools (conservative).
64
+ */
65
+ export function getQuestionClass(toolName: string): QuestionClass {
66
+ return TOOL_QUESTION_CLASS[toolName] ?? "behavior";
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Safety check
71
+ // ---------------------------------------------------------------------------
72
+
73
+ /** Projection detail ordering for comparison. */
74
+ const LEVEL_ORDER: Readonly<Record<ProjectionLevel, number>> = {
75
+ outline: 0,
76
+ signature: 1,
77
+ content: 2,
78
+ };
79
+
80
+ /**
81
+ * Check whether a projection is sufficient for the question implied
82
+ * by a tool call. Returns null if safe, or a warning if insufficient.
83
+ */
84
+ export function checkProjectionSafety(
85
+ toolName: string,
86
+ currentProjection: ProjectionLevel,
87
+ ): SafetyWarning | null {
88
+ const questionClass = getQuestionClass(toolName);
89
+ const required = MINIMUM_PROJECTION[questionClass];
90
+
91
+ if (LEVEL_ORDER[currentProjection] >= LEVEL_ORDER[required]) {
92
+ return null;
93
+ }
94
+
95
+ return {
96
+ questionClass,
97
+ currentProjection,
98
+ requiredProjection: required,
99
+ message: `${toolName} asks a "${questionClass}" question which requires "${required}" projection, but current projection is "${currentProjection}".`,
100
+ };
101
+ }
102
+
103
+ /**
104
+ * Get metadata about what question classes a projection level can answer.
105
+ */
106
+ export function getSafetyClassMetadata(projection: ProjectionLevel): SafetyClassMetadata {
107
+ return {
108
+ projection,
109
+ answerable: ANSWERABLE[projection],
110
+ };
111
+ }
@@ -163,7 +163,7 @@ export class RepoWorkspace {
163
163
  if (snapshot?.supportsOutline === true) {
164
164
  const cacheResult = this.cache.check(filePath, snapshot.rawContent);
165
165
  if (cacheResult.hit) {
166
- cacheResult.obs.touch();
166
+ cacheResult.obs.touch(this.cache.now());
167
167
  return {
168
168
  path: filePath,
169
169
  projection: "cache_hit",
@@ -193,7 +193,7 @@ export class RepoWorkspace {
193
193
  jumpTable: snapshot.jumpTable,
194
194
  actual: snapshot.actual,
195
195
  readCount: cacheResult.stale.readCount + 1,
196
- lastReadAt: updated?.lastReadAt ?? new Date().toISOString(),
196
+ lastReadAt: updated?.lastReadAt ?? this.cache.now(),
197
197
  };
198
198
  }
199
199
  }
@@ -243,7 +243,7 @@ export class RepoWorkspace {
243
243
  }
244
244
  const cacheResult = this.cache.check(filePath, rawContent);
245
245
  if (cacheResult.hit) {
246
- cacheResult.obs.touch();
246
+ cacheResult.obs.touch(this.cache.now());
247
247
  return {
248
248
  path: filePath,
249
249
  outline: [...cacheResult.obs.outline],
@@ -0,0 +1,70 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Semantic Drift Detection — flag interpretation shift during reading
3
+ // ---------------------------------------------------------------------------
4
+
5
+ /** A single entry in the reading path. */
6
+ export interface ReadingPathEntry {
7
+ readonly filePath: string;
8
+ readonly timestamp: string;
9
+ }
10
+
11
+ /** Warning about potential interpretation shift. */
12
+ export interface DriftWarning {
13
+ readonly filePath: string;
14
+ readonly firstReadIndex: number;
15
+ readonly reReadIndex: number;
16
+ readonly intervening: readonly string[];
17
+ readonly message: string;
18
+ }
19
+
20
+ /**
21
+ * Detect semantic drift in a reading path.
22
+ *
23
+ * When an agent re-reads a file after reading structurally related
24
+ * files (files with cross-file reference edges), the re-read may
25
+ * yield different understanding because the agent's context has
26
+ * shifted. This is holonomy: translating through a chain of
27
+ * observers and returning does not return to the same understanding.
28
+ *
29
+ * @param readingPath - Ordered sequence of file reads in the session
30
+ * @param relatedFiles - Map of file → related files (from reference edges)
31
+ */
32
+ export function detectSemanticDrift(
33
+ readingPath: readonly ReadingPathEntry[],
34
+ relatedFiles: ReadonlyMap<string, readonly string[]>,
35
+ ): DriftWarning[] {
36
+ const warnings: DriftWarning[] = [];
37
+ const lastReadIndex = new Map<string, number>();
38
+
39
+ for (let i = 0; i < readingPath.length; i++) {
40
+ const entry = readingPath[i] ?? { filePath: "", timestamp: "" };
41
+ const prevIndex = lastReadIndex.get(entry.filePath);
42
+
43
+ if (prevIndex !== undefined) {
44
+ // This is a re-read — check if related files were read in between
45
+ const related = relatedFiles.get(entry.filePath) ?? [];
46
+ const intervening: string[] = [];
47
+
48
+ for (let j = prevIndex + 1; j < i; j++) {
49
+ const interveningFile = (readingPath[j] ?? { filePath: "" }).filePath;
50
+ if (related.includes(interveningFile)) {
51
+ intervening.push(interveningFile);
52
+ }
53
+ }
54
+
55
+ if (intervening.length > 0) {
56
+ warnings.push({
57
+ filePath: entry.filePath,
58
+ firstReadIndex: prevIndex,
59
+ reReadIndex: i,
60
+ intervening,
61
+ message: `Re-reading ${entry.filePath} after reading related files (${intervening.join(", ")}) may shift interpretation.`,
62
+ });
63
+ }
64
+ }
65
+
66
+ lastReadIndex.set(entry.filePath, i);
67
+ }
68
+
69
+ return warnings;
70
+ }
@@ -0,0 +1,76 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Session Filtration — accumulation-aware projection escalation
3
+ // ---------------------------------------------------------------------------
4
+
5
+ /** Input for filtration level computation. */
6
+ export interface FiltrationInput {
7
+ readonly observedFiles: number;
8
+ readonly totalFiles: number;
9
+ readonly reReadCount: number;
10
+ }
11
+
12
+ /** Input for escalation decision. */
13
+ export interface EscalationInput {
14
+ readonly filtrationLevel: number;
15
+ readonly previousProjection: "outline" | "content";
16
+ readonly readCount: number;
17
+ }
18
+
19
+ /** Result of escalation check. */
20
+ export interface EscalationResult {
21
+ readonly escalate: boolean;
22
+ readonly suggestedProjection: "outline" | "content";
23
+ readonly reason?: string;
24
+ }
25
+
26
+ const ESCALATION_THRESHOLD = 0.5;
27
+
28
+ /**
29
+ * Compute the filtration level for the current session.
30
+ *
31
+ * Ranges from 0 (fresh session) to 1 (fully saturated). Based on
32
+ * the fraction of files observed and the number of re-reads.
33
+ * As filtration grows, graft escalates detail — an agent that has
34
+ * already explored the codebase should get richer projections.
35
+ */
36
+ export function computeFiltrationLevel(input: FiltrationInput): number {
37
+ const { observedFiles, totalFiles, reReadCount } = input;
38
+ if (totalFiles === 0) return 0;
39
+
40
+ const coverageFraction = observedFiles / totalFiles;
41
+ const reReadBoost = Math.min(reReadCount / 20, 0.3);
42
+
43
+ return Math.min(coverageFraction + reReadBoost, 1);
44
+ }
45
+
46
+ /**
47
+ * Determine whether to escalate projection detail for a re-read.
48
+ *
49
+ * Monotone accumulation: as filtration grows, graft escalates.
50
+ * An agent that outlined a directory and is now re-reading a file
51
+ * in that directory should get content instead of another outline.
52
+ */
53
+ export function shouldEscalateDetail(input: EscalationInput): EscalationResult {
54
+ const { filtrationLevel, previousProjection, readCount } = input;
55
+
56
+ // Never escalate on first read
57
+ if (readCount <= 1) {
58
+ return { escalate: false, suggestedProjection: previousProjection };
59
+ }
60
+
61
+ // Already at content — nothing to escalate to
62
+ if (previousProjection === "content") {
63
+ return { escalate: false, suggestedProjection: "content" };
64
+ }
65
+
66
+ // Escalate when filtration is high enough and this is a re-read
67
+ if (filtrationLevel >= ESCALATION_THRESHOLD) {
68
+ return {
69
+ escalate: true,
70
+ suggestedProjection: "content",
71
+ reason: `Filtration level ${String(Math.round(filtrationLevel * 100))}% — escalating from outline to content for re-read.`,
72
+ };
73
+ }
74
+
75
+ return { escalate: false, suggestedProjection: previousProjection };
76
+ }