opentasks 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (662) hide show
  1. package/README.md +344 -1
  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 +19 -1
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/cli.js +702 -23
  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 +228 -0
  27. package/dist/client/client.d.ts.map +1 -0
  28. package/dist/client/client.js +393 -0
  29. package/dist/client/client.js.map +1 -0
  30. package/dist/client/index.d.ts +11 -0
  31. package/dist/client/index.d.ts.map +1 -0
  32. package/dist/client/index.js +9 -0
  33. package/dist/client/index.js.map +1 -0
  34. package/dist/config/__tests__/defaults.test.d.ts +2 -0
  35. package/dist/config/__tests__/defaults.test.d.ts.map +1 -0
  36. package/dist/config/__tests__/defaults.test.js +57 -0
  37. package/dist/config/__tests__/defaults.test.js.map +1 -0
  38. package/dist/config/__tests__/env.test.d.ts +2 -0
  39. package/dist/config/__tests__/env.test.d.ts.map +1 -0
  40. package/dist/config/__tests__/env.test.js +136 -0
  41. package/dist/config/__tests__/env.test.js.map +1 -0
  42. package/dist/config/__tests__/index.test.d.ts +2 -0
  43. package/dist/config/__tests__/index.test.d.ts.map +1 -0
  44. package/dist/config/__tests__/index.test.js +113 -0
  45. package/dist/config/__tests__/index.test.js.map +1 -0
  46. package/dist/config/__tests__/loader.test.d.ts +2 -0
  47. package/dist/config/__tests__/loader.test.d.ts.map +1 -0
  48. package/dist/config/__tests__/loader.test.js +128 -0
  49. package/dist/config/__tests__/loader.test.js.map +1 -0
  50. package/dist/config/__tests__/merge.test.d.ts +2 -0
  51. package/dist/config/__tests__/merge.test.d.ts.map +1 -0
  52. package/dist/config/__tests__/merge.test.js +79 -0
  53. package/dist/config/__tests__/merge.test.js.map +1 -0
  54. package/dist/config/__tests__/schema.test.d.ts +2 -0
  55. package/dist/config/__tests__/schema.test.d.ts.map +1 -0
  56. package/dist/config/__tests__/schema.test.js +300 -0
  57. package/dist/config/__tests__/schema.test.js.map +1 -0
  58. package/dist/config/defaults.d.ts +13 -0
  59. package/dist/config/defaults.d.ts.map +1 -0
  60. package/dist/config/defaults.js +15 -0
  61. package/dist/config/defaults.js.map +1 -0
  62. package/dist/config/env.d.ts +14 -0
  63. package/dist/config/env.d.ts.map +1 -0
  64. package/dist/config/env.js +130 -0
  65. package/dist/config/env.js.map +1 -0
  66. package/dist/config/errors.d.ts +21 -0
  67. package/dist/config/errors.d.ts.map +1 -0
  68. package/dist/config/errors.js +30 -0
  69. package/dist/config/errors.js.map +1 -0
  70. package/dist/config/index.d.ts +21 -0
  71. package/dist/config/index.d.ts.map +1 -0
  72. package/dist/config/index.js +41 -0
  73. package/dist/config/index.js.map +1 -0
  74. package/dist/config/loader.d.ts +8 -0
  75. package/dist/config/loader.d.ts.map +1 -0
  76. package/dist/config/loader.js +87 -0
  77. package/dist/config/loader.js.map +1 -0
  78. package/dist/config/merge.d.ts +12 -0
  79. package/dist/config/merge.d.ts.map +1 -0
  80. package/dist/config/merge.js +54 -0
  81. package/dist/config/merge.js.map +1 -0
  82. package/dist/config/schema.d.ts +644 -0
  83. package/dist/config/schema.d.ts.map +1 -0
  84. package/dist/config/schema.js +491 -0
  85. package/dist/config/schema.js.map +1 -0
  86. package/dist/core/__tests__/conditional-redirects.test.d.ts +2 -0
  87. package/dist/core/__tests__/conditional-redirects.test.d.ts.map +1 -0
  88. package/dist/core/__tests__/conditional-redirects.test.js +83 -0
  89. package/dist/core/__tests__/conditional-redirects.test.js.map +1 -0
  90. package/dist/core/__tests__/connections.test.d.ts +2 -0
  91. package/dist/core/__tests__/connections.test.d.ts.map +1 -0
  92. package/dist/core/__tests__/connections.test.js +158 -0
  93. package/dist/core/__tests__/connections.test.js.map +1 -0
  94. package/dist/core/__tests__/hash.test.d.ts +2 -0
  95. package/dist/core/__tests__/hash.test.d.ts.map +1 -0
  96. package/dist/core/__tests__/hash.test.js +139 -0
  97. package/dist/core/__tests__/hash.test.js.map +1 -0
  98. package/dist/core/__tests__/id.test.d.ts +2 -0
  99. package/dist/core/__tests__/id.test.d.ts.map +1 -0
  100. package/dist/core/__tests__/id.test.js +142 -0
  101. package/dist/core/__tests__/id.test.js.map +1 -0
  102. package/dist/core/__tests__/location.test.d.ts +2 -0
  103. package/dist/core/__tests__/location.test.d.ts.map +1 -0
  104. package/dist/core/__tests__/location.test.js +77 -0
  105. package/dist/core/__tests__/location.test.js.map +1 -0
  106. package/dist/core/__tests__/merge-driver.test.d.ts +2 -0
  107. package/dist/core/__tests__/merge-driver.test.d.ts.map +1 -0
  108. package/dist/core/__tests__/merge-driver.test.js +218 -0
  109. package/dist/core/__tests__/merge-driver.test.js.map +1 -0
  110. package/dist/core/__tests__/redirects.test.d.ts +2 -0
  111. package/dist/core/__tests__/redirects.test.d.ts.map +1 -0
  112. package/dist/core/__tests__/redirects.test.js +123 -0
  113. package/dist/core/__tests__/redirects.test.js.map +1 -0
  114. package/dist/core/__tests__/resolve-location-target.test.d.ts +8 -0
  115. package/dist/core/__tests__/resolve-location-target.test.d.ts.map +1 -0
  116. package/dist/core/__tests__/resolve-location-target.test.js +303 -0
  117. package/dist/core/__tests__/resolve-location-target.test.js.map +1 -0
  118. package/dist/core/__tests__/uri.test.d.ts +2 -0
  119. package/dist/core/__tests__/uri.test.d.ts.map +1 -0
  120. package/dist/core/__tests__/uri.test.js +159 -0
  121. package/dist/core/__tests__/uri.test.js.map +1 -0
  122. package/dist/core/__tests__/worktree.test.d.ts +2 -0
  123. package/dist/core/__tests__/worktree.test.d.ts.map +1 -0
  124. package/dist/core/__tests__/worktree.test.js +120 -0
  125. package/dist/core/__tests__/worktree.test.js.map +1 -0
  126. package/dist/core/conditional-redirects.d.ts +42 -0
  127. package/dist/core/conditional-redirects.d.ts.map +1 -0
  128. package/dist/core/conditional-redirects.js +59 -0
  129. package/dist/core/conditional-redirects.js.map +1 -0
  130. package/dist/core/connections.d.ts +87 -0
  131. package/dist/core/connections.d.ts.map +1 -0
  132. package/dist/core/connections.js +160 -0
  133. package/dist/core/connections.js.map +1 -0
  134. package/dist/core/discover.d.ts +39 -0
  135. package/dist/core/discover.d.ts.map +1 -0
  136. package/dist/core/discover.js +136 -0
  137. package/dist/core/discover.js.map +1 -0
  138. package/dist/core/hash.d.ts +25 -0
  139. package/dist/core/hash.d.ts.map +1 -0
  140. package/dist/core/hash.js +62 -0
  141. package/dist/core/hash.js.map +1 -0
  142. package/dist/core/id.d.ts +79 -0
  143. package/dist/core/id.d.ts.map +1 -0
  144. package/dist/core/id.js +141 -0
  145. package/dist/core/id.js.map +1 -0
  146. package/dist/core/index.d.ts +15 -0
  147. package/dist/core/index.d.ts.map +1 -0
  148. package/dist/core/index.js +24 -0
  149. package/dist/core/index.js.map +1 -0
  150. package/dist/core/location.d.ts +70 -0
  151. package/dist/core/location.d.ts.map +1 -0
  152. package/dist/core/location.js +121 -0
  153. package/dist/core/location.js.map +1 -0
  154. package/dist/core/merge-driver.d.ts +50 -0
  155. package/dist/core/merge-driver.d.ts.map +1 -0
  156. package/dist/core/merge-driver.js +258 -0
  157. package/dist/core/merge-driver.js.map +1 -0
  158. package/dist/core/redirects.d.ts +91 -0
  159. package/dist/core/redirects.d.ts.map +1 -0
  160. package/dist/core/redirects.js +113 -0
  161. package/dist/core/redirects.js.map +1 -0
  162. package/dist/core/uri.d.ts +105 -0
  163. package/dist/core/uri.d.ts.map +1 -0
  164. package/dist/core/uri.js +190 -0
  165. package/dist/core/uri.js.map +1 -0
  166. package/dist/core/worktree.d.ts +106 -0
  167. package/dist/core/worktree.d.ts.map +1 -0
  168. package/dist/core/worktree.js +394 -0
  169. package/dist/core/worktree.js.map +1 -0
  170. package/dist/daemon/__tests__/flush.test.d.ts +5 -0
  171. package/dist/daemon/__tests__/flush.test.d.ts.map +1 -0
  172. package/dist/daemon/__tests__/flush.test.js +213 -0
  173. package/dist/daemon/__tests__/flush.test.js.map +1 -0
  174. package/dist/daemon/__tests__/integration.test.d.ts +7 -0
  175. package/dist/daemon/__tests__/integration.test.d.ts.map +1 -0
  176. package/dist/daemon/__tests__/integration.test.js +276 -0
  177. package/dist/daemon/__tests__/integration.test.js.map +1 -0
  178. package/dist/daemon/__tests__/ipc.test.d.ts +5 -0
  179. package/dist/daemon/__tests__/ipc.test.d.ts.map +1 -0
  180. package/dist/daemon/__tests__/ipc.test.js +314 -0
  181. package/dist/daemon/__tests__/ipc.test.js.map +1 -0
  182. package/dist/daemon/__tests__/lifecycle.test.d.ts +5 -0
  183. package/dist/daemon/__tests__/lifecycle.test.d.ts.map +1 -0
  184. package/dist/daemon/__tests__/lifecycle.test.js +301 -0
  185. package/dist/daemon/__tests__/lifecycle.test.js.map +1 -0
  186. package/dist/daemon/__tests__/lock.test.d.ts +5 -0
  187. package/dist/daemon/__tests__/lock.test.d.ts.map +1 -0
  188. package/dist/daemon/__tests__/lock.test.js +192 -0
  189. package/dist/daemon/__tests__/lock.test.js.map +1 -0
  190. package/dist/daemon/__tests__/methods/graph.test.d.ts +5 -0
  191. package/dist/daemon/__tests__/methods/graph.test.d.ts.map +1 -0
  192. package/dist/daemon/__tests__/methods/graph.test.js +309 -0
  193. package/dist/daemon/__tests__/methods/graph.test.js.map +1 -0
  194. package/dist/daemon/__tests__/methods/provider.test.d.ts +7 -0
  195. package/dist/daemon/__tests__/methods/provider.test.d.ts.map +1 -0
  196. package/dist/daemon/__tests__/methods/provider.test.js +181 -0
  197. package/dist/daemon/__tests__/methods/provider.test.js.map +1 -0
  198. package/dist/daemon/__tests__/methods/tools.test.d.ts +5 -0
  199. package/dist/daemon/__tests__/methods/tools.test.d.ts.map +1 -0
  200. package/dist/daemon/__tests__/methods/tools.test.js +587 -0
  201. package/dist/daemon/__tests__/methods/tools.test.js.map +1 -0
  202. package/dist/daemon/__tests__/multi-location.test.d.ts +8 -0
  203. package/dist/daemon/__tests__/multi-location.test.d.ts.map +1 -0
  204. package/dist/daemon/__tests__/multi-location.test.js +669 -0
  205. package/dist/daemon/__tests__/multi-location.test.js.map +1 -0
  206. package/dist/daemon/__tests__/registry.test.d.ts +5 -0
  207. package/dist/daemon/__tests__/registry.test.d.ts.map +1 -0
  208. package/dist/daemon/__tests__/registry.test.js +208 -0
  209. package/dist/daemon/__tests__/registry.test.js.map +1 -0
  210. package/dist/daemon/__tests__/watcher.test.d.ts +5 -0
  211. package/dist/daemon/__tests__/watcher.test.d.ts.map +1 -0
  212. package/dist/daemon/__tests__/watcher.test.js +234 -0
  213. package/dist/daemon/__tests__/watcher.test.js.map +1 -0
  214. package/dist/daemon/entire-linker.d.ts +68 -0
  215. package/dist/daemon/entire-linker.d.ts.map +1 -0
  216. package/dist/daemon/entire-linker.js +439 -0
  217. package/dist/daemon/entire-linker.js.map +1 -0
  218. package/dist/daemon/entire-watcher.d.ts +66 -0
  219. package/dist/daemon/entire-watcher.d.ts.map +1 -0
  220. package/dist/daemon/entire-watcher.js +258 -0
  221. package/dist/daemon/entire-watcher.js.map +1 -0
  222. package/dist/daemon/factory.d.ts +59 -0
  223. package/dist/daemon/factory.d.ts.map +1 -0
  224. package/dist/daemon/factory.js +72 -0
  225. package/dist/daemon/factory.js.map +1 -0
  226. package/dist/daemon/flush.d.ts +51 -0
  227. package/dist/daemon/flush.d.ts.map +1 -0
  228. package/dist/daemon/flush.js +89 -0
  229. package/dist/daemon/flush.js.map +1 -0
  230. package/dist/daemon/index.d.ts +39 -0
  231. package/dist/daemon/index.d.ts.map +1 -0
  232. package/dist/daemon/index.js +24 -0
  233. package/dist/daemon/index.js.map +1 -0
  234. package/dist/daemon/ipc.d.ts +97 -0
  235. package/dist/daemon/ipc.d.ts.map +1 -0
  236. package/dist/daemon/ipc.js +304 -0
  237. package/dist/daemon/ipc.js.map +1 -0
  238. package/dist/daemon/lifecycle.d.ts +85 -0
  239. package/dist/daemon/lifecycle.d.ts.map +1 -0
  240. package/dist/daemon/lifecycle.js +754 -0
  241. package/dist/daemon/lifecycle.js.map +1 -0
  242. package/dist/daemon/location-state.d.ts +85 -0
  243. package/dist/daemon/location-state.d.ts.map +1 -0
  244. package/dist/daemon/location-state.js +291 -0
  245. package/dist/daemon/location-state.js.map +1 -0
  246. package/dist/daemon/lock.d.ts +29 -0
  247. package/dist/daemon/lock.d.ts.map +1 -0
  248. package/dist/daemon/lock.js +131 -0
  249. package/dist/daemon/lock.js.map +1 -0
  250. package/dist/daemon/methods/__tests__/graph.test.d.ts +5 -0
  251. package/dist/daemon/methods/__tests__/graph.test.d.ts.map +1 -0
  252. package/dist/daemon/methods/__tests__/graph.test.js +274 -0
  253. package/dist/daemon/methods/__tests__/graph.test.js.map +1 -0
  254. package/dist/daemon/methods/__tests__/provider.test.d.ts +5 -0
  255. package/dist/daemon/methods/__tests__/provider.test.d.ts.map +1 -0
  256. package/dist/daemon/methods/__tests__/provider.test.js +184 -0
  257. package/dist/daemon/methods/__tests__/provider.test.js.map +1 -0
  258. package/dist/daemon/methods/__tests__/tools.test.d.ts +5 -0
  259. package/dist/daemon/methods/__tests__/tools.test.d.ts.map +1 -0
  260. package/dist/daemon/methods/__tests__/tools.test.js +295 -0
  261. package/dist/daemon/methods/__tests__/tools.test.js.map +1 -0
  262. package/dist/daemon/methods/archive.d.ts +22 -0
  263. package/dist/daemon/methods/archive.d.ts.map +1 -0
  264. package/dist/daemon/methods/archive.js +107 -0
  265. package/dist/daemon/methods/archive.js.map +1 -0
  266. package/dist/daemon/methods/graph.d.ts +26 -0
  267. package/dist/daemon/methods/graph.d.ts.map +1 -0
  268. package/dist/daemon/methods/graph.js +157 -0
  269. package/dist/daemon/methods/graph.js.map +1 -0
  270. package/dist/daemon/methods/lifecycle.d.ts +54 -0
  271. package/dist/daemon/methods/lifecycle.d.ts.map +1 -0
  272. package/dist/daemon/methods/lifecycle.js +46 -0
  273. package/dist/daemon/methods/lifecycle.js.map +1 -0
  274. package/dist/daemon/methods/location.d.ts +24 -0
  275. package/dist/daemon/methods/location.d.ts.map +1 -0
  276. package/dist/daemon/methods/location.js +72 -0
  277. package/dist/daemon/methods/location.js.map +1 -0
  278. package/dist/daemon/methods/provider.d.ts +22 -0
  279. package/dist/daemon/methods/provider.d.ts.map +1 -0
  280. package/dist/daemon/methods/provider.js +72 -0
  281. package/dist/daemon/methods/provider.js.map +1 -0
  282. package/dist/daemon/methods/tools.d.ts +23 -0
  283. package/dist/daemon/methods/tools.d.ts.map +1 -0
  284. package/dist/daemon/methods/tools.js +111 -0
  285. package/dist/daemon/methods/tools.js.map +1 -0
  286. package/dist/daemon/registry.d.ts +35 -0
  287. package/dist/daemon/registry.d.ts.map +1 -0
  288. package/dist/daemon/registry.js +189 -0
  289. package/dist/daemon/registry.js.map +1 -0
  290. package/dist/daemon/types.d.ts +101 -0
  291. package/dist/daemon/types.d.ts.map +1 -0
  292. package/dist/daemon/types.js +19 -0
  293. package/dist/daemon/types.js.map +1 -0
  294. package/dist/daemon/watcher.d.ts +62 -0
  295. package/dist/daemon/watcher.d.ts.map +1 -0
  296. package/dist/daemon/watcher.js +142 -0
  297. package/dist/daemon/watcher.js.map +1 -0
  298. package/dist/graph/EdgeTypeRegistry.d.ts +134 -0
  299. package/dist/graph/EdgeTypeRegistry.d.ts.map +1 -0
  300. package/dist/graph/EdgeTypeRegistry.js +255 -0
  301. package/dist/graph/EdgeTypeRegistry.js.map +1 -0
  302. package/dist/graph/FederatedGraph.d.ts +296 -0
  303. package/dist/graph/FederatedGraph.d.ts.map +1 -0
  304. package/dist/graph/FederatedGraph.js +406 -0
  305. package/dist/graph/FederatedGraph.js.map +1 -0
  306. package/dist/graph/GraphologyAdapter.d.ts +151 -0
  307. package/dist/graph/GraphologyAdapter.d.ts.map +1 -0
  308. package/dist/graph/GraphologyAdapter.js +209 -0
  309. package/dist/graph/GraphologyAdapter.js.map +1 -0
  310. package/dist/graph/HydratingFederatedGraph.d.ts +151 -0
  311. package/dist/graph/HydratingFederatedGraph.d.ts.map +1 -0
  312. package/dist/graph/HydratingFederatedGraph.js +327 -0
  313. package/dist/graph/HydratingFederatedGraph.js.map +1 -0
  314. package/dist/graph/__tests__/EdgeTypeRegistry.test.d.ts +2 -0
  315. package/dist/graph/__tests__/EdgeTypeRegistry.test.d.ts.map +1 -0
  316. package/dist/graph/__tests__/EdgeTypeRegistry.test.js +212 -0
  317. package/dist/graph/__tests__/EdgeTypeRegistry.test.js.map +1 -0
  318. package/dist/graph/__tests__/FederatedGraph.test.d.ts +2 -0
  319. package/dist/graph/__tests__/FederatedGraph.test.d.ts.map +1 -0
  320. package/dist/graph/__tests__/FederatedGraph.test.js +661 -0
  321. package/dist/graph/__tests__/FederatedGraph.test.js.map +1 -0
  322. package/dist/graph/__tests__/GraphologyAdapter.test.d.ts +2 -0
  323. package/dist/graph/__tests__/GraphologyAdapter.test.d.ts.map +1 -0
  324. package/dist/graph/__tests__/GraphologyAdapter.test.js +326 -0
  325. package/dist/graph/__tests__/GraphologyAdapter.test.js.map +1 -0
  326. package/dist/graph/__tests__/HydratingFederatedGraph.test.d.ts +2 -0
  327. package/dist/graph/__tests__/HydratingFederatedGraph.test.d.ts.map +1 -0
  328. package/dist/graph/__tests__/HydratingFederatedGraph.test.js +587 -0
  329. package/dist/graph/__tests__/HydratingFederatedGraph.test.js.map +1 -0
  330. package/dist/graph/__tests__/debounce.test.d.ts +5 -0
  331. package/dist/graph/__tests__/debounce.test.d.ts.map +1 -0
  332. package/dist/graph/__tests__/debounce.test.js +195 -0
  333. package/dist/graph/__tests__/debounce.test.js.map +1 -0
  334. package/dist/graph/__tests__/edge-cases.test.d.ts +8 -0
  335. package/dist/graph/__tests__/edge-cases.test.d.ts.map +1 -0
  336. package/dist/graph/__tests__/edge-cases.test.js +472 -0
  337. package/dist/graph/__tests__/edge-cases.test.js.map +1 -0
  338. package/dist/graph/__tests__/expansion.test.d.ts +2 -0
  339. package/dist/graph/__tests__/expansion.test.d.ts.map +1 -0
  340. package/dist/graph/__tests__/expansion.test.js +105 -0
  341. package/dist/graph/__tests__/expansion.test.js.map +1 -0
  342. package/dist/graph/__tests__/provider-store.test.d.ts +5 -0
  343. package/dist/graph/__tests__/provider-store.test.d.ts.map +1 -0
  344. package/dist/graph/__tests__/provider-store.test.js +791 -0
  345. package/dist/graph/__tests__/provider-store.test.js.map +1 -0
  346. package/dist/graph/__tests__/query.test.d.ts +5 -0
  347. package/dist/graph/__tests__/query.test.d.ts.map +1 -0
  348. package/dist/graph/__tests__/query.test.js +774 -0
  349. package/dist/graph/__tests__/query.test.js.map +1 -0
  350. package/dist/graph/__tests__/store.test.d.ts +5 -0
  351. package/dist/graph/__tests__/store.test.d.ts.map +1 -0
  352. package/dist/graph/__tests__/store.test.js +489 -0
  353. package/dist/graph/__tests__/store.test.js.map +1 -0
  354. package/dist/graph/__tests__/sync.test.d.ts +5 -0
  355. package/dist/graph/__tests__/sync.test.d.ts.map +1 -0
  356. package/dist/graph/__tests__/sync.test.js +129 -0
  357. package/dist/graph/__tests__/sync.test.js.map +1 -0
  358. package/dist/graph/__tests__/validation.test.d.ts +2 -0
  359. package/dist/graph/__tests__/validation.test.d.ts.map +1 -0
  360. package/dist/graph/__tests__/validation.test.js +521 -0
  361. package/dist/graph/__tests__/validation.test.js.map +1 -0
  362. package/dist/graph/coordination.d.ts +190 -0
  363. package/dist/graph/coordination.d.ts.map +1 -0
  364. package/dist/graph/coordination.js +180 -0
  365. package/dist/graph/coordination.js.map +1 -0
  366. package/dist/graph/debounce.d.ts +47 -0
  367. package/dist/graph/debounce.d.ts.map +1 -0
  368. package/dist/graph/debounce.js +95 -0
  369. package/dist/graph/debounce.js.map +1 -0
  370. package/dist/graph/expansion.d.ts +64 -0
  371. package/dist/graph/expansion.d.ts.map +1 -0
  372. package/dist/graph/expansion.js +205 -0
  373. package/dist/graph/expansion.js.map +1 -0
  374. package/dist/graph/history.d.ts +186 -0
  375. package/dist/graph/history.d.ts.map +1 -0
  376. package/dist/graph/history.js +155 -0
  377. package/dist/graph/history.js.map +1 -0
  378. package/dist/graph/index.d.ts +35 -0
  379. package/dist/graph/index.d.ts.map +1 -0
  380. package/dist/graph/index.js +22 -0
  381. package/dist/graph/index.js.map +1 -0
  382. package/dist/graph/provider-store.d.ts +211 -0
  383. package/dist/graph/provider-store.d.ts.map +1 -0
  384. package/dist/graph/provider-store.js +568 -0
  385. package/dist/graph/provider-store.js.map +1 -0
  386. package/dist/graph/query.d.ts +90 -0
  387. package/dist/graph/query.d.ts.map +1 -0
  388. package/dist/graph/query.js +463 -0
  389. package/dist/graph/query.js.map +1 -0
  390. package/dist/graph/store.d.ts +71 -0
  391. package/dist/graph/store.d.ts.map +1 -0
  392. package/dist/graph/store.js +530 -0
  393. package/dist/graph/store.js.map +1 -0
  394. package/dist/graph/sync.d.ts +48 -0
  395. package/dist/graph/sync.d.ts.map +1 -0
  396. package/dist/graph/sync.js +60 -0
  397. package/dist/graph/sync.js.map +1 -0
  398. package/dist/graph/types.d.ts +289 -0
  399. package/dist/graph/types.d.ts.map +1 -0
  400. package/dist/graph/types.js +19 -0
  401. package/dist/graph/types.js.map +1 -0
  402. package/dist/graph/validation.d.ts +26 -0
  403. package/dist/graph/validation.d.ts.map +1 -0
  404. package/dist/graph/validation.js +338 -0
  405. package/dist/graph/validation.js.map +1 -0
  406. package/dist/index.d.ts +25 -14
  407. package/dist/index.d.ts.map +1 -1
  408. package/dist/index.js +22 -25
  409. package/dist/index.js.map +1 -1
  410. package/dist/materialization/archiver.d.ts +12 -0
  411. package/dist/materialization/archiver.d.ts.map +1 -0
  412. package/dist/materialization/archiver.js +273 -0
  413. package/dist/materialization/archiver.js.map +1 -0
  414. package/dist/materialization/git-archive-store.d.ts +17 -0
  415. package/dist/materialization/git-archive-store.d.ts.map +1 -0
  416. package/dist/materialization/git-archive-store.js +509 -0
  417. package/dist/materialization/git-archive-store.js.map +1 -0
  418. package/dist/materialization/git-remote-store.d.ts +22 -0
  419. package/dist/materialization/git-remote-store.d.ts.map +1 -0
  420. package/dist/materialization/git-remote-store.js +448 -0
  421. package/dist/materialization/git-remote-store.js.map +1 -0
  422. package/dist/materialization/graph-id.d.ts +53 -0
  423. package/dist/materialization/graph-id.d.ts.map +1 -0
  424. package/dist/materialization/graph-id.js +163 -0
  425. package/dist/materialization/graph-id.js.map +1 -0
  426. package/dist/materialization/http-remote-store.d.ts +12 -0
  427. package/dist/materialization/http-remote-store.d.ts.map +1 -0
  428. package/dist/materialization/http-remote-store.js +143 -0
  429. package/dist/materialization/http-remote-store.js.map +1 -0
  430. package/dist/materialization/index.d.ts +15 -0
  431. package/dist/materialization/index.d.ts.map +1 -0
  432. package/dist/materialization/index.js +17 -0
  433. package/dist/materialization/index.js.map +1 -0
  434. package/dist/materialization/remote-store-factory.d.ts +17 -0
  435. package/dist/materialization/remote-store-factory.d.ts.map +1 -0
  436. package/dist/materialization/remote-store-factory.js +46 -0
  437. package/dist/materialization/remote-store-factory.js.map +1 -0
  438. package/dist/materialization/snapshot.d.ts +34 -0
  439. package/dist/materialization/snapshot.d.ts.map +1 -0
  440. package/dist/materialization/snapshot.js +177 -0
  441. package/dist/materialization/snapshot.js.map +1 -0
  442. package/dist/materialization/types.d.ts +300 -0
  443. package/dist/materialization/types.d.ts.map +1 -0
  444. package/dist/materialization/types.js +17 -0
  445. package/dist/materialization/types.js.map +1 -0
  446. package/dist/providers/__tests__/beads.test.d.ts +5 -0
  447. package/dist/providers/__tests__/beads.test.d.ts.map +1 -0
  448. package/dist/providers/__tests__/beads.test.js +591 -0
  449. package/dist/providers/__tests__/beads.test.js.map +1 -0
  450. package/dist/providers/__tests__/claude-tasks.test.d.ts +5 -0
  451. package/dist/providers/__tests__/claude-tasks.test.d.ts.map +1 -0
  452. package/dist/providers/__tests__/claude-tasks.test.js +392 -0
  453. package/dist/providers/__tests__/claude-tasks.test.js.map +1 -0
  454. package/dist/providers/__tests__/from-config.test.d.ts +5 -0
  455. package/dist/providers/__tests__/from-config.test.d.ts.map +1 -0
  456. package/dist/providers/__tests__/from-config.test.js +152 -0
  457. package/dist/providers/__tests__/from-config.test.js.map +1 -0
  458. package/dist/providers/__tests__/materialization.test.d.ts +5 -0
  459. package/dist/providers/__tests__/materialization.test.d.ts.map +1 -0
  460. package/dist/providers/__tests__/materialization.test.js +407 -0
  461. package/dist/providers/__tests__/materialization.test.js.map +1 -0
  462. package/dist/providers/__tests__/native.test.d.ts +5 -0
  463. package/dist/providers/__tests__/native.test.d.ts.map +1 -0
  464. package/dist/providers/__tests__/native.test.js +566 -0
  465. package/dist/providers/__tests__/native.test.js.map +1 -0
  466. package/dist/providers/__tests__/registry.test.d.ts +5 -0
  467. package/dist/providers/__tests__/registry.test.d.ts.map +1 -0
  468. package/dist/providers/__tests__/registry.test.js +183 -0
  469. package/dist/providers/__tests__/registry.test.js.map +1 -0
  470. package/dist/providers/beads.d.ts +46 -0
  471. package/dist/providers/beads.d.ts.map +1 -0
  472. package/dist/providers/beads.js +865 -0
  473. package/dist/providers/beads.js.map +1 -0
  474. package/dist/providers/claude-tasks.d.ts +56 -0
  475. package/dist/providers/claude-tasks.d.ts.map +1 -0
  476. package/dist/providers/claude-tasks.js +282 -0
  477. package/dist/providers/claude-tasks.js.map +1 -0
  478. package/dist/providers/entire.d.ts +88 -0
  479. package/dist/providers/entire.d.ts.map +1 -0
  480. package/dist/providers/entire.js +409 -0
  481. package/dist/providers/entire.js.map +1 -0
  482. package/dist/providers/from-config.d.ts +47 -0
  483. package/dist/providers/from-config.d.ts.map +1 -0
  484. package/dist/providers/from-config.js +150 -0
  485. package/dist/providers/from-config.js.map +1 -0
  486. package/dist/providers/index.d.ts +26 -0
  487. package/dist/providers/index.d.ts.map +1 -0
  488. package/dist/providers/index.js +29 -0
  489. package/dist/providers/index.js.map +1 -0
  490. package/dist/providers/location.d.ts +44 -0
  491. package/dist/providers/location.d.ts.map +1 -0
  492. package/dist/providers/location.js +157 -0
  493. package/dist/providers/location.js.map +1 -0
  494. package/dist/providers/materialization.d.ts +46 -0
  495. package/dist/providers/materialization.d.ts.map +1 -0
  496. package/dist/providers/materialization.js +237 -0
  497. package/dist/providers/materialization.js.map +1 -0
  498. package/dist/providers/native.d.ts +32 -0
  499. package/dist/providers/native.d.ts.map +1 -0
  500. package/dist/providers/native.js +552 -0
  501. package/dist/providers/native.js.map +1 -0
  502. package/dist/providers/registry.d.ts +11 -0
  503. package/dist/providers/registry.d.ts.map +1 -0
  504. package/dist/providers/registry.js +97 -0
  505. package/dist/providers/registry.js.map +1 -0
  506. package/dist/providers/sudocode.d.ts +49 -0
  507. package/dist/providers/sudocode.d.ts.map +1 -0
  508. package/dist/providers/sudocode.js +945 -0
  509. package/dist/providers/sudocode.js.map +1 -0
  510. package/dist/providers/sync.d.ts +299 -0
  511. package/dist/providers/sync.d.ts.map +1 -0
  512. package/dist/providers/sync.js +93 -0
  513. package/dist/providers/sync.js.map +1 -0
  514. package/dist/providers/traits/RelationshipQueryable.d.ts +129 -0
  515. package/dist/providers/traits/RelationshipQueryable.d.ts.map +1 -0
  516. package/dist/providers/traits/RelationshipQueryable.js +68 -0
  517. package/dist/providers/traits/RelationshipQueryable.js.map +1 -0
  518. package/dist/providers/traits/TaskManageable.d.ts +157 -0
  519. package/dist/providers/traits/TaskManageable.d.ts.map +1 -0
  520. package/dist/providers/traits/TaskManageable.js +37 -0
  521. package/dist/providers/traits/TaskManageable.js.map +1 -0
  522. package/dist/providers/traits/Watchable.d.ts +216 -0
  523. package/dist/providers/traits/Watchable.d.ts.map +1 -0
  524. package/dist/providers/traits/Watchable.js +37 -0
  525. package/dist/providers/traits/Watchable.js.map +1 -0
  526. package/dist/providers/traits/__tests__/RelationshipQueryable.test.d.ts +2 -0
  527. package/dist/providers/traits/__tests__/RelationshipQueryable.test.d.ts.map +1 -0
  528. package/dist/providers/traits/__tests__/RelationshipQueryable.test.js +169 -0
  529. package/dist/providers/traits/__tests__/RelationshipQueryable.test.js.map +1 -0
  530. package/dist/providers/traits/__tests__/TaskManageable.test.d.ts +2 -0
  531. package/dist/providers/traits/__tests__/TaskManageable.test.d.ts.map +1 -0
  532. package/dist/providers/traits/__tests__/TaskManageable.test.js +172 -0
  533. package/dist/providers/traits/__tests__/TaskManageable.test.js.map +1 -0
  534. package/dist/providers/traits/index.d.ts +13 -0
  535. package/dist/providers/traits/index.d.ts.map +1 -0
  536. package/dist/providers/traits/index.js +10 -0
  537. package/dist/providers/traits/index.js.map +1 -0
  538. package/dist/providers/types.d.ts +284 -0
  539. package/dist/providers/types.d.ts.map +1 -0
  540. package/dist/providers/types.js +30 -0
  541. package/dist/providers/types.js.map +1 -0
  542. package/dist/schema/__tests__/validation.test.d.ts +2 -0
  543. package/dist/schema/__tests__/validation.test.d.ts.map +1 -0
  544. package/dist/schema/__tests__/validation.test.js +241 -0
  545. package/dist/schema/__tests__/validation.test.js.map +1 -0
  546. package/dist/schema/base.d.ts +68 -0
  547. package/dist/schema/base.d.ts.map +1 -0
  548. package/dist/schema/base.js +5 -0
  549. package/dist/schema/base.js.map +1 -0
  550. package/dist/schema/edges.d.ts +49 -0
  551. package/dist/schema/edges.d.ts.map +1 -0
  552. package/dist/schema/edges.js +9 -0
  553. package/dist/schema/edges.js.map +1 -0
  554. package/dist/schema/index.d.ts +11 -0
  555. package/dist/schema/index.d.ts.map +1 -0
  556. package/dist/schema/index.js +8 -0
  557. package/dist/schema/index.js.map +1 -0
  558. package/dist/schema/nodes.d.ts +97 -0
  559. package/dist/schema/nodes.d.ts.map +1 -0
  560. package/dist/schema/nodes.js +5 -0
  561. package/dist/schema/nodes.js.map +1 -0
  562. package/dist/schema/storage.d.ts +107 -0
  563. package/dist/schema/storage.d.ts.map +1 -0
  564. package/dist/schema/storage.js +10 -0
  565. package/dist/schema/storage.js.map +1 -0
  566. package/dist/schema/validation.d.ts +61 -0
  567. package/dist/schema/validation.d.ts.map +1 -0
  568. package/dist/schema/validation.js +170 -0
  569. package/dist/schema/validation.js.map +1 -0
  570. package/dist/storage/__tests__/atomic-write.test.d.ts +5 -0
  571. package/dist/storage/__tests__/atomic-write.test.d.ts.map +1 -0
  572. package/dist/storage/__tests__/atomic-write.test.js +170 -0
  573. package/dist/storage/__tests__/atomic-write.test.js.map +1 -0
  574. package/dist/storage/__tests__/file-lock.test.d.ts +2 -0
  575. package/dist/storage/__tests__/file-lock.test.d.ts.map +1 -0
  576. package/dist/storage/__tests__/file-lock.test.js +89 -0
  577. package/dist/storage/__tests__/file-lock.test.js.map +1 -0
  578. package/dist/storage/__tests__/jsonl.test.d.ts +2 -0
  579. package/dist/storage/__tests__/jsonl.test.d.ts.map +1 -0
  580. package/dist/storage/__tests__/jsonl.test.js +228 -0
  581. package/dist/storage/__tests__/jsonl.test.js.map +1 -0
  582. package/dist/storage/__tests__/locked-writer.test.d.ts +2 -0
  583. package/dist/storage/__tests__/locked-writer.test.d.ts.map +1 -0
  584. package/dist/storage/__tests__/locked-writer.test.js +109 -0
  585. package/dist/storage/__tests__/locked-writer.test.js.map +1 -0
  586. package/dist/storage/__tests__/sqlite.test.d.ts +2 -0
  587. package/dist/storage/__tests__/sqlite.test.d.ts.map +1 -0
  588. package/dist/storage/__tests__/sqlite.test.js +470 -0
  589. package/dist/storage/__tests__/sqlite.test.js.map +1 -0
  590. package/dist/storage/atomic-write.d.ts +38 -0
  591. package/dist/storage/atomic-write.d.ts.map +1 -0
  592. package/dist/storage/atomic-write.js +83 -0
  593. package/dist/storage/atomic-write.js.map +1 -0
  594. package/dist/storage/file-lock.d.ts +66 -0
  595. package/dist/storage/file-lock.d.ts.map +1 -0
  596. package/dist/storage/file-lock.js +176 -0
  597. package/dist/storage/file-lock.js.map +1 -0
  598. package/dist/storage/index.d.ts +11 -0
  599. package/dist/storage/index.d.ts.map +1 -0
  600. package/dist/storage/index.js +13 -0
  601. package/dist/storage/index.js.map +1 -0
  602. package/dist/storage/interface.d.ts +219 -0
  603. package/dist/storage/interface.d.ts.map +1 -0
  604. package/dist/storage/interface.js +22 -0
  605. package/dist/storage/interface.js.map +1 -0
  606. package/dist/storage/jsonl.d.ts +106 -0
  607. package/dist/storage/jsonl.d.ts.map +1 -0
  608. package/dist/storage/jsonl.js +218 -0
  609. package/dist/storage/jsonl.js.map +1 -0
  610. package/dist/storage/locked-writer.d.ts +67 -0
  611. package/dist/storage/locked-writer.d.ts.map +1 -0
  612. package/dist/storage/locked-writer.js +105 -0
  613. package/dist/storage/locked-writer.js.map +1 -0
  614. package/dist/storage/sqlite-schema.d.ts +48 -0
  615. package/dist/storage/sqlite-schema.d.ts.map +1 -0
  616. package/dist/storage/sqlite-schema.js +169 -0
  617. package/dist/storage/sqlite-schema.js.map +1 -0
  618. package/dist/storage/sqlite.d.ts +73 -0
  619. package/dist/storage/sqlite.d.ts.map +1 -0
  620. package/dist/storage/sqlite.js +698 -0
  621. package/dist/storage/sqlite.js.map +1 -0
  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/annotate.d.ts +17 -0
  639. package/dist/tools/annotate.d.ts.map +1 -0
  640. package/dist/tools/annotate.js +218 -0
  641. package/dist/tools/annotate.js.map +1 -0
  642. package/dist/tools/index.d.ts +14 -0
  643. package/dist/tools/index.d.ts.map +1 -0
  644. package/dist/tools/index.js +14 -0
  645. package/dist/tools/index.js.map +1 -0
  646. package/dist/tools/link.d.ts +17 -0
  647. package/dist/tools/link.d.ts.map +1 -0
  648. package/dist/tools/link.js +127 -0
  649. package/dist/tools/link.js.map +1 -0
  650. package/dist/tools/query.d.ts +17 -0
  651. package/dist/tools/query.d.ts.map +1 -0
  652. package/dist/tools/query.js +342 -0
  653. package/dist/tools/query.js.map +1 -0
  654. package/dist/tools/task.d.ts +20 -0
  655. package/dist/tools/task.d.ts.map +1 -0
  656. package/dist/tools/task.js +161 -0
  657. package/dist/tools/task.js.map +1 -0
  658. package/dist/tools/types.d.ts +334 -0
  659. package/dist/tools/types.d.ts.map +1 -0
  660. package/dist/tools/types.js +19 -0
  661. package/dist/tools/types.js.map +1 -0
  662. package/package.json +40 -5
@@ -0,0 +1,945 @@
1
+ /**
2
+ * Sudocode Provider
3
+ *
4
+ * Provider that integrates with sudocode via CLI.
5
+ * Handles sudocode:// and sc:// URI schemes.
6
+ *
7
+ * Sudocode is a git-native agent orchestration system that stores
8
+ * specs, issues, and relationships in .sudocode/ (JSONL + SQLite).
9
+ * This provider bridges sudocode data into the OpenTasks graph.
10
+ */
11
+ import { exec as execCallback } from 'child_process';
12
+ import { readFile } from 'fs/promises';
13
+ import { join } from 'path';
14
+ import { promisify } from 'util';
15
+ import chokidar from 'chokidar';
16
+ import { ProviderError as ProviderErrorClass } from './types.js';
17
+ import { filterEdgesByType, filterEdgesByDirection } from './traits/RelationshipQueryable.js';
18
+ const execAsync = promisify(execCallback);
19
+ // ============================================================================
20
+ // Constants
21
+ // ============================================================================
22
+ /**
23
+ * Pattern for sudocode:// or sc:// URIs
24
+ * Format: sudocode://[workspace/]id or sc://[workspace/]id
25
+ */
26
+ const SUDOCODE_URI_PATTERN = /^(sudocode|sc):\/\/(?:([^/]+)\/)?(.+)$/i;
27
+ /**
28
+ * Pattern for Sudocode spec IDs (e.g., s-14sh, SPEC-001)
29
+ */
30
+ const SPEC_ID_PATTERN = /^(?:s-[a-z0-9]+|SPEC-\d+)$/i;
31
+ /**
32
+ * Pattern for Sudocode issue IDs (e.g., i-x7k9, ISSUE-001)
33
+ */
34
+ const ISSUE_ID_PATTERN = /^(?:i-[a-z0-9]+|ISSUE-\d+)$/i;
35
+ /**
36
+ * Pattern for any Sudocode entity ID
37
+ */
38
+ const ENTITY_ID_PATTERN = /^(?:[si]-[a-z0-9]+|(?:SPEC|ISSUE)-\d+)$/i;
39
+ // ============================================================================
40
+ // Helper Functions
41
+ // ============================================================================
42
+ /**
43
+ * Determine entity type from ID prefix
44
+ */
45
+ function entityTypeFromId(id) {
46
+ if (id.startsWith('s-') || id.toUpperCase().startsWith('SPEC-')) {
47
+ return 'spec';
48
+ }
49
+ return 'issue';
50
+ }
51
+ /**
52
+ * Valid task actions for each status state
53
+ */
54
+ function validActionsForStatus(status) {
55
+ switch (status) {
56
+ case 'open':
57
+ return ['start', 'block', 'close'];
58
+ case 'in_progress':
59
+ return ['complete', 'block', 'close'];
60
+ case 'blocked':
61
+ return ['reopen', 'close'];
62
+ case 'closed':
63
+ return ['reopen'];
64
+ default:
65
+ return ['start', 'complete', 'block', 'reopen', 'close'];
66
+ }
67
+ }
68
+ /**
69
+ * Map Sudocode priority to normalized 0-4 scale
70
+ */
71
+ function mapPriority(priority) {
72
+ if (priority === undefined)
73
+ return undefined;
74
+ return Math.max(0, Math.min(4, priority));
75
+ }
76
+ /**
77
+ * Map Sudocode relationship types to OpenTasks edge types
78
+ */
79
+ function mapRelationshipType(relType) {
80
+ switch (relType.toLowerCase()) {
81
+ case 'blocks':
82
+ return 'blocks';
83
+ case 'related':
84
+ return 'related';
85
+ case 'discovered-from':
86
+ return 'discovered-from';
87
+ case 'implements':
88
+ return 'implements';
89
+ case 'references':
90
+ return 'references';
91
+ case 'depends-on':
92
+ return 'depends-on';
93
+ default:
94
+ return relType;
95
+ }
96
+ }
97
+ /**
98
+ * Normalize relationships from either format into a flat array
99
+ * with consistent `from_id`, `to_id`, `relationship_type` fields.
100
+ */
101
+ function normalizeRelationships(rels) {
102
+ if (!rels)
103
+ return [];
104
+ let flat;
105
+ if (Array.isArray(rels)) {
106
+ // JSONL format: flat array with from/to/type
107
+ flat = rels;
108
+ }
109
+ else {
110
+ // show --json format: { outgoing, incoming } with from_id/to_id/relationship_type
111
+ flat = [...(rels.outgoing ?? []), ...(rels.incoming ?? [])];
112
+ }
113
+ // Normalize field names so consumers can always use from_id/to_id/relationship_type
114
+ return flat.map((rel) => ({
115
+ ...rel,
116
+ from_id: rel.from_id ?? rel.from,
117
+ to_id: rel.to_id ?? rel.to,
118
+ relationship_type: rel.relationship_type ?? rel.type,
119
+ }));
120
+ }
121
+ /**
122
+ * Check if an entity is archived (handles both boolean and number)
123
+ */
124
+ function isArchived(entity) {
125
+ return entity.archived === true || entity.archived === 1;
126
+ }
127
+ /**
128
+ * Convert Sudocode spec to ProviderNode
129
+ */
130
+ function specToProviderNode(spec, workspace = '.') {
131
+ return {
132
+ id: spec.id,
133
+ uri: `sudocode://${workspace}/${spec.id}`,
134
+ type: 'spec',
135
+ title: spec.title,
136
+ content: spec.content,
137
+ status: isArchived(spec) ? 'archived' : 'active',
138
+ priority: mapPriority(spec.priority),
139
+ rawData: spec,
140
+ fetchedAt: new Date().toISOString(),
141
+ };
142
+ }
143
+ /**
144
+ * Convert Sudocode issue to ProviderNode
145
+ */
146
+ function issueToProviderNode(issue, workspace = '.') {
147
+ return {
148
+ id: issue.id,
149
+ uri: `sudocode://${workspace}/${issue.id}`,
150
+ type: 'issue',
151
+ title: issue.title,
152
+ content: issue.content,
153
+ status: issue.status,
154
+ priority: mapPriority(issue.priority),
155
+ rawData: issue,
156
+ fetchedAt: new Date().toISOString(),
157
+ };
158
+ }
159
+ /**
160
+ * Convert any Sudocode entity to ProviderNode
161
+ */
162
+ function entityToProviderNode(entity, workspace = '.') {
163
+ const entityType = entityTypeFromId(entity.id);
164
+ if (entityType === 'spec') {
165
+ return specToProviderNode(entity, workspace);
166
+ }
167
+ return issueToProviderNode(entity, workspace);
168
+ }
169
+ // ============================================================================
170
+ // Sudocode Provider Implementation
171
+ // ============================================================================
172
+ /**
173
+ * Create a Sudocode provider with relationship querying and optional watching support
174
+ */
175
+ export function createSudocodeProvider(config = {}) {
176
+ const executable = config.executable ?? 'sudocode';
177
+ const cwd = config.cwd;
178
+ const timeout = config.timeout ?? 30000;
179
+ const extraArgs = config.extraArgs ?? [];
180
+ const watchPath = config.watchPath;
181
+ const watchDebounceMs = config.watchDebounceMs ?? 200;
182
+ const capabilities = {
183
+ read: true,
184
+ write: true,
185
+ search: true,
186
+ watch: !!watchPath,
187
+ mount: true,
188
+ feedback: false,
189
+ };
190
+ // =========================================================================
191
+ // Watch State (only active when watchPath is configured)
192
+ // =========================================================================
193
+ /** Cached content hashes for change diffing: entity id → hash of serialized data */
194
+ const cachedHashes = new Map();
195
+ /** Cached edge signatures for edge change detection: "from\0to\0type" → true */
196
+ const cachedEdgeKeys = new Set();
197
+ /** chokidar watcher instance */
198
+ let fileWatcher = null;
199
+ /** Current watch callback */
200
+ let watchCallback = null;
201
+ /** Debounce timer for coalescing rapid file changes */
202
+ let debounceTimer = null;
203
+ /**
204
+ * Simple hash for diffing (not cryptographic — just for detecting changes).
205
+ */
206
+ function quickHash(input) {
207
+ let hash = 0;
208
+ for (let i = 0; i < input.length; i++) {
209
+ const char = input.charCodeAt(i);
210
+ hash = (hash << 5) - hash + char;
211
+ hash = hash & hash;
212
+ }
213
+ return hash.toString(16);
214
+ }
215
+ /**
216
+ * Build a stable hash key for a Sudocode entity (substantive fields only)
217
+ */
218
+ function entityHashKey(entity) {
219
+ const substantive = {
220
+ title: entity.title,
221
+ content: entity.content,
222
+ status: 'status' in entity ? entity.status : undefined,
223
+ priority: entity.priority,
224
+ tags: entity.tags ? [...entity.tags].sort() : undefined,
225
+ archived: entity.archived,
226
+ parent_id: entity.parent_id,
227
+ };
228
+ return quickHash(JSON.stringify(substantive));
229
+ }
230
+ /**
231
+ * Compute the set of edge keys from an entity's relationship fields
232
+ */
233
+ function entityEdgeKeys(entity) {
234
+ const keys = new Set();
235
+ for (const rel of normalizeRelationships(entity.relationships)) {
236
+ if (rel.from_id && rel.to_id && rel.relationship_type) {
237
+ const edgeType = mapRelationshipType(rel.relationship_type);
238
+ keys.add(`${rel.from_id}\0${rel.to_id}\0${edgeType}`);
239
+ }
240
+ }
241
+ if (entity.parent_id) {
242
+ keys.add(`${entity.parent_id}\0${entity.id}\0parent-of`);
243
+ }
244
+ return keys;
245
+ }
246
+ /**
247
+ * Diff current Sudocode state against cached hashes and emit change events.
248
+ */
249
+ async function diffAndEmit() {
250
+ if (!watchCallback)
251
+ return;
252
+ try {
253
+ // Read entities directly from JSONL files (includes relationships)
254
+ const issues = await readEntitiesFromJsonl('issue');
255
+ const specs = await readEntitiesFromJsonl('spec');
256
+ const allEntities = [...issues, ...specs];
257
+ const currentIds = new Set();
258
+ const currentEdgeKeys = new Set();
259
+ for (const entity of allEntities) {
260
+ if (isArchived(entity)) {
261
+ if (cachedHashes.has(entity.id)) {
262
+ watchCallback({
263
+ kind: 'node',
264
+ event: {
265
+ type: 'deleted',
266
+ nodeId: entity.id,
267
+ uri: `sudocode://./${entity.id}`,
268
+ timestamp: new Date().toISOString(),
269
+ },
270
+ });
271
+ cachedHashes.delete(entity.id);
272
+ }
273
+ continue;
274
+ }
275
+ currentIds.add(entity.id);
276
+ const hash = entityHashKey(entity);
277
+ const prevHash = cachedHashes.get(entity.id);
278
+ const providerNode = entityToProviderNode(entity);
279
+ if (!prevHash) {
280
+ watchCallback({
281
+ kind: 'node',
282
+ event: {
283
+ type: 'created',
284
+ nodeId: entity.id,
285
+ uri: providerNode.uri,
286
+ node: providerNode,
287
+ timestamp: new Date().toISOString(),
288
+ },
289
+ });
290
+ }
291
+ else if (prevHash !== hash) {
292
+ const event = {
293
+ type: 'updated',
294
+ nodeId: entity.id,
295
+ uri: providerNode.uri,
296
+ node: providerNode,
297
+ timestamp: new Date().toISOString(),
298
+ };
299
+ watchCallback({ kind: 'node', event });
300
+ }
301
+ cachedHashes.set(entity.id, hash);
302
+ for (const key of entityEdgeKeys(entity)) {
303
+ currentEdgeKeys.add(key);
304
+ }
305
+ }
306
+ // Detect deleted entities
307
+ for (const [id] of cachedHashes) {
308
+ if (!currentIds.has(id)) {
309
+ watchCallback({
310
+ kind: 'node',
311
+ event: {
312
+ type: 'deleted',
313
+ nodeId: id,
314
+ uri: `sudocode://./${id}`,
315
+ timestamp: new Date().toISOString(),
316
+ },
317
+ });
318
+ cachedHashes.delete(id);
319
+ }
320
+ }
321
+ // Detect edge changes — new edges
322
+ for (const key of currentEdgeKeys) {
323
+ if (!cachedEdgeKeys.has(key)) {
324
+ const [from, to, type] = key.split('\0');
325
+ const event = {
326
+ type: 'created',
327
+ edge: { from, to, type },
328
+ sourceUri: `sudocode://./${from}`,
329
+ targetUri: `sudocode://./${to}`,
330
+ timestamp: new Date().toISOString(),
331
+ };
332
+ watchCallback({ kind: 'edge', event });
333
+ }
334
+ }
335
+ // Deleted edges
336
+ for (const key of cachedEdgeKeys) {
337
+ if (!currentEdgeKeys.has(key)) {
338
+ const [from, to, type] = key.split('\0');
339
+ const event = {
340
+ type: 'deleted',
341
+ edge: { from, to, type },
342
+ sourceUri: `sudocode://./${from}`,
343
+ targetUri: `sudocode://./${to}`,
344
+ timestamp: new Date().toISOString(),
345
+ };
346
+ watchCallback({ kind: 'edge', event });
347
+ }
348
+ }
349
+ // Update cached edge state
350
+ cachedEdgeKeys.clear();
351
+ for (const key of currentEdgeKeys) {
352
+ cachedEdgeKeys.add(key);
353
+ }
354
+ }
355
+ catch {
356
+ // Resilient — log internally but don't crash the watcher
357
+ }
358
+ }
359
+ /**
360
+ * Handle a raw file change event with debouncing
361
+ */
362
+ function onFileChange() {
363
+ if (debounceTimer) {
364
+ clearTimeout(debounceTimer);
365
+ }
366
+ debounceTimer = setTimeout(() => {
367
+ debounceTimer = null;
368
+ void diffAndEmit();
369
+ }, watchDebounceMs);
370
+ }
371
+ /**
372
+ * Seed the cached hashes from current Sudocode state
373
+ */
374
+ async function seedCache() {
375
+ try {
376
+ const issues = await readEntitiesFromJsonl('issue');
377
+ const specs = await readEntitiesFromJsonl('spec');
378
+ const allEntities = [...issues, ...specs];
379
+ cachedHashes.clear();
380
+ cachedEdgeKeys.clear();
381
+ for (const entity of allEntities) {
382
+ if (isArchived(entity))
383
+ continue;
384
+ cachedHashes.set(entity.id, entityHashKey(entity));
385
+ for (const key of entityEdgeKeys(entity)) {
386
+ cachedEdgeKeys.add(key);
387
+ }
388
+ }
389
+ }
390
+ catch {
391
+ // If we can't seed, first diff will treat everything as 'created'
392
+ }
393
+ }
394
+ /**
395
+ * Shell-escape a single argument
396
+ */
397
+ function shellEscape(arg) {
398
+ if (/['\s"\\$`!]/.test(arg)) {
399
+ return `'${arg.replace(/'/g, "'\\''")}'`;
400
+ }
401
+ return arg;
402
+ }
403
+ /**
404
+ * Execute a sudocode CLI command
405
+ */
406
+ async function execSudocode(args, context) {
407
+ const command = [executable, ...extraArgs.map(shellEscape), ...args.map(shellEscape)].join(' ');
408
+ try {
409
+ const { stdout } = await execAsync(command, {
410
+ cwd: context?.cwd ?? cwd,
411
+ timeout: context?.timeout ?? timeout,
412
+ env: { ...process.env },
413
+ });
414
+ return stdout.trim();
415
+ }
416
+ catch (error) {
417
+ const err = error;
418
+ if (err.code === 'ENOENT') {
419
+ throw new ProviderErrorClass('PROVIDER_ERROR', `Sudocode CLI not found: ${executable}`, 'sudocode');
420
+ }
421
+ if (err.killed) {
422
+ throw new ProviderErrorClass('TIMEOUT', `Command timed out: ${command}`, 'sudocode');
423
+ }
424
+ let errorMessage = err.message ?? 'Unknown error';
425
+ if (err.stdout) {
426
+ try {
427
+ const parsed = JSON.parse(err.stdout);
428
+ if (parsed.error) {
429
+ errorMessage = parsed.error;
430
+ }
431
+ }
432
+ catch {
433
+ if (err.stdout.trim()) {
434
+ errorMessage = err.stdout.trim();
435
+ }
436
+ }
437
+ }
438
+ throw new ProviderErrorClass('OPERATION_FAILED', `Sudocode CLI error: ${errorMessage}`, 'sudocode', error instanceof Error ? error : undefined);
439
+ }
440
+ }
441
+ /**
442
+ * Parse JSON output from sudocode CLI
443
+ */
444
+ function parseJson(output) {
445
+ try {
446
+ return JSON.parse(output);
447
+ }
448
+ catch {
449
+ throw new ProviderErrorClass('PROVIDER_ERROR', 'Failed to parse Sudocode CLI output as JSON', 'sudocode');
450
+ }
451
+ }
452
+ /**
453
+ * Fetch entities of a given type from sudocode CLI (list — no relationships)
454
+ */
455
+ async function fetchEntities(type) {
456
+ const subcommand = type === 'spec' ? 'spec' : 'issue';
457
+ const output = await execSudocode(['--json', subcommand, 'list']);
458
+ return parseJson(output);
459
+ }
460
+ /**
461
+ * Resolve the path to the .sudocode data directory.
462
+ * Prefers watchPath, then derives from cwd.
463
+ */
464
+ function sudocodeDataDir() {
465
+ return watchPath ?? (cwd ? join(cwd, '.sudocode') : null);
466
+ }
467
+ /**
468
+ * Read entities directly from a JSONL file in .sudocode/.
469
+ * The JSONL files contain fully denormalized data with relationships
470
+ * and tags already embedded — no CLI calls needed.
471
+ *
472
+ * Falls back to CLI `list` if the file can't be read.
473
+ */
474
+ async function readEntitiesFromJsonl(type) {
475
+ const jsonlFile = type === 'spec' ? 'specs.jsonl' : 'issues.jsonl';
476
+ const dir = sudocodeDataDir();
477
+ if (!dir) {
478
+ // No path to .sudocode dir — fall back to CLI
479
+ return fetchEntities(type);
480
+ }
481
+ try {
482
+ const content = await readFile(join(dir, jsonlFile), 'utf-8');
483
+ const entities = [];
484
+ for (const line of content.split('\n')) {
485
+ const trimmed = line.trim();
486
+ if (!trimmed)
487
+ continue;
488
+ try {
489
+ entities.push(JSON.parse(trimmed));
490
+ }
491
+ catch {
492
+ // Skip malformed lines
493
+ }
494
+ }
495
+ return entities;
496
+ }
497
+ catch {
498
+ // File doesn't exist or isn't readable — fall back to CLI
499
+ return fetchEntities(type);
500
+ }
501
+ }
502
+ /**
503
+ * Find a single entity by ID from the JSONL files.
504
+ * Falls back to CLI `show` if JSONL is unavailable or entity not found.
505
+ */
506
+ async function findEntityById(id, workspace = '.') {
507
+ const entityType = entityTypeFromId(id);
508
+ const dir = sudocodeDataDir();
509
+ if (dir) {
510
+ try {
511
+ const entities = await readEntitiesFromJsonl(entityType);
512
+ const found = entities.find((e) => e.id === id);
513
+ if (found)
514
+ return found;
515
+ // Not in JSONL — might be a stale file; fall through to CLI
516
+ }
517
+ catch {
518
+ // JSONL read failed — fall through to CLI
519
+ }
520
+ }
521
+ // Fall back to CLI show
522
+ const subcommand = entityType === 'spec' ? 'spec' : 'issue';
523
+ try {
524
+ const output = await execSudocode(['--json', subcommand, 'show', id]);
525
+ return parseJson(output);
526
+ }
527
+ catch (error) {
528
+ if (error instanceof ProviderErrorClass && error.code === 'OPERATION_FAILED') {
529
+ const message = error.message.toLowerCase();
530
+ if (message.includes('not found') ||
531
+ message.includes('does not exist') ||
532
+ message.includes('no issue found') ||
533
+ message.includes('no spec found')) {
534
+ return null;
535
+ }
536
+ }
537
+ throw error;
538
+ }
539
+ }
540
+ return {
541
+ name: 'sudocode',
542
+ schemes: ['sudocode', 'sc'],
543
+ capabilities,
544
+ // =========================================================================
545
+ // URI Operations
546
+ // =========================================================================
547
+ parseUri(uri) {
548
+ // Check for sudocode:// or sc:// URI
549
+ const match = uri.match(SUDOCODE_URI_PATTERN);
550
+ if (match) {
551
+ const scheme = match[1].toLowerCase();
552
+ const workspace = match[2] || '.';
553
+ const id = match[3];
554
+ return {
555
+ scheme,
556
+ workspace,
557
+ id,
558
+ isRelative: workspace === '.',
559
+ };
560
+ }
561
+ // Check for bare Sudocode entity ID
562
+ if (ENTITY_ID_PATTERN.test(uri)) {
563
+ return {
564
+ scheme: 'sudocode',
565
+ workspace: '.',
566
+ id: uri,
567
+ isRelative: true,
568
+ };
569
+ }
570
+ return null;
571
+ },
572
+ buildUri(id, options) {
573
+ const workspace = options?.workspace ?? '.';
574
+ if (options?.relative) {
575
+ return id;
576
+ }
577
+ return `sudocode://${workspace}/${id}`;
578
+ },
579
+ isValidUri(uri) {
580
+ return this.parseUri(uri) !== null;
581
+ },
582
+ // =========================================================================
583
+ // CRUD Operations
584
+ // =========================================================================
585
+ async get(id, _context) {
586
+ const parsed = this.parseUri(id);
587
+ const entityId = parsed?.id ?? id;
588
+ const workspace = parsed?.workspace ?? '.';
589
+ const entity = await findEntityById(entityId, workspace);
590
+ if (!entity)
591
+ return null;
592
+ return entityToProviderNode(entity, workspace);
593
+ },
594
+ async list(filter, _context) {
595
+ const results = [];
596
+ const entityTypes = filter?.type === 'spec'
597
+ ? ['spec']
598
+ : filter?.type === 'issue'
599
+ ? ['issue']
600
+ : ['issue', 'spec'];
601
+ for (const entityType of entityTypes) {
602
+ try {
603
+ const entities = await readEntitiesFromJsonl(entityType);
604
+ for (const entity of entities) {
605
+ if (isArchived(entity))
606
+ continue;
607
+ // Client-side status filter (applies to issues; specs don't have status)
608
+ if (filter?.status && entityType === 'issue') {
609
+ const issue = entity;
610
+ if (issue.status !== filter.status)
611
+ continue;
612
+ }
613
+ results.push(entityToProviderNode(entity));
614
+ }
615
+ }
616
+ catch {
617
+ // If one type fails (e.g., no specs), continue with the other
618
+ }
619
+ }
620
+ // Apply limit after combining
621
+ if (filter?.limit && results.length > filter.limit) {
622
+ return results.slice(0, filter.limit);
623
+ }
624
+ return results;
625
+ },
626
+ async create(input, context) {
627
+ const entityType = input.type === 'spec' ? 'spec' : 'issue';
628
+ const subcommand = entityType === 'spec' ? 'spec' : 'issue';
629
+ // --json is global (before subcommand), title is positional (after create)
630
+ const args = ['--json', subcommand, 'create', input.title];
631
+ if (input.content) {
632
+ args.push('-d', input.content);
633
+ }
634
+ if (input.priority !== undefined) {
635
+ args.push('-p', String(input.priority));
636
+ }
637
+ const output = await execSudocode(args, context);
638
+ const entity = parseJson(output);
639
+ return entityToProviderNode(entity);
640
+ },
641
+ async update(id, updates, context) {
642
+ const parsed = this.parseUri(id);
643
+ const entityId = parsed?.id ?? id;
644
+ const entityType = entityTypeFromId(entityId);
645
+ const subcommand = entityType === 'spec' ? 'spec' : 'issue';
646
+ // --json is global (before subcommand)
647
+ const args = ['--json', subcommand, 'update', entityId];
648
+ if (updates.title) {
649
+ args.push('--title', updates.title);
650
+ }
651
+ if (updates.content) {
652
+ args.push('--description', updates.content);
653
+ }
654
+ if (updates.status && entityType === 'issue') {
655
+ args.push('-s', updates.status);
656
+ }
657
+ if (updates.priority !== undefined) {
658
+ args.push('-p', String(updates.priority));
659
+ }
660
+ const output = await execSudocode(args, context);
661
+ const entity = parseJson(output);
662
+ return entityToProviderNode(entity);
663
+ },
664
+ async delete(id, context) {
665
+ const parsed = this.parseUri(id);
666
+ const entityId = parsed?.id ?? id;
667
+ const entityType = entityTypeFromId(entityId);
668
+ const subcommand = entityType === 'spec' ? 'spec' : 'issue';
669
+ await execSudocode([subcommand, 'delete', '--hard', entityId], context);
670
+ },
671
+ // =========================================================================
672
+ // Search
673
+ // =========================================================================
674
+ async search(query, options) {
675
+ const results = [];
676
+ const entityTypes = options?.type === 'spec'
677
+ ? ['spec']
678
+ : options?.type === 'issue'
679
+ ? ['issue']
680
+ : ['issue', 'spec'];
681
+ const queryLower = query.toLowerCase();
682
+ for (const entityType of entityTypes) {
683
+ try {
684
+ const entities = await readEntitiesFromJsonl(entityType);
685
+ for (const entity of entities) {
686
+ if (isArchived(entity))
687
+ continue;
688
+ // Client-side text search across title and content
689
+ const titleMatch = entity.title?.toLowerCase().includes(queryLower);
690
+ const contentMatch = entity.content?.toLowerCase().includes(queryLower);
691
+ if (!titleMatch && !contentMatch)
692
+ continue;
693
+ results.push(entityToProviderNode(entity));
694
+ }
695
+ }
696
+ catch {
697
+ // If search fails for one type, continue with the other
698
+ }
699
+ }
700
+ if (options?.limit && results.length > options.limit) {
701
+ return results.slice(0, options.limit);
702
+ }
703
+ return results;
704
+ },
705
+ // =========================================================================
706
+ // RelationshipQueryable Implementation
707
+ // =========================================================================
708
+ async queryEdges(nodeId, options) {
709
+ const parsed = this.parseUri(nodeId);
710
+ const entityId = parsed?.id ?? nodeId;
711
+ const entity = await findEntityById(entityId);
712
+ if (!entity)
713
+ return [];
714
+ let edges = [];
715
+ // Extract relationships (handles both show and JSONL formats)
716
+ for (const rel of normalizeRelationships(entity.relationships)) {
717
+ if (rel.from_id && rel.to_id && rel.relationship_type) {
718
+ edges.push({
719
+ from: rel.from_id,
720
+ to: rel.to_id,
721
+ type: mapRelationshipType(rel.relationship_type),
722
+ });
723
+ }
724
+ }
725
+ // Parent-child relationship
726
+ if (entity.parent_id) {
727
+ edges.push({ from: entity.parent_id, to: entityId, type: 'parent-of' });
728
+ }
729
+ // Deduplicate edges
730
+ const seen = new Set();
731
+ edges = edges.filter((edge) => {
732
+ const key = `${edge.from}\0${edge.to}\0${edge.type}`;
733
+ if (seen.has(key))
734
+ return false;
735
+ seen.add(key);
736
+ return true;
737
+ });
738
+ // Apply filters
739
+ if (options?.edgeType) {
740
+ edges = filterEdgesByType(edges, options.edgeType);
741
+ }
742
+ if (options?.direction) {
743
+ edges = filterEdgesByDirection(edges, entityId, options.direction);
744
+ }
745
+ if (options?.limit && edges.length > options.limit) {
746
+ edges = edges.slice(0, options.limit);
747
+ }
748
+ return edges;
749
+ },
750
+ supportedEdgeTypes() {
751
+ return [
752
+ { type: 'blocks', canQuery: true, canCreate: false, canDelete: false },
753
+ { type: 'related', canQuery: true, canCreate: false, canDelete: false },
754
+ { type: 'discovered-from', canQuery: true, canCreate: false, canDelete: false },
755
+ { type: 'implements', canQuery: true, canCreate: false, canDelete: false },
756
+ { type: 'references', canQuery: true, canCreate: false, canDelete: false },
757
+ { type: 'depends-on', canQuery: true, canCreate: false, canDelete: false },
758
+ { type: 'parent-of', canQuery: true, canCreate: false, canDelete: false },
759
+ ];
760
+ },
761
+ // =========================================================================
762
+ // Watchable Implementation (only functional when watchPath is configured)
763
+ // =========================================================================
764
+ watchGranularity: {
765
+ reportsChangedFields: false,
766
+ reportsPreviousValues: false,
767
+ reportsEdgeChanges: true,
768
+ mechanism: 'file-watch',
769
+ },
770
+ startWatching(callback) {
771
+ if (!watchPath) {
772
+ return;
773
+ }
774
+ watchCallback = callback;
775
+ if (fileWatcher) {
776
+ return; // Already watching, just updated callback
777
+ }
778
+ void seedCache().then(() => {
779
+ if (!watchCallback)
780
+ return;
781
+ fileWatcher = chokidar.watch(watchPath, {
782
+ ignoreInitial: true,
783
+ persistent: true,
784
+ awaitWriteFinish: {
785
+ stabilityThreshold: 100,
786
+ pollInterval: 20,
787
+ },
788
+ });
789
+ fileWatcher.on('add', onFileChange);
790
+ fileWatcher.on('change', onFileChange);
791
+ fileWatcher.on('unlink', onFileChange);
792
+ fileWatcher.on('error', () => {
793
+ // Resilient — continue watching despite transient errors
794
+ });
795
+ });
796
+ },
797
+ stopWatching() {
798
+ watchCallback = null;
799
+ if (debounceTimer) {
800
+ clearTimeout(debounceTimer);
801
+ debounceTimer = null;
802
+ }
803
+ if (fileWatcher) {
804
+ void fileWatcher.close();
805
+ fileWatcher = null;
806
+ }
807
+ },
808
+ get isWatching() {
809
+ return fileWatcher !== null && watchCallback !== null;
810
+ },
811
+ // =========================================================================
812
+ // TaskManageable Implementation
813
+ // =========================================================================
814
+ taskCapabilities: {
815
+ actions: ['start', 'complete', 'block', 'reopen', 'close'],
816
+ supportsAssignment: true,
817
+ supportsReadyQuery: true,
818
+ statusModel: ['open', 'in_progress', 'blocked', 'closed'],
819
+ },
820
+ async transitionTask(id, action, context) {
821
+ const parsed = this.parseUri(id);
822
+ const entityId = parsed?.id ?? id;
823
+ // Only issues support task transitions (not specs)
824
+ if (entityTypeFromId(entityId) !== 'issue') {
825
+ throw new ProviderErrorClass('NOT_SUPPORTED', `Cannot transition spec ${entityId} — only issues support task lifecycle`, 'sudocode');
826
+ }
827
+ const statusMap = {
828
+ start: 'in_progress',
829
+ complete: 'closed',
830
+ block: 'blocked',
831
+ reopen: 'open',
832
+ close: 'closed',
833
+ };
834
+ const targetStatus = statusMap[action];
835
+ if (!targetStatus) {
836
+ throw new ProviderErrorClass('NOT_SUPPORTED', `Unsupported task action: ${action}`, 'sudocode');
837
+ }
838
+ // Validate transition is allowed from current state
839
+ const current = await findEntityById(entityId);
840
+ if (current) {
841
+ const currentStatus = current.status ?? 'open';
842
+ const allowed = validActionsForStatus(currentStatus);
843
+ if (!allowed.includes(action)) {
844
+ throw new ProviderErrorClass('NOT_SUPPORTED', `Cannot ${action} an issue in '${currentStatus}' state. Valid actions: ${allowed.join(', ')}`, 'sudocode');
845
+ }
846
+ }
847
+ const output = await execSudocode(['--json', 'issue', 'update', entityId, '-s', targetStatus], context);
848
+ const entity = parseJson(output);
849
+ return entityToProviderNode(entity);
850
+ },
851
+ async readyTasks(options, context) {
852
+ const issues = await readEntitiesFromJsonl('issue');
853
+ const readyIssues = [];
854
+ for (const entity of issues) {
855
+ const issue = entity;
856
+ if (isArchived(issue))
857
+ continue;
858
+ if (issue.status !== 'open')
859
+ continue;
860
+ // Apply tag filter
861
+ if (options?.tags) {
862
+ const issueTags = issue.tags ?? [];
863
+ if (!options.tags.every((t) => issueTags.includes(t)))
864
+ continue;
865
+ }
866
+ // Apply priority filter
867
+ if (options?.priority !== undefined) {
868
+ const p = mapPriority(issue.priority);
869
+ if (p === undefined || p > options.priority)
870
+ continue;
871
+ }
872
+ // Apply assignee filter
873
+ if (options?.assignee && issue.assignee !== options.assignee)
874
+ continue;
875
+ // Check for active blockers via relationships
876
+ let hasActiveBlocker = false;
877
+ const rels = normalizeRelationships(issue.relationships);
878
+ for (const rel of rels) {
879
+ if (!rel.from_id || !rel.to_id || !rel.relationship_type)
880
+ continue;
881
+ const relType = rel.relationship_type.toLowerCase();
882
+ const isBlocking = (relType === 'blocks' && rel.to_id === issue.id) ||
883
+ (relType === 'depends-on' && rel.from_id === issue.id);
884
+ if (!isBlocking)
885
+ continue;
886
+ // Resolve the blocker to check its status
887
+ const blockerId = relType === 'blocks' ? rel.from_id : rel.to_id;
888
+ try {
889
+ const blocker = await findEntityById(blockerId);
890
+ if (blocker && !isArchived(blocker)) {
891
+ const blockerStatus = blocker.status;
892
+ if (blockerStatus && blockerStatus !== 'closed') {
893
+ hasActiveBlocker = true;
894
+ break;
895
+ }
896
+ }
897
+ }
898
+ catch {
899
+ // If we can't resolve the blocker, assume it's active
900
+ hasActiveBlocker = true;
901
+ break;
902
+ }
903
+ }
904
+ if (!hasActiveBlocker) {
905
+ readyIssues.push(entityToProviderNode(issue));
906
+ }
907
+ }
908
+ // Sort by priority (lower number = higher priority)
909
+ readyIssues.sort((a, b) => {
910
+ const aPriority = a.priority ?? Infinity;
911
+ const bPriority = b.priority ?? Infinity;
912
+ return aPriority - bPriority;
913
+ });
914
+ // Apply limit
915
+ if (options?.limit && readyIssues.length > options.limit) {
916
+ return readyIssues.slice(0, options.limit);
917
+ }
918
+ return readyIssues;
919
+ },
920
+ async assignTask(id, assignee, context) {
921
+ const parsed = this.parseUri(id);
922
+ const entityId = parsed?.id ?? id;
923
+ if (entityTypeFromId(entityId) !== 'issue') {
924
+ throw new ProviderErrorClass('NOT_SUPPORTED', `Cannot assign spec ${entityId} — only issues support assignment`, 'sudocode');
925
+ }
926
+ const output = await execSudocode(['--json', 'issue', 'update', entityId, '--assignee', assignee], context);
927
+ const entity = parseJson(output);
928
+ return entityToProviderNode(entity);
929
+ },
930
+ async validActions(id, context) {
931
+ const parsed = this.parseUri(id);
932
+ const entityId = parsed?.id ?? id;
933
+ if (entityTypeFromId(entityId) !== 'issue') {
934
+ throw new ProviderErrorClass('NOT_SUPPORTED', `Cannot query actions for spec ${entityId} — only issues support task lifecycle`, 'sudocode');
935
+ }
936
+ const entity = await findEntityById(entityId);
937
+ if (!entity) {
938
+ throw new ProviderErrorClass('NOT_FOUND', `Issue not found: ${entityId}`, 'sudocode');
939
+ }
940
+ const issue = entity;
941
+ return validActionsForStatus(issue.status ?? 'open');
942
+ },
943
+ };
944
+ }
945
+ //# sourceMappingURL=sudocode.js.map