opentasks 0.0.6 → 0.0.7

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 (660) hide show
  1. package/README.md +68 -0
  2. package/dist/__tests__/cli-tools.test.d.ts +8 -0
  3. package/dist/__tests__/cli-tools.test.d.ts.map +1 -0
  4. package/dist/__tests__/cli-tools.test.js +546 -0
  5. package/dist/__tests__/cli-tools.test.js.map +1 -0
  6. package/dist/__tests__/cli.test.d.ts +5 -0
  7. package/dist/__tests__/cli.test.d.ts.map +1 -0
  8. package/dist/__tests__/cli.test.js +77 -0
  9. package/dist/__tests__/cli.test.js.map +1 -0
  10. package/dist/__tests__/p1-p3-gaps.test.d.ts +2 -0
  11. package/dist/__tests__/p1-p3-gaps.test.d.ts.map +1 -0
  12. package/dist/__tests__/p1-p3-gaps.test.js +463 -0
  13. package/dist/__tests__/p1-p3-gaps.test.js.map +1 -0
  14. package/dist/cli.d.ts +1 -0
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/cli.js +64 -0
  17. package/dist/cli.js.map +1 -1
  18. package/dist/client/__tests__/client-crud.test.d.ts +7 -0
  19. package/dist/client/__tests__/client-crud.test.d.ts.map +1 -0
  20. package/dist/client/__tests__/client-crud.test.js +404 -0
  21. package/dist/client/__tests__/client-crud.test.js.map +1 -0
  22. package/dist/client/__tests__/client.test.d.ts +5 -0
  23. package/dist/client/__tests__/client.test.d.ts.map +1 -0
  24. package/dist/client/__tests__/client.test.js +518 -0
  25. package/dist/client/__tests__/client.test.js.map +1 -0
  26. package/dist/client/client.d.ts +47 -1
  27. package/dist/client/client.d.ts.map +1 -1
  28. package/dist/client/client.js +71 -0
  29. package/dist/client/client.js.map +1 -1
  30. package/dist/client/index.d.ts +1 -1
  31. package/dist/client/index.d.ts.map +1 -1
  32. package/dist/config/__tests__/defaults.test.d.ts +2 -0
  33. package/dist/config/__tests__/defaults.test.d.ts.map +1 -0
  34. package/dist/config/__tests__/defaults.test.js +57 -0
  35. package/dist/config/__tests__/defaults.test.js.map +1 -0
  36. package/dist/config/__tests__/env.test.d.ts +2 -0
  37. package/dist/config/__tests__/env.test.d.ts.map +1 -0
  38. package/dist/config/__tests__/env.test.js +136 -0
  39. package/dist/config/__tests__/env.test.js.map +1 -0
  40. package/dist/config/__tests__/index.test.d.ts +2 -0
  41. package/dist/config/__tests__/index.test.d.ts.map +1 -0
  42. package/dist/config/__tests__/index.test.js +113 -0
  43. package/dist/config/__tests__/index.test.js.map +1 -0
  44. package/dist/config/__tests__/loader.test.d.ts +2 -0
  45. package/dist/config/__tests__/loader.test.d.ts.map +1 -0
  46. package/dist/config/__tests__/loader.test.js +128 -0
  47. package/dist/config/__tests__/loader.test.js.map +1 -0
  48. package/dist/config/__tests__/merge.test.d.ts +2 -0
  49. package/dist/config/__tests__/merge.test.d.ts.map +1 -0
  50. package/dist/config/__tests__/merge.test.js +79 -0
  51. package/dist/config/__tests__/merge.test.js.map +1 -0
  52. package/dist/config/__tests__/schema.test.d.ts +2 -0
  53. package/dist/config/__tests__/schema.test.d.ts.map +1 -0
  54. package/dist/config/__tests__/schema.test.js +300 -0
  55. package/dist/config/__tests__/schema.test.js.map +1 -0
  56. package/dist/config/schema.d.ts +178 -4
  57. package/dist/config/schema.d.ts.map +1 -1
  58. package/dist/config/schema.js +109 -7
  59. package/dist/config/schema.js.map +1 -1
  60. package/dist/context-files/context-files.d.ts +72 -0
  61. package/dist/context-files/context-files.d.ts.map +1 -0
  62. package/dist/context-files/context-files.js +145 -0
  63. package/dist/context-files/context-files.js.map +1 -0
  64. package/dist/context-files/index.d.ts +16 -0
  65. package/dist/context-files/index.d.ts.map +1 -0
  66. package/dist/context-files/index.js +14 -0
  67. package/dist/context-files/index.js.map +1 -0
  68. package/dist/context-files/resolver.d.ts +43 -0
  69. package/dist/context-files/resolver.d.ts.map +1 -0
  70. package/dist/context-files/resolver.js +127 -0
  71. package/dist/context-files/resolver.js.map +1 -0
  72. package/dist/context-files/types.d.ts +94 -0
  73. package/dist/context-files/types.d.ts.map +1 -0
  74. package/dist/context-files/types.js +10 -0
  75. package/dist/context-files/types.js.map +1 -0
  76. package/dist/context-files/watcher-integration.d.ts +47 -0
  77. package/dist/context-files/watcher-integration.d.ts.map +1 -0
  78. package/dist/context-files/watcher-integration.js +47 -0
  79. package/dist/context-files/watcher-integration.js.map +1 -0
  80. package/dist/core/__tests__/conditional-redirects.test.d.ts +2 -0
  81. package/dist/core/__tests__/conditional-redirects.test.d.ts.map +1 -0
  82. package/dist/core/__tests__/conditional-redirects.test.js +83 -0
  83. package/dist/core/__tests__/conditional-redirects.test.js.map +1 -0
  84. package/dist/core/__tests__/connections.test.d.ts +2 -0
  85. package/dist/core/__tests__/connections.test.d.ts.map +1 -0
  86. package/dist/core/__tests__/connections.test.js +158 -0
  87. package/dist/core/__tests__/connections.test.js.map +1 -0
  88. package/dist/core/__tests__/hash.test.d.ts +2 -0
  89. package/dist/core/__tests__/hash.test.d.ts.map +1 -0
  90. package/dist/core/__tests__/hash.test.js +139 -0
  91. package/dist/core/__tests__/hash.test.js.map +1 -0
  92. package/dist/core/__tests__/id.test.d.ts +2 -0
  93. package/dist/core/__tests__/id.test.d.ts.map +1 -0
  94. package/dist/core/__tests__/id.test.js +142 -0
  95. package/dist/core/__tests__/id.test.js.map +1 -0
  96. package/dist/core/__tests__/location.test.d.ts +2 -0
  97. package/dist/core/__tests__/location.test.d.ts.map +1 -0
  98. package/dist/core/__tests__/location.test.js +77 -0
  99. package/dist/core/__tests__/location.test.js.map +1 -0
  100. package/dist/core/__tests__/merge-driver.test.d.ts +2 -0
  101. package/dist/core/__tests__/merge-driver.test.d.ts.map +1 -0
  102. package/dist/core/__tests__/merge-driver.test.js +218 -0
  103. package/dist/core/__tests__/merge-driver.test.js.map +1 -0
  104. package/dist/core/__tests__/redirects.test.d.ts +2 -0
  105. package/dist/core/__tests__/redirects.test.d.ts.map +1 -0
  106. package/dist/core/__tests__/redirects.test.js +123 -0
  107. package/dist/core/__tests__/redirects.test.js.map +1 -0
  108. package/dist/core/__tests__/resolve-location-target.test.d.ts +8 -0
  109. package/dist/core/__tests__/resolve-location-target.test.d.ts.map +1 -0
  110. package/dist/core/__tests__/resolve-location-target.test.js +303 -0
  111. package/dist/core/__tests__/resolve-location-target.test.js.map +1 -0
  112. package/dist/core/__tests__/uri.test.d.ts +2 -0
  113. package/dist/core/__tests__/uri.test.d.ts.map +1 -0
  114. package/dist/core/__tests__/uri.test.js +159 -0
  115. package/dist/core/__tests__/uri.test.js.map +1 -0
  116. package/dist/core/__tests__/worktree.test.d.ts +2 -0
  117. package/dist/core/__tests__/worktree.test.d.ts.map +1 -0
  118. package/dist/core/__tests__/worktree.test.js +120 -0
  119. package/dist/core/__tests__/worktree.test.js.map +1 -0
  120. package/dist/core/merge-driver.d.ts +5 -0
  121. package/dist/core/merge-driver.d.ts.map +1 -1
  122. package/dist/core/merge-driver.js +35 -0
  123. package/dist/core/merge-driver.js.map +1 -1
  124. package/dist/daemon/__tests__/flush.test.d.ts +5 -0
  125. package/dist/daemon/__tests__/flush.test.d.ts.map +1 -0
  126. package/dist/daemon/__tests__/flush.test.js +213 -0
  127. package/dist/daemon/__tests__/flush.test.js.map +1 -0
  128. package/dist/daemon/__tests__/integration.test.d.ts +7 -0
  129. package/dist/daemon/__tests__/integration.test.d.ts.map +1 -0
  130. package/dist/daemon/__tests__/integration.test.js +276 -0
  131. package/dist/daemon/__tests__/integration.test.js.map +1 -0
  132. package/dist/daemon/__tests__/ipc.test.d.ts +5 -0
  133. package/dist/daemon/__tests__/ipc.test.d.ts.map +1 -0
  134. package/dist/daemon/__tests__/ipc.test.js +314 -0
  135. package/dist/daemon/__tests__/ipc.test.js.map +1 -0
  136. package/dist/daemon/__tests__/lifecycle.test.d.ts +5 -0
  137. package/dist/daemon/__tests__/lifecycle.test.d.ts.map +1 -0
  138. package/dist/daemon/__tests__/lifecycle.test.js +301 -0
  139. package/dist/daemon/__tests__/lifecycle.test.js.map +1 -0
  140. package/dist/daemon/__tests__/lock.test.d.ts +5 -0
  141. package/dist/daemon/__tests__/lock.test.d.ts.map +1 -0
  142. package/dist/daemon/__tests__/lock.test.js +192 -0
  143. package/dist/daemon/__tests__/lock.test.js.map +1 -0
  144. package/dist/daemon/__tests__/methods/graph.test.d.ts +5 -0
  145. package/dist/daemon/__tests__/methods/graph.test.d.ts.map +1 -0
  146. package/dist/daemon/__tests__/methods/graph.test.js +309 -0
  147. package/dist/daemon/__tests__/methods/graph.test.js.map +1 -0
  148. package/dist/daemon/__tests__/methods/provider.test.d.ts +7 -0
  149. package/dist/daemon/__tests__/methods/provider.test.d.ts.map +1 -0
  150. package/dist/daemon/__tests__/methods/provider.test.js +181 -0
  151. package/dist/daemon/__tests__/methods/provider.test.js.map +1 -0
  152. package/dist/daemon/__tests__/methods/tools.test.d.ts +5 -0
  153. package/dist/daemon/__tests__/methods/tools.test.d.ts.map +1 -0
  154. package/dist/daemon/__tests__/methods/tools.test.js +587 -0
  155. package/dist/daemon/__tests__/methods/tools.test.js.map +1 -0
  156. package/dist/daemon/__tests__/multi-location.test.d.ts +8 -0
  157. package/dist/daemon/__tests__/multi-location.test.d.ts.map +1 -0
  158. package/dist/daemon/__tests__/multi-location.test.js +669 -0
  159. package/dist/daemon/__tests__/multi-location.test.js.map +1 -0
  160. package/dist/daemon/__tests__/registry.test.d.ts +5 -0
  161. package/dist/daemon/__tests__/registry.test.d.ts.map +1 -0
  162. package/dist/daemon/__tests__/registry.test.js +208 -0
  163. package/dist/daemon/__tests__/registry.test.js.map +1 -0
  164. package/dist/daemon/__tests__/watcher.test.d.ts +5 -0
  165. package/dist/daemon/__tests__/watcher.test.d.ts.map +1 -0
  166. package/dist/daemon/__tests__/watcher.test.js +234 -0
  167. package/dist/daemon/__tests__/watcher.test.js.map +1 -0
  168. package/dist/daemon/index.d.ts +6 -4
  169. package/dist/daemon/index.d.ts.map +1 -1
  170. package/dist/daemon/index.js +3 -2
  171. package/dist/daemon/index.js.map +1 -1
  172. package/dist/daemon/ipc.d.ts.map +1 -1
  173. package/dist/daemon/ipc.js +8 -1
  174. package/dist/daemon/ipc.js.map +1 -1
  175. package/dist/daemon/lifecycle.d.ts.map +1 -1
  176. package/dist/daemon/lifecycle.js +138 -25
  177. package/dist/daemon/lifecycle.js.map +1 -1
  178. package/dist/daemon/location-state.d.ts +12 -6
  179. package/dist/daemon/location-state.d.ts.map +1 -1
  180. package/dist/daemon/location-state.js +50 -20
  181. package/dist/daemon/location-state.js.map +1 -1
  182. package/dist/daemon/methods/__tests__/graph.test.d.ts +5 -0
  183. package/dist/daemon/methods/__tests__/graph.test.d.ts.map +1 -0
  184. package/dist/daemon/methods/__tests__/graph.test.js +274 -0
  185. package/dist/daemon/methods/__tests__/graph.test.js.map +1 -0
  186. package/dist/daemon/methods/__tests__/provider.test.d.ts +5 -0
  187. package/dist/daemon/methods/__tests__/provider.test.d.ts.map +1 -0
  188. package/dist/daemon/methods/__tests__/provider.test.js +184 -0
  189. package/dist/daemon/methods/__tests__/provider.test.js.map +1 -0
  190. package/dist/daemon/methods/__tests__/tools.test.d.ts +5 -0
  191. package/dist/daemon/methods/__tests__/tools.test.d.ts.map +1 -0
  192. package/dist/daemon/methods/__tests__/tools.test.js +295 -0
  193. package/dist/daemon/methods/__tests__/tools.test.js.map +1 -0
  194. package/dist/daemon/methods/context-files.d.ts +14 -0
  195. package/dist/daemon/methods/context-files.d.ts.map +1 -0
  196. package/dist/daemon/methods/context-files.js +95 -0
  197. package/dist/daemon/methods/context-files.js.map +1 -0
  198. package/dist/daemon/methods/provider.d.ts.map +1 -1
  199. package/dist/daemon/methods/provider.js +15 -0
  200. package/dist/daemon/methods/provider.js.map +1 -1
  201. package/dist/daemon/methods/tools.d.ts +1 -1
  202. package/dist/daemon/methods/tools.d.ts.map +1 -1
  203. package/dist/daemon/methods/tools.js +3 -2
  204. package/dist/daemon/methods/tools.js.map +1 -1
  205. package/dist/daemon/sessionlog-linker.d.ts +71 -0
  206. package/dist/daemon/sessionlog-linker.d.ts.map +1 -0
  207. package/dist/daemon/sessionlog-linker.js +472 -0
  208. package/dist/daemon/sessionlog-linker.js.map +1 -0
  209. package/dist/daemon/sessionlog-watcher.d.ts +79 -0
  210. package/dist/daemon/sessionlog-watcher.d.ts.map +1 -0
  211. package/dist/daemon/sessionlog-watcher.js +289 -0
  212. package/dist/daemon/sessionlog-watcher.js.map +1 -0
  213. package/dist/graph/__tests__/EdgeTypeRegistry.test.d.ts +2 -0
  214. package/dist/graph/__tests__/EdgeTypeRegistry.test.d.ts.map +1 -0
  215. package/dist/graph/__tests__/EdgeTypeRegistry.test.js +212 -0
  216. package/dist/graph/__tests__/EdgeTypeRegistry.test.js.map +1 -0
  217. package/dist/graph/__tests__/FederatedGraph.test.d.ts +2 -0
  218. package/dist/graph/__tests__/FederatedGraph.test.d.ts.map +1 -0
  219. package/dist/graph/__tests__/FederatedGraph.test.js +661 -0
  220. package/dist/graph/__tests__/FederatedGraph.test.js.map +1 -0
  221. package/dist/graph/__tests__/GraphologyAdapter.test.d.ts +2 -0
  222. package/dist/graph/__tests__/GraphologyAdapter.test.d.ts.map +1 -0
  223. package/dist/graph/__tests__/GraphologyAdapter.test.js +326 -0
  224. package/dist/graph/__tests__/GraphologyAdapter.test.js.map +1 -0
  225. package/dist/graph/__tests__/HydratingFederatedGraph.test.d.ts +2 -0
  226. package/dist/graph/__tests__/HydratingFederatedGraph.test.d.ts.map +1 -0
  227. package/dist/graph/__tests__/HydratingFederatedGraph.test.js +587 -0
  228. package/dist/graph/__tests__/HydratingFederatedGraph.test.js.map +1 -0
  229. package/dist/graph/__tests__/debounce.test.d.ts +5 -0
  230. package/dist/graph/__tests__/debounce.test.d.ts.map +1 -0
  231. package/dist/graph/__tests__/debounce.test.js +195 -0
  232. package/dist/graph/__tests__/debounce.test.js.map +1 -0
  233. package/dist/graph/__tests__/edge-cases.test.d.ts +8 -0
  234. package/dist/graph/__tests__/edge-cases.test.d.ts.map +1 -0
  235. package/dist/graph/__tests__/edge-cases.test.js +472 -0
  236. package/dist/graph/__tests__/edge-cases.test.js.map +1 -0
  237. package/dist/graph/__tests__/expansion.test.d.ts +2 -0
  238. package/dist/graph/__tests__/expansion.test.d.ts.map +1 -0
  239. package/dist/graph/__tests__/expansion.test.js +105 -0
  240. package/dist/graph/__tests__/expansion.test.js.map +1 -0
  241. package/dist/graph/__tests__/provider-store.test.d.ts +5 -0
  242. package/dist/graph/__tests__/provider-store.test.d.ts.map +1 -0
  243. package/dist/graph/__tests__/provider-store.test.js +791 -0
  244. package/dist/graph/__tests__/provider-store.test.js.map +1 -0
  245. package/dist/graph/__tests__/query.test.d.ts +5 -0
  246. package/dist/graph/__tests__/query.test.d.ts.map +1 -0
  247. package/dist/graph/__tests__/query.test.js +774 -0
  248. package/dist/graph/__tests__/query.test.js.map +1 -0
  249. package/dist/graph/__tests__/store.test.d.ts +5 -0
  250. package/dist/graph/__tests__/store.test.d.ts.map +1 -0
  251. package/dist/graph/__tests__/store.test.js +489 -0
  252. package/dist/graph/__tests__/store.test.js.map +1 -0
  253. package/dist/graph/__tests__/sync.test.d.ts +5 -0
  254. package/dist/graph/__tests__/sync.test.d.ts.map +1 -0
  255. package/dist/graph/__tests__/sync.test.js +129 -0
  256. package/dist/graph/__tests__/sync.test.js.map +1 -0
  257. package/dist/graph/__tests__/validation.test.d.ts +2 -0
  258. package/dist/graph/__tests__/validation.test.d.ts.map +1 -0
  259. package/dist/graph/__tests__/validation.test.js +521 -0
  260. package/dist/graph/__tests__/validation.test.js.map +1 -0
  261. package/dist/graph/index.d.ts +1 -1
  262. package/dist/graph/index.d.ts.map +1 -1
  263. package/dist/graph/index.js.map +1 -1
  264. package/dist/graph/provider-store.d.ts +78 -4
  265. package/dist/graph/provider-store.d.ts.map +1 -1
  266. package/dist/graph/provider-store.js +579 -55
  267. package/dist/graph/provider-store.js.map +1 -1
  268. package/dist/graph/store.d.ts.map +1 -1
  269. package/dist/graph/store.js +3 -0
  270. package/dist/graph/store.js.map +1 -1
  271. package/dist/graph/types.d.ts +16 -0
  272. package/dist/graph/types.d.ts.map +1 -1
  273. package/dist/graph/types.js.map +1 -1
  274. package/dist/index.d.ts +4 -2
  275. package/dist/index.d.ts.map +1 -1
  276. package/dist/index.js +9 -3
  277. package/dist/index.js.map +1 -1
  278. package/dist/materialization/git-archive-store.js +2 -2
  279. package/dist/materialization/git-archive-store.js.map +1 -1
  280. package/dist/materialization/git-remote-store.js +2 -2
  281. package/dist/materialization/git-remote-store.js.map +1 -1
  282. package/dist/materialization/snapshot.js +4 -4
  283. package/dist/materialization/snapshot.js.map +1 -1
  284. package/dist/mcp/index.d.ts +8 -0
  285. package/dist/mcp/index.d.ts.map +1 -0
  286. package/dist/mcp/index.js +8 -0
  287. package/dist/mcp/index.js.map +1 -0
  288. package/dist/mcp/server.d.ts +39 -0
  289. package/dist/mcp/server.d.ts.map +1 -0
  290. package/dist/mcp/server.js +677 -0
  291. package/dist/mcp/server.js.map +1 -0
  292. package/dist/mcp/stdio.d.ts +14 -0
  293. package/dist/mcp/stdio.d.ts.map +1 -0
  294. package/dist/mcp/stdio.js +19 -0
  295. package/dist/mcp/stdio.js.map +1 -0
  296. package/dist/providers/__tests__/beads.test.d.ts +5 -0
  297. package/dist/providers/__tests__/beads.test.d.ts.map +1 -0
  298. package/dist/providers/__tests__/beads.test.js +591 -0
  299. package/dist/providers/__tests__/beads.test.js.map +1 -0
  300. package/dist/providers/__tests__/claude-tasks.test.d.ts +5 -0
  301. package/dist/providers/__tests__/claude-tasks.test.d.ts.map +1 -0
  302. package/dist/providers/__tests__/claude-tasks.test.js +392 -0
  303. package/dist/providers/__tests__/claude-tasks.test.js.map +1 -0
  304. package/dist/providers/__tests__/from-config.test.d.ts +5 -0
  305. package/dist/providers/__tests__/from-config.test.d.ts.map +1 -0
  306. package/dist/providers/__tests__/from-config.test.js +152 -0
  307. package/dist/providers/__tests__/from-config.test.js.map +1 -0
  308. package/dist/providers/__tests__/materialization.test.d.ts +5 -0
  309. package/dist/providers/__tests__/materialization.test.d.ts.map +1 -0
  310. package/dist/providers/__tests__/materialization.test.js +407 -0
  311. package/dist/providers/__tests__/materialization.test.js.map +1 -0
  312. package/dist/providers/__tests__/native.test.d.ts +5 -0
  313. package/dist/providers/__tests__/native.test.d.ts.map +1 -0
  314. package/dist/providers/__tests__/native.test.js +566 -0
  315. package/dist/providers/__tests__/native.test.js.map +1 -0
  316. package/dist/providers/__tests__/registry.test.d.ts +5 -0
  317. package/dist/providers/__tests__/registry.test.d.ts.map +1 -0
  318. package/dist/providers/__tests__/registry.test.js +183 -0
  319. package/dist/providers/__tests__/registry.test.js.map +1 -0
  320. package/dist/providers/beads.d.ts.map +1 -1
  321. package/dist/providers/beads.js +17 -1
  322. package/dist/providers/beads.js.map +1 -1
  323. package/dist/providers/claude-tasks-fs.d.ts +22 -0
  324. package/dist/providers/claude-tasks-fs.d.ts.map +1 -0
  325. package/dist/providers/claude-tasks-fs.js +158 -0
  326. package/dist/providers/claude-tasks-fs.js.map +1 -0
  327. package/dist/providers/claude-tasks.d.ts +13 -4
  328. package/dist/providers/claude-tasks.d.ts.map +1 -1
  329. package/dist/providers/claude-tasks.js +318 -5
  330. package/dist/providers/claude-tasks.js.map +1 -1
  331. package/dist/providers/from-config.d.ts.map +1 -1
  332. package/dist/providers/from-config.js +35 -17
  333. package/dist/providers/from-config.js.map +1 -1
  334. package/dist/providers/global.d.ts.map +1 -1
  335. package/dist/providers/global.js +5 -0
  336. package/dist/providers/global.js.map +1 -1
  337. package/dist/providers/index.d.ts +6 -4
  338. package/dist/providers/index.d.ts.map +1 -1
  339. package/dist/providers/index.js +4 -3
  340. package/dist/providers/index.js.map +1 -1
  341. package/dist/providers/map.d.ts.map +1 -1
  342. package/dist/providers/map.js +5 -0
  343. package/dist/providers/map.js.map +1 -1
  344. package/dist/providers/materialization.d.ts +23 -5
  345. package/dist/providers/materialization.d.ts.map +1 -1
  346. package/dist/providers/materialization.js +95 -4
  347. package/dist/providers/materialization.js.map +1 -1
  348. package/dist/providers/native.d.ts.map +1 -1
  349. package/dist/providers/native.js +5 -0
  350. package/dist/providers/native.js.map +1 -1
  351. package/dist/providers/sessionlog.d.ts +81 -0
  352. package/dist/providers/sessionlog.d.ts.map +1 -0
  353. package/dist/providers/sessionlog.js +478 -0
  354. package/dist/providers/sessionlog.js.map +1 -0
  355. package/dist/providers/sudocode.d.ts.map +1 -1
  356. package/dist/providers/sudocode.js +17 -1
  357. package/dist/providers/sudocode.js.map +1 -1
  358. package/dist/providers/traits/Reconcilable.d.ts +57 -0
  359. package/dist/providers/traits/Reconcilable.d.ts.map +1 -0
  360. package/dist/providers/traits/Reconcilable.js +18 -0
  361. package/dist/providers/traits/Reconcilable.js.map +1 -0
  362. package/dist/providers/traits/__tests__/RelationshipQueryable.test.d.ts +2 -0
  363. package/dist/providers/traits/__tests__/RelationshipQueryable.test.d.ts.map +1 -0
  364. package/dist/providers/traits/__tests__/RelationshipQueryable.test.js +169 -0
  365. package/dist/providers/traits/__tests__/RelationshipQueryable.test.js.map +1 -0
  366. package/dist/providers/traits/__tests__/TaskManageable.test.d.ts +2 -0
  367. package/dist/providers/traits/__tests__/TaskManageable.test.d.ts.map +1 -0
  368. package/dist/providers/traits/__tests__/TaskManageable.test.js +172 -0
  369. package/dist/providers/traits/__tests__/TaskManageable.test.js.map +1 -0
  370. package/dist/providers/traits/index.d.ts +2 -0
  371. package/dist/providers/traits/index.d.ts.map +1 -1
  372. package/dist/providers/traits/index.js +1 -0
  373. package/dist/providers/traits/index.js.map +1 -1
  374. package/dist/providers/types.d.ts +63 -0
  375. package/dist/providers/types.d.ts.map +1 -1
  376. package/dist/providers/types.js +22 -0
  377. package/dist/providers/types.js.map +1 -1
  378. package/dist/schema/__tests__/validation.test.d.ts +2 -0
  379. package/dist/schema/__tests__/validation.test.d.ts.map +1 -0
  380. package/dist/schema/__tests__/validation.test.js +241 -0
  381. package/dist/schema/__tests__/validation.test.js.map +1 -0
  382. package/dist/schema/edges.d.ts +3 -3
  383. package/dist/schema/edges.d.ts.map +1 -1
  384. package/dist/sessionlog/agent/agents/claude-code.d.ts +76 -0
  385. package/dist/sessionlog/agent/agents/claude-code.d.ts.map +1 -0
  386. package/dist/sessionlog/agent/agents/claude-code.js +759 -0
  387. package/dist/sessionlog/agent/agents/claude-code.js.map +1 -0
  388. package/dist/sessionlog/agent/agents/cursor.d.ts +35 -0
  389. package/dist/sessionlog/agent/agents/cursor.d.ts.map +1 -0
  390. package/dist/sessionlog/agent/agents/cursor.js +294 -0
  391. package/dist/sessionlog/agent/agents/cursor.js.map +1 -0
  392. package/dist/sessionlog/agent/agents/gemini-cli.d.ts +62 -0
  393. package/dist/sessionlog/agent/agents/gemini-cli.d.ts.map +1 -0
  394. package/dist/sessionlog/agent/agents/gemini-cli.js +462 -0
  395. package/dist/sessionlog/agent/agents/gemini-cli.js.map +1 -0
  396. package/dist/sessionlog/agent/agents/opencode.d.ts +100 -0
  397. package/dist/sessionlog/agent/agents/opencode.d.ts.map +1 -0
  398. package/dist/sessionlog/agent/agents/opencode.js +423 -0
  399. package/dist/sessionlog/agent/agents/opencode.js.map +1 -0
  400. package/dist/sessionlog/agent/registry.d.ts +54 -0
  401. package/dist/sessionlog/agent/registry.d.ts.map +1 -0
  402. package/dist/sessionlog/agent/registry.js +123 -0
  403. package/dist/sessionlog/agent/registry.js.map +1 -0
  404. package/dist/sessionlog/agent/session-types.d.ts +45 -0
  405. package/dist/sessionlog/agent/session-types.d.ts.map +1 -0
  406. package/dist/sessionlog/agent/session-types.js +50 -0
  407. package/dist/sessionlog/agent/session-types.js.map +1 -0
  408. package/dist/sessionlog/agent/types.d.ts +126 -0
  409. package/dist/sessionlog/agent/types.d.ts.map +1 -0
  410. package/dist/sessionlog/agent/types.js +39 -0
  411. package/dist/sessionlog/agent/types.js.map +1 -0
  412. package/dist/sessionlog/commands/clean.d.ts +40 -0
  413. package/dist/sessionlog/commands/clean.d.ts.map +1 -0
  414. package/dist/sessionlog/commands/clean.js +105 -0
  415. package/dist/sessionlog/commands/clean.js.map +1 -0
  416. package/dist/sessionlog/commands/disable.d.ts +23 -0
  417. package/dist/sessionlog/commands/disable.d.ts.map +1 -0
  418. package/dist/sessionlog/commands/disable.js +57 -0
  419. package/dist/sessionlog/commands/disable.js.map +1 -0
  420. package/dist/sessionlog/commands/doctor.d.ts +43 -0
  421. package/dist/sessionlog/commands/doctor.d.ts.map +1 -0
  422. package/dist/sessionlog/commands/doctor.js +97 -0
  423. package/dist/sessionlog/commands/doctor.js.map +1 -0
  424. package/dist/sessionlog/commands/enable.d.ts +31 -0
  425. package/dist/sessionlog/commands/enable.d.ts.map +1 -0
  426. package/dist/sessionlog/commands/enable.js +102 -0
  427. package/dist/sessionlog/commands/enable.js.map +1 -0
  428. package/dist/sessionlog/commands/explain.d.ts +69 -0
  429. package/dist/sessionlog/commands/explain.d.ts.map +1 -0
  430. package/dist/sessionlog/commands/explain.js +185 -0
  431. package/dist/sessionlog/commands/explain.js.map +1 -0
  432. package/dist/sessionlog/commands/reset.d.ts +23 -0
  433. package/dist/sessionlog/commands/reset.d.ts.map +1 -0
  434. package/dist/sessionlog/commands/reset.js +68 -0
  435. package/dist/sessionlog/commands/reset.js.map +1 -0
  436. package/dist/sessionlog/commands/resume.d.ts +42 -0
  437. package/dist/sessionlog/commands/resume.d.ts.map +1 -0
  438. package/dist/sessionlog/commands/resume.js +134 -0
  439. package/dist/sessionlog/commands/resume.js.map +1 -0
  440. package/dist/sessionlog/commands/rewind.d.ts +40 -0
  441. package/dist/sessionlog/commands/rewind.d.ts.map +1 -0
  442. package/dist/sessionlog/commands/rewind.js +155 -0
  443. package/dist/sessionlog/commands/rewind.js.map +1 -0
  444. package/dist/sessionlog/commands/status.d.ts +54 -0
  445. package/dist/sessionlog/commands/status.d.ts.map +1 -0
  446. package/dist/sessionlog/commands/status.js +95 -0
  447. package/dist/sessionlog/commands/status.js.map +1 -0
  448. package/dist/sessionlog/config.d.ts +40 -0
  449. package/dist/sessionlog/config.d.ts.map +1 -0
  450. package/dist/sessionlog/config.js +126 -0
  451. package/dist/sessionlog/config.js.map +1 -0
  452. package/dist/sessionlog/git-operations.d.ts +173 -0
  453. package/dist/sessionlog/git-operations.d.ts.map +1 -0
  454. package/dist/sessionlog/git-operations.js +399 -0
  455. package/dist/sessionlog/git-operations.js.map +1 -0
  456. package/dist/sessionlog/hooks/git-hooks.d.ts +22 -0
  457. package/dist/sessionlog/hooks/git-hooks.d.ts.map +1 -0
  458. package/dist/sessionlog/hooks/git-hooks.js +145 -0
  459. package/dist/sessionlog/hooks/git-hooks.js.map +1 -0
  460. package/dist/sessionlog/hooks/lifecycle.d.ts +21 -0
  461. package/dist/sessionlog/hooks/lifecycle.d.ts.map +1 -0
  462. package/dist/sessionlog/hooks/lifecycle.js +179 -0
  463. package/dist/sessionlog/hooks/lifecycle.js.map +1 -0
  464. package/dist/sessionlog/index.d.ts +69 -0
  465. package/dist/sessionlog/index.d.ts.map +1 -0
  466. package/dist/sessionlog/index.js +154 -0
  467. package/dist/sessionlog/index.js.map +1 -0
  468. package/dist/sessionlog/security/redaction.d.ts +35 -0
  469. package/dist/sessionlog/security/redaction.d.ts.map +1 -0
  470. package/dist/sessionlog/security/redaction.js +221 -0
  471. package/dist/sessionlog/security/redaction.js.map +1 -0
  472. package/dist/sessionlog/session/state-machine.d.ts +90 -0
  473. package/dist/sessionlog/session/state-machine.d.ts.map +1 -0
  474. package/dist/sessionlog/session/state-machine.js +347 -0
  475. package/dist/sessionlog/session/state-machine.js.map +1 -0
  476. package/dist/sessionlog/store/checkpoint-store.d.ts +47 -0
  477. package/dist/sessionlog/store/checkpoint-store.d.ts.map +1 -0
  478. package/dist/sessionlog/store/checkpoint-store.js +309 -0
  479. package/dist/sessionlog/store/checkpoint-store.js.map +1 -0
  480. package/dist/sessionlog/store/native-store.d.ts +21 -0
  481. package/dist/sessionlog/store/native-store.d.ts.map +1 -0
  482. package/dist/sessionlog/store/native-store.js +162 -0
  483. package/dist/sessionlog/store/native-store.js.map +1 -0
  484. package/dist/sessionlog/store/provider-types.d.ts +78 -0
  485. package/dist/sessionlog/store/provider-types.d.ts.map +1 -0
  486. package/dist/sessionlog/store/provider-types.js +12 -0
  487. package/dist/sessionlog/store/provider-types.js.map +1 -0
  488. package/dist/sessionlog/store/session-store.d.ts +28 -0
  489. package/dist/sessionlog/store/session-store.d.ts.map +1 -0
  490. package/dist/sessionlog/store/session-store.js +187 -0
  491. package/dist/sessionlog/store/session-store.js.map +1 -0
  492. package/dist/sessionlog/strategy/attribution.d.ts +39 -0
  493. package/dist/sessionlog/strategy/attribution.d.ts.map +1 -0
  494. package/dist/sessionlog/strategy/attribution.js +227 -0
  495. package/dist/sessionlog/strategy/attribution.js.map +1 -0
  496. package/dist/sessionlog/strategy/common.d.ts +60 -0
  497. package/dist/sessionlog/strategy/common.d.ts.map +1 -0
  498. package/dist/sessionlog/strategy/common.js +162 -0
  499. package/dist/sessionlog/strategy/common.js.map +1 -0
  500. package/dist/sessionlog/strategy/content-overlap.d.ts +33 -0
  501. package/dist/sessionlog/strategy/content-overlap.d.ts.map +1 -0
  502. package/dist/sessionlog/strategy/content-overlap.js +168 -0
  503. package/dist/sessionlog/strategy/content-overlap.js.map +1 -0
  504. package/dist/sessionlog/strategy/manual-commit.d.ts +35 -0
  505. package/dist/sessionlog/strategy/manual-commit.d.ts.map +1 -0
  506. package/dist/sessionlog/strategy/manual-commit.js +732 -0
  507. package/dist/sessionlog/strategy/manual-commit.js.map +1 -0
  508. package/dist/sessionlog/strategy/types.d.ts +163 -0
  509. package/dist/sessionlog/strategy/types.d.ts.map +1 -0
  510. package/dist/sessionlog/strategy/types.js +49 -0
  511. package/dist/sessionlog/strategy/types.js.map +1 -0
  512. package/dist/sessionlog/summarize/claude-generator.d.ts +25 -0
  513. package/dist/sessionlog/summarize/claude-generator.d.ts.map +1 -0
  514. package/dist/sessionlog/summarize/claude-generator.js +87 -0
  515. package/dist/sessionlog/summarize/claude-generator.js.map +1 -0
  516. package/dist/sessionlog/summarize/summarize.d.ts +52 -0
  517. package/dist/sessionlog/summarize/summarize.d.ts.map +1 -0
  518. package/dist/sessionlog/summarize/summarize.js +335 -0
  519. package/dist/sessionlog/summarize/summarize.js.map +1 -0
  520. package/dist/sessionlog/types.d.ts +298 -0
  521. package/dist/sessionlog/types.d.ts.map +1 -0
  522. package/dist/sessionlog/types.js +104 -0
  523. package/dist/sessionlog/types.js.map +1 -0
  524. package/dist/sessionlog/utils/chunk-files.d.ts +25 -0
  525. package/dist/sessionlog/utils/chunk-files.d.ts.map +1 -0
  526. package/dist/sessionlog/utils/chunk-files.js +47 -0
  527. package/dist/sessionlog/utils/chunk-files.js.map +1 -0
  528. package/dist/sessionlog/utils/commit-message.d.ts +11 -0
  529. package/dist/sessionlog/utils/commit-message.d.ts.map +1 -0
  530. package/dist/sessionlog/utils/commit-message.js +54 -0
  531. package/dist/sessionlog/utils/commit-message.js.map +1 -0
  532. package/dist/sessionlog/utils/detect-agent.d.ts +19 -0
  533. package/dist/sessionlog/utils/detect-agent.d.ts.map +1 -0
  534. package/dist/sessionlog/utils/detect-agent.js +34 -0
  535. package/dist/sessionlog/utils/detect-agent.js.map +1 -0
  536. package/dist/sessionlog/utils/hook-managers.d.ts +24 -0
  537. package/dist/sessionlog/utils/hook-managers.d.ts.map +1 -0
  538. package/dist/sessionlog/utils/hook-managers.js +87 -0
  539. package/dist/sessionlog/utils/hook-managers.js.map +1 -0
  540. package/dist/sessionlog/utils/ide-tags.d.ts +12 -0
  541. package/dist/sessionlog/utils/ide-tags.d.ts.map +1 -0
  542. package/dist/sessionlog/utils/ide-tags.js +30 -0
  543. package/dist/sessionlog/utils/ide-tags.js.map +1 -0
  544. package/dist/sessionlog/utils/paths.d.ts +32 -0
  545. package/dist/sessionlog/utils/paths.d.ts.map +1 -0
  546. package/dist/sessionlog/utils/paths.js +55 -0
  547. package/dist/sessionlog/utils/paths.js.map +1 -0
  548. package/dist/sessionlog/utils/preview-rewind.d.ts +23 -0
  549. package/dist/sessionlog/utils/preview-rewind.d.ts.map +1 -0
  550. package/dist/sessionlog/utils/preview-rewind.js +63 -0
  551. package/dist/sessionlog/utils/preview-rewind.js.map +1 -0
  552. package/dist/sessionlog/utils/rewind-conflict.d.ts +52 -0
  553. package/dist/sessionlog/utils/rewind-conflict.d.ts.map +1 -0
  554. package/dist/sessionlog/utils/rewind-conflict.js +79 -0
  555. package/dist/sessionlog/utils/rewind-conflict.js.map +1 -0
  556. package/dist/sessionlog/utils/shadow-branch.d.ts +53 -0
  557. package/dist/sessionlog/utils/shadow-branch.d.ts.map +1 -0
  558. package/dist/sessionlog/utils/shadow-branch.js +97 -0
  559. package/dist/sessionlog/utils/shadow-branch.js.map +1 -0
  560. package/dist/sessionlog/utils/string-utils.d.ts +24 -0
  561. package/dist/sessionlog/utils/string-utils.d.ts.map +1 -0
  562. package/dist/sessionlog/utils/string-utils.js +47 -0
  563. package/dist/sessionlog/utils/string-utils.js.map +1 -0
  564. package/dist/sessionlog/utils/todo-extract.d.ts +52 -0
  565. package/dist/sessionlog/utils/todo-extract.d.ts.map +1 -0
  566. package/dist/sessionlog/utils/todo-extract.js +167 -0
  567. package/dist/sessionlog/utils/todo-extract.js.map +1 -0
  568. package/dist/sessionlog/utils/trailers.d.ts +36 -0
  569. package/dist/sessionlog/utils/trailers.d.ts.map +1 -0
  570. package/dist/sessionlog/utils/trailers.js +149 -0
  571. package/dist/sessionlog/utils/trailers.js.map +1 -0
  572. package/dist/sessionlog/utils/transcript-parse.d.ts +57 -0
  573. package/dist/sessionlog/utils/transcript-parse.d.ts.map +1 -0
  574. package/dist/sessionlog/utils/transcript-parse.js +126 -0
  575. package/dist/sessionlog/utils/transcript-parse.js.map +1 -0
  576. package/dist/sessionlog/utils/transcript-timestamp.d.ts +22 -0
  577. package/dist/sessionlog/utils/transcript-timestamp.d.ts.map +1 -0
  578. package/dist/sessionlog/utils/transcript-timestamp.js +56 -0
  579. package/dist/sessionlog/utils/transcript-timestamp.js.map +1 -0
  580. package/dist/sessionlog/utils/tree-ops.d.ts +47 -0
  581. package/dist/sessionlog/utils/tree-ops.d.ts.map +1 -0
  582. package/dist/sessionlog/utils/tree-ops.js +145 -0
  583. package/dist/sessionlog/utils/tree-ops.js.map +1 -0
  584. package/dist/sessionlog/utils/tty.d.ts +25 -0
  585. package/dist/sessionlog/utils/tty.d.ts.map +1 -0
  586. package/dist/sessionlog/utils/tty.js +70 -0
  587. package/dist/sessionlog/utils/tty.js.map +1 -0
  588. package/dist/sessionlog/utils/validation.d.ts +31 -0
  589. package/dist/sessionlog/utils/validation.d.ts.map +1 -0
  590. package/dist/sessionlog/utils/validation.js +59 -0
  591. package/dist/sessionlog/utils/validation.js.map +1 -0
  592. package/dist/sessionlog/utils/worktree.d.ts +16 -0
  593. package/dist/sessionlog/utils/worktree.d.ts.map +1 -0
  594. package/dist/sessionlog/utils/worktree.js +50 -0
  595. package/dist/sessionlog/utils/worktree.js.map +1 -0
  596. package/dist/storage/__tests__/atomic-write.test.d.ts +5 -0
  597. package/dist/storage/__tests__/atomic-write.test.d.ts.map +1 -0
  598. package/dist/storage/__tests__/atomic-write.test.js +170 -0
  599. package/dist/storage/__tests__/atomic-write.test.js.map +1 -0
  600. package/dist/storage/__tests__/file-lock.test.d.ts +2 -0
  601. package/dist/storage/__tests__/file-lock.test.d.ts.map +1 -0
  602. package/dist/storage/__tests__/file-lock.test.js +89 -0
  603. package/dist/storage/__tests__/file-lock.test.js.map +1 -0
  604. package/dist/storage/__tests__/jsonl.test.d.ts +2 -0
  605. package/dist/storage/__tests__/jsonl.test.d.ts.map +1 -0
  606. package/dist/storage/__tests__/jsonl.test.js +228 -0
  607. package/dist/storage/__tests__/jsonl.test.js.map +1 -0
  608. package/dist/storage/__tests__/locked-writer.test.d.ts +2 -0
  609. package/dist/storage/__tests__/locked-writer.test.d.ts.map +1 -0
  610. package/dist/storage/__tests__/locked-writer.test.js +109 -0
  611. package/dist/storage/__tests__/locked-writer.test.js.map +1 -0
  612. package/dist/storage/__tests__/sqlite.test.d.ts +2 -0
  613. package/dist/storage/__tests__/sqlite.test.d.ts.map +1 -0
  614. package/dist/storage/__tests__/sqlite.test.js +470 -0
  615. package/dist/storage/__tests__/sqlite.test.js.map +1 -0
  616. package/dist/storage/sqlite-schema.d.ts.map +1 -1
  617. package/dist/storage/sqlite-schema.js +2 -0
  618. package/dist/storage/sqlite-schema.js.map +1 -1
  619. package/dist/storage/sqlite.d.ts.map +1 -1
  620. package/dist/storage/sqlite.js +18 -4
  621. package/dist/storage/sqlite.js.map +1 -1
  622. package/dist/tools/__tests__/annotate.test.d.ts +5 -0
  623. package/dist/tools/__tests__/annotate.test.d.ts.map +1 -0
  624. package/dist/tools/__tests__/annotate.test.js +314 -0
  625. package/dist/tools/__tests__/annotate.test.js.map +1 -0
  626. package/dist/tools/__tests__/link.test.d.ts +5 -0
  627. package/dist/tools/__tests__/link.test.d.ts.map +1 -0
  628. package/dist/tools/__tests__/link.test.js +245 -0
  629. package/dist/tools/__tests__/link.test.js.map +1 -0
  630. package/dist/tools/__tests__/query.test.d.ts +5 -0
  631. package/dist/tools/__tests__/query.test.d.ts.map +1 -0
  632. package/dist/tools/__tests__/query.test.js +288 -0
  633. package/dist/tools/__tests__/query.test.js.map +1 -0
  634. package/dist/tools/__tests__/task.test.d.ts +5 -0
  635. package/dist/tools/__tests__/task.test.d.ts.map +1 -0
  636. package/dist/tools/__tests__/task.test.js +178 -0
  637. package/dist/tools/__tests__/task.test.js.map +1 -0
  638. package/dist/tools/index.d.ts +1 -1
  639. package/dist/tools/index.d.ts.map +1 -1
  640. package/dist/tools/index.js.map +1 -1
  641. package/dist/tools/query.d.ts +2 -1
  642. package/dist/tools/query.d.ts.map +1 -1
  643. package/dist/tools/query.js +217 -7
  644. package/dist/tools/query.js.map +1 -1
  645. package/dist/tools/types.d.ts +57 -0
  646. package/dist/tools/types.d.ts.map +1 -1
  647. package/dist/tools/types.js.map +1 -1
  648. package/dist/tracking/claude-task-reconstructor.d.ts +41 -0
  649. package/dist/tracking/claude-task-reconstructor.d.ts.map +1 -0
  650. package/dist/tracking/claude-task-reconstructor.js +91 -0
  651. package/dist/tracking/claude-task-reconstructor.js.map +1 -0
  652. package/dist/tracking/plan-mode-tracker.d.ts +20 -0
  653. package/dist/tracking/plan-mode-tracker.d.ts.map +1 -0
  654. package/dist/tracking/plan-mode-tracker.js +35 -0
  655. package/dist/tracking/plan-mode-tracker.js.map +1 -0
  656. package/dist/tracking/transcript-extractor.d.ts +3 -3
  657. package/dist/tracking/transcript-extractor.d.ts.map +1 -1
  658. package/dist/tracking/transcript-extractor.js +1 -1
  659. package/dist/tracking/transcript-extractor.js.map +1 -1
  660. package/package.json +3 -1
@@ -0,0 +1,774 @@
1
+ /**
2
+ * Tests for Query Engine
3
+ */
4
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
5
+ import { createQueryEngine } from '../query.js';
6
+ // ============================================================================
7
+ // Test Fixtures
8
+ // ============================================================================
9
+ function createMockStorage() {
10
+ return {
11
+ createNode: vi.fn(),
12
+ getNode: vi.fn(),
13
+ updateNode: vi.fn(),
14
+ deleteNode: vi.fn(),
15
+ queryNodes: vi.fn().mockResolvedValue([]),
16
+ createEdge: vi.fn(),
17
+ getEdge: vi.fn(),
18
+ deleteEdge: vi.fn(),
19
+ getEdgesFrom: vi.fn().mockResolvedValue([]),
20
+ getEdgesTo: vi.fn().mockResolvedValue([]),
21
+ addTag: vi.fn(),
22
+ removeTag: vi.fn(),
23
+ getTags: vi.fn().mockResolvedValue([]),
24
+ getTagsForNodes: vi.fn().mockResolvedValue(new Map()),
25
+ getNodesByTag: vi.fn().mockResolvedValue([]),
26
+ getReady: vi.fn().mockResolvedValue([]),
27
+ runInTransaction: vi.fn(),
28
+ markDirty: vi.fn(),
29
+ getDirtyNodes: vi.fn().mockResolvedValue([]),
30
+ clearDirty: vi.fn(),
31
+ close: vi.fn(),
32
+ };
33
+ }
34
+ function createSpec(id, title, overrides = {}) {
35
+ return {
36
+ id,
37
+ uuid: `uuid-${id}`,
38
+ type: 'spec',
39
+ title,
40
+ created_at: '2024-01-01T00:00:00Z',
41
+ updated_at: '2024-01-01T00:00:00Z',
42
+ archived: false,
43
+ ...overrides,
44
+ };
45
+ }
46
+ function createIssue(id, title, status = 'open', overrides = {}) {
47
+ return {
48
+ id,
49
+ uuid: `uuid-${id}`,
50
+ type: 'issue',
51
+ title,
52
+ status,
53
+ created_at: '2024-01-01T00:00:00Z',
54
+ updated_at: '2024-01-01T00:00:00Z',
55
+ archived: false,
56
+ ...overrides,
57
+ };
58
+ }
59
+ function createFeedback(id, targetId, overrides = {}) {
60
+ return {
61
+ id,
62
+ uuid: `uuid-${id}`,
63
+ type: 'feedback',
64
+ title: 'Feedback',
65
+ target_id: targetId,
66
+ feedback_type: 'comment',
67
+ created_at: '2024-01-01T00:00:00Z',
68
+ updated_at: '2024-01-01T00:00:00Z',
69
+ archived: false,
70
+ resolved: false,
71
+ dismissed: false,
72
+ ...overrides,
73
+ };
74
+ }
75
+ function createEdge(id, fromId, toId, type) {
76
+ return {
77
+ id,
78
+ uuid: `uuid-${id}`,
79
+ from_id: fromId,
80
+ to_id: toId,
81
+ type,
82
+ created_at: '2024-01-01T00:00:00Z',
83
+ };
84
+ }
85
+ // ============================================================================
86
+ // Test Suites
87
+ // ============================================================================
88
+ describe('QueryEngine', () => {
89
+ let storage;
90
+ let engine;
91
+ beforeEach(() => {
92
+ storage = createMockStorage();
93
+ engine = createQueryEngine(storage);
94
+ });
95
+ describe('nodes()', () => {
96
+ it('should query nodes with filter', async () => {
97
+ const spec = createSpec('s-abc1', 'Test Spec');
98
+ vi.mocked(storage.queryNodes).mockResolvedValue([spec]);
99
+ const result = await engine.nodes({ type: 'spec' });
100
+ expect(storage.queryNodes).toHaveBeenCalledWith({
101
+ type: 'spec',
102
+ status: undefined,
103
+ tags: undefined,
104
+ parent_id: undefined,
105
+ archived: undefined,
106
+ search: undefined,
107
+ limit: undefined,
108
+ offset: undefined,
109
+ });
110
+ expect(result).toHaveLength(1);
111
+ expect(result[0].id).toBe('s-abc1');
112
+ });
113
+ it('should filter out invalid nodes', async () => {
114
+ const validSpec = createSpec('s-abc1', 'Test Spec');
115
+ const invalidNode = { id: 'invalid', type: 'unknown' };
116
+ vi.mocked(storage.queryNodes).mockResolvedValue([validSpec, invalidNode]);
117
+ const result = await engine.nodes({});
118
+ // Only valid spec should be returned
119
+ expect(result).toHaveLength(1);
120
+ expect(result[0].id).toBe('s-abc1');
121
+ });
122
+ it('should handle archived filter', async () => {
123
+ vi.mocked(storage.queryNodes).mockResolvedValue([]);
124
+ await engine.nodes({ archived: false });
125
+ expect(storage.queryNodes).toHaveBeenCalledWith(expect.objectContaining({ archived: false }));
126
+ });
127
+ it('should convert null archived to undefined', async () => {
128
+ vi.mocked(storage.queryNodes).mockResolvedValue([]);
129
+ await engine.nodes({ archived: null });
130
+ expect(storage.queryNodes).toHaveBeenCalledWith(expect.objectContaining({ archived: undefined }));
131
+ });
132
+ });
133
+ describe('edges()', () => {
134
+ it('should query edges from a node', async () => {
135
+ const edge = createEdge('x-abc1', 's-a', 's-b', 'references');
136
+ vi.mocked(storage.getEdgesFrom).mockResolvedValue([edge]);
137
+ const result = await engine.edges({ from_id: 's-a' });
138
+ expect(storage.getEdgesFrom).toHaveBeenCalledWith('s-a', undefined);
139
+ expect(result).toHaveLength(1);
140
+ expect(result[0].id).toBe('x-abc1');
141
+ });
142
+ it('should query edges to a node', async () => {
143
+ const edge = createEdge('x-abc1', 's-a', 's-b', 'references');
144
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([edge]);
145
+ const result = await engine.edges({ to_id: 's-b' });
146
+ expect(storage.getEdgesTo).toHaveBeenCalledWith('s-b', undefined);
147
+ expect(result).toHaveLength(1);
148
+ });
149
+ it('should return empty when no filter specified', async () => {
150
+ const result = await engine.edges({});
151
+ expect(result).toHaveLength(0);
152
+ });
153
+ it('should apply pagination', async () => {
154
+ const edges = [
155
+ createEdge('x-1', 's-a', 's-b', 'references'),
156
+ createEdge('x-2', 's-a', 's-c', 'references'),
157
+ createEdge('x-3', 's-a', 's-d', 'references'),
158
+ ];
159
+ vi.mocked(storage.getEdgesFrom).mockResolvedValue(edges);
160
+ const result = await engine.edges({ from_id: 's-a', offset: 1, limit: 1 });
161
+ expect(result).toHaveLength(1);
162
+ expect(result[0].id).toBe('x-2');
163
+ });
164
+ });
165
+ describe('edgesFrom()', () => {
166
+ it('should get edges from a node', async () => {
167
+ const edge = createEdge('x-abc1', 's-a', 's-b', 'blocks');
168
+ vi.mocked(storage.getEdgesFrom).mockResolvedValue([edge]);
169
+ const result = await engine.edgesFrom('s-a', 'blocks');
170
+ expect(storage.getEdgesFrom).toHaveBeenCalledWith('s-a', 'blocks');
171
+ expect(result).toHaveLength(1);
172
+ });
173
+ });
174
+ describe('edgesTo()', () => {
175
+ it('should get edges to a node', async () => {
176
+ const edge = createEdge('x-abc1', 's-a', 's-b', 'blocks');
177
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([edge]);
178
+ const result = await engine.edgesTo('s-b', 'blocks');
179
+ expect(storage.getEdgesTo).toHaveBeenCalledWith('s-b', 'blocks');
180
+ expect(result).toHaveLength(1);
181
+ });
182
+ });
183
+ describe('edgesFor()', () => {
184
+ it('should get all edges for a node (both directions)', async () => {
185
+ const fromEdge = createEdge('x-1', 's-a', 's-b', 'blocks');
186
+ const toEdge = createEdge('x-2', 's-c', 's-a', 'references');
187
+ vi.mocked(storage.getEdgesFrom).mockResolvedValue([fromEdge]);
188
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([toEdge]);
189
+ const result = await engine.edgesFor('s-a');
190
+ expect(result).toHaveLength(2);
191
+ expect(result.map((e) => e.id).sort()).toEqual(['x-1', 'x-2']);
192
+ });
193
+ it('should deduplicate edges', async () => {
194
+ const edge = createEdge('x-1', 's-a', 's-a', 'references'); // self-reference
195
+ vi.mocked(storage.getEdgesFrom).mockResolvedValue([edge]);
196
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([edge]);
197
+ const result = await engine.edgesFor('s-a');
198
+ expect(result).toHaveLength(1);
199
+ });
200
+ });
201
+ describe('blockers()', () => {
202
+ it('should get direct blockers', async () => {
203
+ const blocker = createIssue('i-blocker', 'Blocking Issue', 'open');
204
+ const edge = createEdge('x-1', 'i-blocker', 'i-blocked', 'blocks');
205
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([edge]);
206
+ vi.mocked(storage.getNode).mockResolvedValue(blocker);
207
+ const result = await engine.blockers('i-blocked');
208
+ expect(result).toHaveLength(1);
209
+ expect(result[0].id).toBe('i-blocker');
210
+ });
211
+ it('should filter out inactive blockers by default', async () => {
212
+ const closedBlocker = createIssue('i-blocker', 'Closed Blocker', 'closed');
213
+ const edge = createEdge('x-1', 'i-blocker', 'i-blocked', 'blocks');
214
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([edge]);
215
+ vi.mocked(storage.getNode).mockResolvedValue(closedBlocker);
216
+ const result = await engine.blockers('i-blocked');
217
+ expect(result).toHaveLength(0);
218
+ });
219
+ it('should include inactive blockers when activeOnly=false', async () => {
220
+ const closedBlocker = createIssue('i-blocker', 'Closed Blocker', 'closed');
221
+ const edge = createEdge('x-1', 'i-blocker', 'i-blocked', 'blocks');
222
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([edge]);
223
+ vi.mocked(storage.getNode).mockResolvedValue(closedBlocker);
224
+ const result = await engine.blockers('i-blocked', { activeOnly: false });
225
+ expect(result).toHaveLength(1);
226
+ });
227
+ it('should get transitive blockers', async () => {
228
+ const blockerA = createIssue('i-a', 'Blocker A', 'open');
229
+ const blockerB = createIssue('i-b', 'Blocker B', 'open');
230
+ vi.mocked(storage.getEdgesTo)
231
+ .mockResolvedValueOnce([createEdge('x-1', 'i-a', 'i-target', 'blocks')])
232
+ .mockResolvedValueOnce([createEdge('x-2', 'i-b', 'i-a', 'blocks')])
233
+ .mockResolvedValueOnce([]);
234
+ vi.mocked(storage.getNode)
235
+ .mockResolvedValueOnce(blockerA)
236
+ .mockResolvedValueOnce(blockerB);
237
+ const result = await engine.blockers('i-target', { transitive: true });
238
+ expect(result).toHaveLength(2);
239
+ });
240
+ it('should respect maxDepth', async () => {
241
+ // maxDepth=0 allows depth 0 (direct blockers) but no recursion
242
+ // maxDepth=1 would allow depth 0 and depth 1 (blockers of blockers)
243
+ const directBlocker = createIssue('i-direct', 'Direct Blocker', 'open');
244
+ const transitiveBlocker = createIssue('i-transitive', 'Transitive', 'open');
245
+ vi.mocked(storage.getEdgesTo)
246
+ .mockResolvedValueOnce([createEdge('x-1', 'i-direct', 'i-target', 'blocks')])
247
+ .mockResolvedValueOnce([createEdge('x-2', 'i-transitive', 'i-direct', 'blocks')]);
248
+ vi.mocked(storage.getNode)
249
+ .mockResolvedValueOnce(directBlocker)
250
+ .mockResolvedValueOnce(transitiveBlocker);
251
+ // With maxDepth=0 and transitive=true, we get direct blocker but NOT its blockers
252
+ const result = await engine.blockers('i-target', {
253
+ transitive: true,
254
+ maxDepth: 0,
255
+ });
256
+ // Should have 1 result (direct blocker), not 2 (would include transitive)
257
+ expect(result).toHaveLength(1);
258
+ expect(result[0].id).toBe('i-direct');
259
+ });
260
+ });
261
+ describe('blocking()', () => {
262
+ it('should get nodes this blocks', async () => {
263
+ const blocked = createIssue('i-blocked', 'Blocked Issue', 'open');
264
+ const edge = createEdge('x-1', 'i-blocker', 'i-blocked', 'blocks');
265
+ vi.mocked(storage.getEdgesFrom).mockResolvedValue([edge]);
266
+ vi.mocked(storage.getNode).mockResolvedValue(blocked);
267
+ const result = await engine.blocking('i-blocker');
268
+ expect(result).toHaveLength(1);
269
+ expect(result[0].id).toBe('i-blocked');
270
+ });
271
+ });
272
+ describe('isBlocking()', () => {
273
+ it('should return true for direct blocking', async () => {
274
+ vi.mocked(storage.getEdgesFrom).mockResolvedValue([
275
+ createEdge('x-1', 'i-a', 'i-b', 'blocks'),
276
+ ]);
277
+ const result = await engine.isBlocking('i-a', 'i-b');
278
+ expect(result).toBe(true);
279
+ });
280
+ it('should return true for transitive blocking', async () => {
281
+ vi.mocked(storage.getEdgesFrom)
282
+ .mockResolvedValueOnce([createEdge('x-1', 'i-a', 'i-b', 'blocks')])
283
+ .mockResolvedValueOnce([createEdge('x-2', 'i-b', 'i-c', 'blocks')]);
284
+ const result = await engine.isBlocking('i-a', 'i-c');
285
+ expect(result).toBe(true);
286
+ });
287
+ it('should return false when not blocking', async () => {
288
+ vi.mocked(storage.getEdgesFrom).mockResolvedValue([]);
289
+ const result = await engine.isBlocking('i-a', 'i-b');
290
+ expect(result).toBe(false);
291
+ });
292
+ it('should handle cycles in graph without infinite loop', async () => {
293
+ vi.mocked(storage.getEdgesFrom)
294
+ .mockResolvedValueOnce([createEdge('x-1', 'i-a', 'i-b', 'blocks')])
295
+ .mockResolvedValueOnce([createEdge('x-2', 'i-b', 'i-a', 'blocks')]) // cycle back
296
+ .mockResolvedValue([]);
297
+ // Should terminate without infinite loop
298
+ const result = await engine.isBlocking('i-a', 'i-c');
299
+ expect(result).toBe(false);
300
+ });
301
+ });
302
+ describe('implementers()', () => {
303
+ it('should get issues that implement a spec', async () => {
304
+ const issue = createIssue('i-impl', 'Implementation');
305
+ const edge = createEdge('x-1', 'i-impl', 's-spec', 'implements');
306
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([edge]);
307
+ vi.mocked(storage.getNode).mockResolvedValue(issue);
308
+ const result = await engine.implementers('s-spec');
309
+ expect(storage.getEdgesTo).toHaveBeenCalledWith('s-spec', 'implements');
310
+ expect(result).toHaveLength(1);
311
+ expect(result[0].id).toBe('i-impl');
312
+ });
313
+ it('should filter out non-issues', async () => {
314
+ const spec = createSpec('s-not-issue', 'Not an issue');
315
+ const edge = createEdge('x-1', 's-not-issue', 's-spec', 'implements');
316
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([edge]);
317
+ vi.mocked(storage.getNode).mockResolvedValue(spec);
318
+ const result = await engine.implementers('s-spec');
319
+ expect(result).toHaveLength(0);
320
+ });
321
+ });
322
+ describe('specs()', () => {
323
+ it('should get specs that an issue implements', async () => {
324
+ const spec = createSpec('s-spec', 'Spec');
325
+ const edge = createEdge('x-1', 'i-issue', 's-spec', 'implements');
326
+ vi.mocked(storage.getEdgesFrom).mockResolvedValue([edge]);
327
+ vi.mocked(storage.getNode).mockResolvedValue(spec);
328
+ const result = await engine.specs('i-issue');
329
+ expect(storage.getEdgesFrom).toHaveBeenCalledWith('i-issue', 'implements');
330
+ expect(result).toHaveLength(1);
331
+ expect(result[0].id).toBe('s-spec');
332
+ });
333
+ });
334
+ describe('children()', () => {
335
+ it('should get child nodes', async () => {
336
+ const child = createSpec('s-child', 'Child', { parent_id: 's-parent' });
337
+ vi.mocked(storage.queryNodes).mockResolvedValue([child]);
338
+ const result = await engine.children('s-parent');
339
+ expect(storage.queryNodes).toHaveBeenCalledWith({ parent_id: 's-parent' });
340
+ expect(result).toHaveLength(1);
341
+ expect(result[0].id).toBe('s-child');
342
+ });
343
+ });
344
+ describe('parent()', () => {
345
+ it('should get parent node', async () => {
346
+ const child = createSpec('s-child', 'Child', { parent_id: 's-parent' });
347
+ const parent = createSpec('s-parent', 'Parent');
348
+ vi.mocked(storage.getNode)
349
+ .mockResolvedValueOnce(child)
350
+ .mockResolvedValueOnce(parent);
351
+ const result = await engine.parent('s-child');
352
+ expect(result).not.toBeNull();
353
+ expect(result?.id).toBe('s-parent');
354
+ });
355
+ it('should return null when no parent', async () => {
356
+ const node = createSpec('s-root', 'Root');
357
+ vi.mocked(storage.getNode).mockResolvedValue(node);
358
+ const result = await engine.parent('s-root');
359
+ expect(result).toBeNull();
360
+ });
361
+ it('should return null when node not found', async () => {
362
+ vi.mocked(storage.getNode).mockResolvedValue(null);
363
+ const result = await engine.parent('s-missing');
364
+ expect(result).toBeNull();
365
+ });
366
+ });
367
+ describe('ancestors()', () => {
368
+ it('should get all ancestors', async () => {
369
+ const child = createSpec('s-child', 'Child', { parent_id: 's-mid' });
370
+ const mid = createSpec('s-mid', 'Mid', { parent_id: 's-root' });
371
+ const root = createSpec('s-root', 'Root');
372
+ vi.mocked(storage.getNode)
373
+ .mockResolvedValueOnce(child)
374
+ .mockResolvedValueOnce(mid)
375
+ .mockResolvedValueOnce(mid) // gets mid again for parent_id check
376
+ .mockResolvedValueOnce(root)
377
+ .mockResolvedValueOnce(root); // gets root again for parent_id check
378
+ const result = await engine.ancestors('s-child');
379
+ expect(result.length).toBeGreaterThanOrEqual(1);
380
+ });
381
+ it('should return empty for root node', async () => {
382
+ const root = createSpec('s-root', 'Root');
383
+ vi.mocked(storage.getNode).mockResolvedValue(root);
384
+ const result = await engine.ancestors('s-root');
385
+ expect(result).toHaveLength(0);
386
+ });
387
+ });
388
+ describe('descendants()', () => {
389
+ it('should get all descendants', async () => {
390
+ const child1 = createSpec('s-c1', 'Child 1', { parent_id: 's-root' });
391
+ const child2 = createSpec('s-c2', 'Child 2', { parent_id: 's-root' });
392
+ const grandchild = createSpec('s-gc', 'Grandchild', { parent_id: 's-c1' });
393
+ vi.mocked(storage.queryNodes)
394
+ .mockResolvedValueOnce([child1, child2]) // children of root
395
+ .mockResolvedValueOnce([grandchild]) // children of c1
396
+ .mockResolvedValueOnce([]) // children of c2
397
+ .mockResolvedValueOnce([]); // children of gc
398
+ const result = await engine.descendants('s-root');
399
+ expect(result).toHaveLength(3);
400
+ });
401
+ });
402
+ describe('ready()', () => {
403
+ it('should return open issues with no active blockers', async () => {
404
+ const issue = createIssue('i-ready', 'Ready Issue', 'open');
405
+ vi.mocked(storage.queryNodes).mockResolvedValue([issue]);
406
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([]); // no blockers
407
+ const result = await engine.ready();
408
+ expect(storage.queryNodes).toHaveBeenCalledWith({
409
+ type: 'issue',
410
+ status: 'open',
411
+ archived: false,
412
+ });
413
+ expect(result).toHaveLength(1);
414
+ expect(result[0].id).toBe('i-ready');
415
+ });
416
+ it('should exclude issues with active blockers', async () => {
417
+ const issue = createIssue('i-blocked', 'Blocked Issue', 'open');
418
+ const blocker = createIssue('i-blocker', 'Blocker', 'open');
419
+ vi.mocked(storage.queryNodes).mockResolvedValue([issue]);
420
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([
421
+ createEdge('x-1', 'i-blocker', 'i-blocked', 'blocks'),
422
+ ]);
423
+ vi.mocked(storage.getNode).mockResolvedValue(blocker);
424
+ const result = await engine.ready();
425
+ expect(result).toHaveLength(0);
426
+ });
427
+ it('should include issues whose blockers are closed', async () => {
428
+ const issue = createIssue('i-ready', 'Ready Issue', 'open');
429
+ const closedBlocker = createIssue('i-blocker', 'Closed', 'closed');
430
+ vi.mocked(storage.queryNodes).mockResolvedValue([issue]);
431
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([
432
+ createEdge('x-1', 'i-blocker', 'i-ready', 'blocks'),
433
+ ]);
434
+ vi.mocked(storage.getNode).mockResolvedValue(closedBlocker);
435
+ const result = await engine.ready();
436
+ expect(result).toHaveLength(1);
437
+ });
438
+ it('should include issues whose blockers are archived', async () => {
439
+ const issue = createIssue('i-ready', 'Ready Issue', 'open');
440
+ const archivedBlocker = createIssue('i-blocker', 'Archived', 'open', {
441
+ archived: true,
442
+ });
443
+ vi.mocked(storage.queryNodes).mockResolvedValue([issue]);
444
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([
445
+ createEdge('x-1', 'i-blocker', 'i-ready', 'blocks'),
446
+ ]);
447
+ vi.mocked(storage.getNode).mockResolvedValue(archivedBlocker);
448
+ const result = await engine.ready();
449
+ expect(result).toHaveLength(1);
450
+ });
451
+ it('should filter by tags', async () => {
452
+ const issue1 = createIssue('i-1', 'Issue 1', 'open', { tags: ['urgent'] });
453
+ const issue2 = createIssue('i-2', 'Issue 2', 'open', { tags: ['low'] });
454
+ vi.mocked(storage.queryNodes).mockResolvedValue([issue1, issue2]);
455
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([]);
456
+ const result = await engine.ready({ tags: ['urgent'] });
457
+ expect(result).toHaveLength(1);
458
+ expect(result[0].id).toBe('i-1');
459
+ });
460
+ it('should filter by priority', async () => {
461
+ const highPriority = createIssue('i-high', 'High', 'open', { priority: 0 });
462
+ const lowPriority = createIssue('i-low', 'Low', 'open', { priority: 4 });
463
+ vi.mocked(storage.queryNodes).mockResolvedValue([highPriority, lowPriority]);
464
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([]);
465
+ const result = await engine.ready({ priority: 0 });
466
+ expect(result).toHaveLength(1);
467
+ expect(result[0].id).toBe('i-high');
468
+ });
469
+ it('should filter by priority range', async () => {
470
+ const p0 = createIssue('i-0', 'P0', 'open', { priority: 0 });
471
+ const p2 = createIssue('i-2', 'P2', 'open', { priority: 2 });
472
+ const p4 = createIssue('i-4', 'P4', 'open', { priority: 4 });
473
+ vi.mocked(storage.queryNodes).mockResolvedValue([p0, p2, p4]);
474
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([]);
475
+ const result = await engine.ready({ priority: { min: 1, max: 3 } });
476
+ expect(result).toHaveLength(1);
477
+ expect(result[0].id).toBe('i-2');
478
+ });
479
+ it('should filter by assignee', async () => {
480
+ const assigned = createIssue('i-assigned', 'Assigned', 'open', {
481
+ assignee: 'alice',
482
+ });
483
+ const unassigned = createIssue('i-unassigned', 'Unassigned', 'open');
484
+ vi.mocked(storage.queryNodes).mockResolvedValue([assigned, unassigned]);
485
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([]);
486
+ const result = await engine.ready({ assignee: 'alice' });
487
+ expect(result).toHaveLength(1);
488
+ expect(result[0].id).toBe('i-assigned');
489
+ });
490
+ it('should respect limit', async () => {
491
+ const issues = [
492
+ createIssue('i-1', 'Issue 1', 'open'),
493
+ createIssue('i-2', 'Issue 2', 'open'),
494
+ createIssue('i-3', 'Issue 3', 'open'),
495
+ ];
496
+ vi.mocked(storage.queryNodes).mockResolvedValue(issues);
497
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([]);
498
+ const result = await engine.ready({ limit: 2 });
499
+ expect(result).toHaveLength(2);
500
+ });
501
+ });
502
+ describe('feedback()', () => {
503
+ it('should get feedback for a target', async () => {
504
+ const fb = createFeedback('f-1', 's-spec');
505
+ vi.mocked(storage.queryNodes).mockResolvedValue([fb]);
506
+ const result = await engine.feedback('s-spec');
507
+ expect(result).toHaveLength(1);
508
+ expect(result[0].id).toBe('f-1');
509
+ });
510
+ it('should filter by type', async () => {
511
+ const comment = createFeedback('f-1', 's-spec', { feedback_type: 'comment' });
512
+ const suggestion = createFeedback('f-2', 's-spec', {
513
+ feedback_type: 'suggestion',
514
+ });
515
+ vi.mocked(storage.queryNodes).mockResolvedValue([comment, suggestion]);
516
+ const result = await engine.feedback('s-spec', { type: 'comment' });
517
+ expect(result).toHaveLength(1);
518
+ expect(result[0].id).toBe('f-1');
519
+ });
520
+ it('should filter by resolved status', async () => {
521
+ const resolved = createFeedback('f-1', 's-spec', { resolved: true });
522
+ const unresolved = createFeedback('f-2', 's-spec', { resolved: false });
523
+ vi.mocked(storage.queryNodes).mockResolvedValue([resolved, unresolved]);
524
+ const result = await engine.feedback('s-spec', { resolved: true });
525
+ expect(result).toHaveLength(1);
526
+ expect(result[0].id).toBe('f-1');
527
+ });
528
+ it('should exclude dismissed by default', async () => {
529
+ const normal = createFeedback('f-1', 's-spec', { dismissed: false });
530
+ const dismissed = createFeedback('f-2', 's-spec', { dismissed: true });
531
+ vi.mocked(storage.queryNodes).mockResolvedValue([normal, dismissed]);
532
+ const result = await engine.feedback('s-spec');
533
+ expect(result).toHaveLength(1);
534
+ expect(result[0].id).toBe('f-1');
535
+ });
536
+ it('should include dismissed when requested', async () => {
537
+ const normal = createFeedback('f-1', 's-spec', { dismissed: false });
538
+ const dismissed = createFeedback('f-2', 's-spec', { dismissed: true });
539
+ vi.mocked(storage.queryNodes).mockResolvedValue([normal, dismissed]);
540
+ const result = await engine.feedback('s-spec', { includeDismissed: true });
541
+ expect(result).toHaveLength(2);
542
+ });
543
+ });
544
+ describe('unresolvedFeedback()', () => {
545
+ it('should get unresolved feedback', async () => {
546
+ const unresolved = createFeedback('f-1', 's-spec', { resolved: false });
547
+ const resolved = createFeedback('f-2', 's-spec', { resolved: true });
548
+ vi.mocked(storage.queryNodes).mockResolvedValue([unresolved, resolved]);
549
+ const result = await engine.unresolvedFeedback();
550
+ expect(result).toHaveLength(1);
551
+ expect(result[0].id).toBe('f-1');
552
+ });
553
+ it('should exclude dismissed feedback', async () => {
554
+ const unresolved = createFeedback('f-1', 's-spec', {
555
+ resolved: false,
556
+ dismissed: false,
557
+ });
558
+ const dismissed = createFeedback('f-2', 's-spec', {
559
+ resolved: false,
560
+ dismissed: true,
561
+ });
562
+ vi.mocked(storage.queryNodes).mockResolvedValue([unresolved, dismissed]);
563
+ const result = await engine.unresolvedFeedback();
564
+ expect(result).toHaveLength(1);
565
+ expect(result[0].id).toBe('f-1');
566
+ });
567
+ it('should filter by target', async () => {
568
+ const fb1 = createFeedback('f-1', 's-spec1', { resolved: false });
569
+ const fb2 = createFeedback('f-2', 's-spec2', { resolved: false });
570
+ vi.mocked(storage.queryNodes).mockResolvedValue([fb1, fb2]);
571
+ const result = await engine.unresolvedFeedback('s-spec1');
572
+ expect(result).toHaveLength(1);
573
+ expect(result[0].id).toBe('f-1');
574
+ });
575
+ });
576
+ // ============================================================================
577
+ // Cross-Provider Resolution Tests
578
+ // ============================================================================
579
+ describe('Cross-Provider Resolution', () => {
580
+ describe('with nodeResolver', () => {
581
+ it('blockers() should call resolver for external URIs', async () => {
582
+ const externalNode = {
583
+ id: 'x-ext1',
584
+ uuid: 'ext-uuid-1',
585
+ type: 'external',
586
+ title: 'External Blocker',
587
+ status: 'open',
588
+ uri: 'beads://./bd-blocker',
589
+ source: 'beads',
590
+ created_at: '2024-01-01T00:00:00Z',
591
+ updated_at: '2024-01-01T00:00:00Z',
592
+ };
593
+ // Setup mock resolver
594
+ const mockResolver = vi.fn().mockResolvedValue(externalNode);
595
+ // Create engine with resolver
596
+ const engineWithResolver = createQueryEngine({
597
+ storage,
598
+ nodeResolver: mockResolver,
599
+ });
600
+ // Setup storage mocks
601
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([
602
+ createEdge('e-1', 'beads://./bd-blocker', 'i-local', 'blocks'),
603
+ ]);
604
+ await engineWithResolver.blockers('i-local');
605
+ // Resolver should be called for external URI
606
+ expect(mockResolver).toHaveBeenCalledWith('beads://./bd-blocker');
607
+ // Note: External nodes don't appear in results because they don't
608
+ // parse as valid Node types (spec/issue/feedback). The resolver is
609
+ // still called to support ready() blocking checks.
610
+ });
611
+ it('blockers() should return local issues resolved via resolver', async () => {
612
+ // When resolver returns a valid issue type, it should be in results
613
+ const localBlocker = createIssue('i-blocker', 'Local Blocker');
614
+ const mockResolver = vi.fn().mockResolvedValue(localBlocker);
615
+ const engineWithResolver = createQueryEngine({
616
+ storage,
617
+ nodeResolver: mockResolver,
618
+ });
619
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([
620
+ createEdge('e-1', 'beads://./bd-blocker', 'i-local', 'blocks'),
621
+ ]);
622
+ const result = await engineWithResolver.blockers('i-local');
623
+ expect(mockResolver).toHaveBeenCalledWith('beads://./bd-blocker');
624
+ expect(result).toHaveLength(1);
625
+ expect(result[0].title).toBe('Local Blocker');
626
+ });
627
+ it('blockers() should use storage for local IDs even with resolver', async () => {
628
+ const localBlocker = createIssue('i-blocker', 'Local Blocker');
629
+ const mockResolver = vi.fn();
630
+ const engineWithResolver = createQueryEngine({
631
+ storage,
632
+ nodeResolver: mockResolver,
633
+ });
634
+ vi.mocked(storage.getNode).mockResolvedValue(localBlocker);
635
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([
636
+ createEdge('e-1', 'i-blocker', 'i-local', 'blocks'),
637
+ ]);
638
+ await engineWithResolver.blockers('i-local');
639
+ // Resolver should NOT be called for local IDs
640
+ expect(mockResolver).not.toHaveBeenCalled();
641
+ expect(storage.getNode).toHaveBeenCalledWith('i-blocker');
642
+ });
643
+ it('blocking() should call resolver for external URIs', async () => {
644
+ const externalNode = {
645
+ id: 'x-ext1',
646
+ uuid: 'ext-uuid-1',
647
+ type: 'external',
648
+ title: 'External Blocked',
649
+ status: 'open',
650
+ uri: 'beads://./bd-blocked',
651
+ source: 'beads',
652
+ created_at: '2024-01-01T00:00:00Z',
653
+ updated_at: '2024-01-01T00:00:00Z',
654
+ };
655
+ const mockResolver = vi.fn().mockResolvedValue(externalNode);
656
+ const engineWithResolver = createQueryEngine({
657
+ storage,
658
+ nodeResolver: mockResolver,
659
+ });
660
+ vi.mocked(storage.getEdgesFrom).mockResolvedValue([
661
+ createEdge('e-1', 'i-blocker', 'beads://./bd-blocked', 'blocks'),
662
+ ]);
663
+ await engineWithResolver.blocking('i-blocker');
664
+ // Resolver should be called for external URI
665
+ expect(mockResolver).toHaveBeenCalledWith('beads://./bd-blocked');
666
+ // Note: External nodes don't appear in results because they don't
667
+ // parse as valid Node types (spec/issue/feedback).
668
+ });
669
+ it('blocking() should return local issues resolved via resolver', async () => {
670
+ const localBlocked = createIssue('i-blocked', 'Local Blocked Issue');
671
+ const mockResolver = vi.fn().mockResolvedValue(localBlocked);
672
+ const engineWithResolver = createQueryEngine({
673
+ storage,
674
+ nodeResolver: mockResolver,
675
+ });
676
+ vi.mocked(storage.getEdgesFrom).mockResolvedValue([
677
+ createEdge('e-1', 'i-blocker', 'beads://./bd-blocked', 'blocks'),
678
+ ]);
679
+ const result = await engineWithResolver.blocking('i-blocker');
680
+ expect(mockResolver).toHaveBeenCalledWith('beads://./bd-blocked');
681
+ expect(result).toHaveLength(1);
682
+ expect(result[0].title).toBe('Local Blocked Issue');
683
+ });
684
+ it('ready() should consider external blockers when resolver provided', async () => {
685
+ const localIssue = createIssue('i-ready', 'Ready Issue');
686
+ const externalBlocker = {
687
+ id: 'x-ext1',
688
+ uuid: 'ext-uuid-1',
689
+ type: 'external',
690
+ title: 'External Blocker',
691
+ status: 'open', // Active blocker
692
+ uri: 'beads://./bd-blocker',
693
+ source: 'beads',
694
+ created_at: '2024-01-01T00:00:00Z',
695
+ updated_at: '2024-01-01T00:00:00Z',
696
+ };
697
+ const mockResolver = vi.fn().mockResolvedValue(externalBlocker);
698
+ const engineWithResolver = createQueryEngine({
699
+ storage,
700
+ nodeResolver: mockResolver,
701
+ });
702
+ vi.mocked(storage.queryNodes).mockResolvedValue([localIssue]);
703
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([
704
+ createEdge('e-1', 'beads://./bd-blocker', 'i-ready', 'blocks'),
705
+ ]);
706
+ const result = await engineWithResolver.ready();
707
+ // Issue should NOT be ready because external blocker is active
708
+ expect(result).toHaveLength(0);
709
+ });
710
+ it('ready() should include issue when external blocker is closed', async () => {
711
+ const localIssue = createIssue('i-ready', 'Ready Issue');
712
+ const closedBlocker = {
713
+ id: 'x-ext1',
714
+ uuid: 'ext-uuid-1',
715
+ type: 'external',
716
+ title: 'Closed External Blocker',
717
+ status: 'closed', // Closed - not blocking
718
+ uri: 'beads://./bd-blocker',
719
+ source: 'beads',
720
+ created_at: '2024-01-01T00:00:00Z',
721
+ updated_at: '2024-01-01T00:00:00Z',
722
+ };
723
+ const mockResolver = vi.fn().mockResolvedValue(closedBlocker);
724
+ const engineWithResolver = createQueryEngine({
725
+ storage,
726
+ nodeResolver: mockResolver,
727
+ });
728
+ vi.mocked(storage.queryNodes).mockResolvedValue([localIssue]);
729
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([
730
+ createEdge('e-1', 'beads://./bd-blocker', 'i-ready', 'blocks'),
731
+ ]);
732
+ const result = await engineWithResolver.ready();
733
+ // Issue should be ready because external blocker is closed
734
+ expect(result).toHaveLength(1);
735
+ expect(result[0].id).toBe('i-ready');
736
+ });
737
+ it('resolver returning null should skip the node', async () => {
738
+ const mockResolver = vi.fn().mockResolvedValue(null);
739
+ const engineWithResolver = createQueryEngine({
740
+ storage,
741
+ nodeResolver: mockResolver,
742
+ });
743
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([
744
+ createEdge('e-1', 'beads://./bd-missing', 'i-local', 'blocks'),
745
+ ]);
746
+ const result = await engineWithResolver.blockers('i-local');
747
+ expect(mockResolver).toHaveBeenCalledWith('beads://./bd-missing');
748
+ expect(result).toHaveLength(0);
749
+ });
750
+ });
751
+ describe('without nodeResolver (backward compatibility)', () => {
752
+ it('blockers() should skip external URIs that storage cannot find', async () => {
753
+ vi.mocked(storage.getNode).mockResolvedValue(null);
754
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([
755
+ createEdge('e-1', 'beads://./bd-blocker', 'i-local', 'blocks'),
756
+ ]);
757
+ const result = await engine.blockers('i-local');
758
+ expect(result).toHaveLength(0);
759
+ });
760
+ it('ready() should not be blocked by unresolvable external nodes', async () => {
761
+ const localIssue = createIssue('i-ready', 'Ready Issue');
762
+ vi.mocked(storage.queryNodes).mockResolvedValue([localIssue]);
763
+ vi.mocked(storage.getEdgesTo).mockResolvedValue([
764
+ createEdge('e-1', 'beads://./bd-unresolvable', 'i-ready', 'blocks'),
765
+ ]);
766
+ vi.mocked(storage.getNode).mockResolvedValue(null);
767
+ const result = await engine.ready();
768
+ // Issue should be ready because blocker cannot be resolved
769
+ expect(result).toHaveLength(1);
770
+ });
771
+ });
772
+ });
773
+ });
774
+ //# sourceMappingURL=query.test.js.map