@selvakumaresra/specship 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (624) hide show
  1. package/.claude-plugin/plugin.json +6 -0
  2. package/LICENSE +21 -0
  3. package/README.md +573 -0
  4. package/agents/specship-explorer.md +29 -0
  5. package/commands/cg-drifted.md +23 -0
  6. package/commands/cg-explore.md +13 -0
  7. package/commands/cg-fix.md +21 -0
  8. package/commands/cg-impact.md +13 -0
  9. package/commands/cg-implement.md +23 -0
  10. package/commands/cg-relink.md +21 -0
  11. package/commands/cg-spec.md +19 -0
  12. package/commands/cg-sync.md +8 -0
  13. package/commands/cg-trace.md +13 -0
  14. package/dist/bin/node-version-check.d.ts +37 -0
  15. package/dist/bin/node-version-check.d.ts.map +1 -0
  16. package/dist/bin/node-version-check.js +79 -0
  17. package/dist/bin/node-version-check.js.map +1 -0
  18. package/dist/bin/specship.d.ts +25 -0
  19. package/dist/bin/specship.d.ts.map +1 -0
  20. package/dist/bin/specship.js +2018 -0
  21. package/dist/bin/specship.js.map +1 -0
  22. package/dist/bin/uninstall.d.ts +13 -0
  23. package/dist/bin/uninstall.d.ts.map +1 -0
  24. package/dist/bin/uninstall.js +35 -0
  25. package/dist/bin/uninstall.js.map +1 -0
  26. package/dist/context/formatter.d.ts +30 -0
  27. package/dist/context/formatter.d.ts.map +1 -0
  28. package/dist/context/formatter.js +263 -0
  29. package/dist/context/formatter.js.map +1 -0
  30. package/dist/context/index.d.ts +119 -0
  31. package/dist/context/index.d.ts.map +1 -0
  32. package/dist/context/index.js +1289 -0
  33. package/dist/context/index.js.map +1 -0
  34. package/dist/context/markers.d.ts +19 -0
  35. package/dist/context/markers.d.ts.map +1 -0
  36. package/dist/context/markers.js +22 -0
  37. package/dist/context/markers.js.map +1 -0
  38. package/dist/db/index.d.ts +101 -0
  39. package/dist/db/index.d.ts.map +1 -0
  40. package/dist/db/index.js +276 -0
  41. package/dist/db/index.js.map +1 -0
  42. package/dist/db/migrations.d.ts +44 -0
  43. package/dist/db/migrations.d.ts.map +1 -0
  44. package/dist/db/migrations.js +427 -0
  45. package/dist/db/migrations.js.map +1 -0
  46. package/dist/db/queries.d.ts +357 -0
  47. package/dist/db/queries.d.ts.map +1 -0
  48. package/dist/db/queries.js +1504 -0
  49. package/dist/db/queries.js.map +1 -0
  50. package/dist/db/schema.sql +410 -0
  51. package/dist/db/spec-queries.d.ts +101 -0
  52. package/dist/db/spec-queries.d.ts.map +1 -0
  53. package/dist/db/spec-queries.js +675 -0
  54. package/dist/db/spec-queries.js.map +1 -0
  55. package/dist/db/sqlite-adapter.d.ts +65 -0
  56. package/dist/db/sqlite-adapter.d.ts.map +1 -0
  57. package/dist/db/sqlite-adapter.js +214 -0
  58. package/dist/db/sqlite-adapter.js.map +1 -0
  59. package/dist/directory.d.ts +57 -0
  60. package/dist/directory.d.ts.map +1 -0
  61. package/dist/directory.js +253 -0
  62. package/dist/directory.js.map +1 -0
  63. package/dist/errors.d.ts +136 -0
  64. package/dist/errors.d.ts.map +1 -0
  65. package/dist/errors.js +219 -0
  66. package/dist/errors.js.map +1 -0
  67. package/dist/extraction/dfm-extractor.d.ts +31 -0
  68. package/dist/extraction/dfm-extractor.d.ts.map +1 -0
  69. package/dist/extraction/dfm-extractor.js +151 -0
  70. package/dist/extraction/dfm-extractor.js.map +1 -0
  71. package/dist/extraction/generated-detection.d.ts +30 -0
  72. package/dist/extraction/generated-detection.d.ts.map +1 -0
  73. package/dist/extraction/generated-detection.js +80 -0
  74. package/dist/extraction/generated-detection.js.map +1 -0
  75. package/dist/extraction/grammars.d.ts +100 -0
  76. package/dist/extraction/grammars.d.ts.map +1 -0
  77. package/dist/extraction/grammars.js +426 -0
  78. package/dist/extraction/grammars.js.map +1 -0
  79. package/dist/extraction/index.d.ts +138 -0
  80. package/dist/extraction/index.d.ts.map +1 -0
  81. package/dist/extraction/index.js +1394 -0
  82. package/dist/extraction/index.js.map +1 -0
  83. package/dist/extraction/languages/c-cpp.d.ts +4 -0
  84. package/dist/extraction/languages/c-cpp.d.ts.map +1 -0
  85. package/dist/extraction/languages/c-cpp.js +171 -0
  86. package/dist/extraction/languages/c-cpp.js.map +1 -0
  87. package/dist/extraction/languages/csharp.d.ts +3 -0
  88. package/dist/extraction/languages/csharp.d.ts.map +1 -0
  89. package/dist/extraction/languages/csharp.js +73 -0
  90. package/dist/extraction/languages/csharp.js.map +1 -0
  91. package/dist/extraction/languages/dart.d.ts +3 -0
  92. package/dist/extraction/languages/dart.d.ts.map +1 -0
  93. package/dist/extraction/languages/dart.js +192 -0
  94. package/dist/extraction/languages/dart.js.map +1 -0
  95. package/dist/extraction/languages/go.d.ts +3 -0
  96. package/dist/extraction/languages/go.d.ts.map +1 -0
  97. package/dist/extraction/languages/go.js +74 -0
  98. package/dist/extraction/languages/go.js.map +1 -0
  99. package/dist/extraction/languages/index.d.ts +10 -0
  100. package/dist/extraction/languages/index.d.ts.map +1 -0
  101. package/dist/extraction/languages/index.js +51 -0
  102. package/dist/extraction/languages/index.js.map +1 -0
  103. package/dist/extraction/languages/java.d.ts +3 -0
  104. package/dist/extraction/languages/java.d.ts.map +1 -0
  105. package/dist/extraction/languages/java.js +70 -0
  106. package/dist/extraction/languages/java.js.map +1 -0
  107. package/dist/extraction/languages/javascript.d.ts +3 -0
  108. package/dist/extraction/languages/javascript.d.ts.map +1 -0
  109. package/dist/extraction/languages/javascript.js +90 -0
  110. package/dist/extraction/languages/javascript.js.map +1 -0
  111. package/dist/extraction/languages/kotlin.d.ts +3 -0
  112. package/dist/extraction/languages/kotlin.d.ts.map +1 -0
  113. package/dist/extraction/languages/kotlin.js +259 -0
  114. package/dist/extraction/languages/kotlin.js.map +1 -0
  115. package/dist/extraction/languages/lua.d.ts +3 -0
  116. package/dist/extraction/languages/lua.d.ts.map +1 -0
  117. package/dist/extraction/languages/lua.js +150 -0
  118. package/dist/extraction/languages/lua.js.map +1 -0
  119. package/dist/extraction/languages/luau.d.ts +3 -0
  120. package/dist/extraction/languages/luau.d.ts.map +1 -0
  121. package/dist/extraction/languages/luau.js +37 -0
  122. package/dist/extraction/languages/luau.js.map +1 -0
  123. package/dist/extraction/languages/objc.d.ts +3 -0
  124. package/dist/extraction/languages/objc.d.ts.map +1 -0
  125. package/dist/extraction/languages/objc.js +133 -0
  126. package/dist/extraction/languages/objc.js.map +1 -0
  127. package/dist/extraction/languages/pascal.d.ts +3 -0
  128. package/dist/extraction/languages/pascal.d.ts.map +1 -0
  129. package/dist/extraction/languages/pascal.js +66 -0
  130. package/dist/extraction/languages/pascal.js.map +1 -0
  131. package/dist/extraction/languages/php.d.ts +3 -0
  132. package/dist/extraction/languages/php.d.ts.map +1 -0
  133. package/dist/extraction/languages/php.js +107 -0
  134. package/dist/extraction/languages/php.js.map +1 -0
  135. package/dist/extraction/languages/python.d.ts +3 -0
  136. package/dist/extraction/languages/python.d.ts.map +1 -0
  137. package/dist/extraction/languages/python.js +56 -0
  138. package/dist/extraction/languages/python.js.map +1 -0
  139. package/dist/extraction/languages/ruby.d.ts +3 -0
  140. package/dist/extraction/languages/ruby.d.ts.map +1 -0
  141. package/dist/extraction/languages/ruby.js +114 -0
  142. package/dist/extraction/languages/ruby.js.map +1 -0
  143. package/dist/extraction/languages/rust.d.ts +3 -0
  144. package/dist/extraction/languages/rust.d.ts.map +1 -0
  145. package/dist/extraction/languages/rust.js +109 -0
  146. package/dist/extraction/languages/rust.js.map +1 -0
  147. package/dist/extraction/languages/scala.d.ts +3 -0
  148. package/dist/extraction/languages/scala.d.ts.map +1 -0
  149. package/dist/extraction/languages/scala.js +139 -0
  150. package/dist/extraction/languages/scala.js.map +1 -0
  151. package/dist/extraction/languages/swift.d.ts +3 -0
  152. package/dist/extraction/languages/swift.d.ts.map +1 -0
  153. package/dist/extraction/languages/swift.js +91 -0
  154. package/dist/extraction/languages/swift.js.map +1 -0
  155. package/dist/extraction/languages/typescript.d.ts +3 -0
  156. package/dist/extraction/languages/typescript.d.ts.map +1 -0
  157. package/dist/extraction/languages/typescript.js +129 -0
  158. package/dist/extraction/languages/typescript.js.map +1 -0
  159. package/dist/extraction/liquid-extractor.d.ts +52 -0
  160. package/dist/extraction/liquid-extractor.d.ts.map +1 -0
  161. package/dist/extraction/liquid-extractor.js +313 -0
  162. package/dist/extraction/liquid-extractor.js.map +1 -0
  163. package/dist/extraction/mybatis-extractor.d.ts +48 -0
  164. package/dist/extraction/mybatis-extractor.d.ts.map +1 -0
  165. package/dist/extraction/mybatis-extractor.js +198 -0
  166. package/dist/extraction/mybatis-extractor.js.map +1 -0
  167. package/dist/extraction/parse-worker.d.ts +8 -0
  168. package/dist/extraction/parse-worker.d.ts.map +1 -0
  169. package/dist/extraction/parse-worker.js +94 -0
  170. package/dist/extraction/parse-worker.js.map +1 -0
  171. package/dist/extraction/specs/markdown-spec-extractor.d.ts +59 -0
  172. package/dist/extraction/specs/markdown-spec-extractor.d.ts.map +1 -0
  173. package/dist/extraction/specs/markdown-spec-extractor.js +327 -0
  174. package/dist/extraction/specs/markdown-spec-extractor.js.map +1 -0
  175. package/dist/extraction/specs/types.d.ts +39 -0
  176. package/dist/extraction/specs/types.d.ts.map +1 -0
  177. package/dist/extraction/specs/types.js +8 -0
  178. package/dist/extraction/specs/types.js.map +1 -0
  179. package/dist/extraction/svelte-extractor.d.ts +56 -0
  180. package/dist/extraction/svelte-extractor.d.ts.map +1 -0
  181. package/dist/extraction/svelte-extractor.js +272 -0
  182. package/dist/extraction/svelte-extractor.js.map +1 -0
  183. package/dist/extraction/tree-sitter-helpers.d.ts +28 -0
  184. package/dist/extraction/tree-sitter-helpers.d.ts.map +1 -0
  185. package/dist/extraction/tree-sitter-helpers.js +103 -0
  186. package/dist/extraction/tree-sitter-helpers.js.map +1 -0
  187. package/dist/extraction/tree-sitter-types.d.ts +193 -0
  188. package/dist/extraction/tree-sitter-types.d.ts.map +1 -0
  189. package/dist/extraction/tree-sitter-types.js +10 -0
  190. package/dist/extraction/tree-sitter-types.js.map +1 -0
  191. package/dist/extraction/tree-sitter.d.ts +317 -0
  192. package/dist/extraction/tree-sitter.d.ts.map +1 -0
  193. package/dist/extraction/tree-sitter.js +3092 -0
  194. package/dist/extraction/tree-sitter.js.map +1 -0
  195. package/dist/extraction/vue-extractor.d.ts +51 -0
  196. package/dist/extraction/vue-extractor.d.ts.map +1 -0
  197. package/dist/extraction/vue-extractor.js +251 -0
  198. package/dist/extraction/vue-extractor.js.map +1 -0
  199. package/dist/extraction/wasm/tree-sitter-lua.wasm +0 -0
  200. package/dist/extraction/wasm/tree-sitter-luau.wasm +0 -0
  201. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  202. package/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  203. package/dist/extraction/wasm-runtime-flags.d.ts +38 -0
  204. package/dist/extraction/wasm-runtime-flags.d.ts.map +1 -0
  205. package/dist/extraction/wasm-runtime-flags.js +106 -0
  206. package/dist/extraction/wasm-runtime-flags.js.map +1 -0
  207. package/dist/graph/index.d.ts +8 -0
  208. package/dist/graph/index.d.ts.map +1 -0
  209. package/dist/graph/index.js +13 -0
  210. package/dist/graph/index.js.map +1 -0
  211. package/dist/graph/queries.d.ts +106 -0
  212. package/dist/graph/queries.d.ts.map +1 -0
  213. package/dist/graph/queries.js +366 -0
  214. package/dist/graph/queries.js.map +1 -0
  215. package/dist/graph/traversal.d.ts +127 -0
  216. package/dist/graph/traversal.d.ts.map +1 -0
  217. package/dist/graph/traversal.js +531 -0
  218. package/dist/graph/traversal.js.map +1 -0
  219. package/dist/index.d.ts +551 -0
  220. package/dist/index.d.ts.map +1 -0
  221. package/dist/index.js +1165 -0
  222. package/dist/index.js.map +1 -0
  223. package/dist/installer/config-writer.d.ts +28 -0
  224. package/dist/installer/config-writer.d.ts.map +1 -0
  225. package/dist/installer/config-writer.js +91 -0
  226. package/dist/installer/config-writer.js.map +1 -0
  227. package/dist/installer/index.d.ts +87 -0
  228. package/dist/installer/index.d.ts.map +1 -0
  229. package/dist/installer/index.js +409 -0
  230. package/dist/installer/index.js.map +1 -0
  231. package/dist/installer/instructions-template.d.ts +18 -0
  232. package/dist/installer/instructions-template.d.ts.map +1 -0
  233. package/dist/installer/instructions-template.js +21 -0
  234. package/dist/installer/instructions-template.js.map +1 -0
  235. package/dist/installer/targets/claude.d.ts +88 -0
  236. package/dist/installer/targets/claude.d.ts.map +1 -0
  237. package/dist/installer/targets/claude.js +582 -0
  238. package/dist/installer/targets/claude.js.map +1 -0
  239. package/dist/installer/targets/registry.d.ts +19 -0
  240. package/dist/installer/targets/registry.d.ts.map +1 -0
  241. package/dist/installer/targets/registry.js +31 -0
  242. package/dist/installer/targets/registry.js.map +1 -0
  243. package/dist/installer/targets/shared.d.ts +62 -0
  244. package/dist/installer/targets/shared.d.ts.map +1 -0
  245. package/dist/installer/targets/shared.js +207 -0
  246. package/dist/installer/targets/shared.js.map +1 -0
  247. package/dist/installer/targets/types.d.ts +76 -0
  248. package/dist/installer/targets/types.d.ts.map +1 -0
  249. package/dist/installer/targets/types.js +12 -0
  250. package/dist/installer/targets/types.js.map +1 -0
  251. package/dist/isolation/worktree.d.ts +65 -0
  252. package/dist/isolation/worktree.d.ts.map +1 -0
  253. package/dist/isolation/worktree.js +231 -0
  254. package/dist/isolation/worktree.js.map +1 -0
  255. package/dist/mcp/daemon-paths.d.ts +46 -0
  256. package/dist/mcp/daemon-paths.d.ts.map +1 -0
  257. package/dist/mcp/daemon-paths.js +125 -0
  258. package/dist/mcp/daemon-paths.js.map +1 -0
  259. package/dist/mcp/daemon.d.ts +161 -0
  260. package/dist/mcp/daemon.d.ts.map +1 -0
  261. package/dist/mcp/daemon.js +403 -0
  262. package/dist/mcp/daemon.js.map +1 -0
  263. package/dist/mcp/engine.d.ts +105 -0
  264. package/dist/mcp/engine.d.ts.map +1 -0
  265. package/dist/mcp/engine.js +270 -0
  266. package/dist/mcp/engine.js.map +1 -0
  267. package/dist/mcp/index.d.ts +112 -0
  268. package/dist/mcp/index.d.ts.map +1 -0
  269. package/dist/mcp/index.js +477 -0
  270. package/dist/mcp/index.js.map +1 -0
  271. package/dist/mcp/proxy.d.ts +81 -0
  272. package/dist/mcp/proxy.d.ts.map +1 -0
  273. package/dist/mcp/proxy.js +510 -0
  274. package/dist/mcp/proxy.js.map +1 -0
  275. package/dist/mcp/server-instructions.d.ts +18 -0
  276. package/dist/mcp/server-instructions.d.ts.map +1 -0
  277. package/dist/mcp/server-instructions.js +77 -0
  278. package/dist/mcp/server-instructions.js.map +1 -0
  279. package/dist/mcp/session.d.ts +77 -0
  280. package/dist/mcp/session.d.ts.map +1 -0
  281. package/dist/mcp/session.js +294 -0
  282. package/dist/mcp/session.js.map +1 -0
  283. package/dist/mcp/spec-tools.d.ts +39 -0
  284. package/dist/mcp/spec-tools.d.ts.map +1 -0
  285. package/dist/mcp/spec-tools.js +326 -0
  286. package/dist/mcp/spec-tools.js.map +1 -0
  287. package/dist/mcp/tools.d.ts +404 -0
  288. package/dist/mcp/tools.d.ts.map +1 -0
  289. package/dist/mcp/tools.js +3066 -0
  290. package/dist/mcp/tools.js.map +1 -0
  291. package/dist/mcp/transport.d.ts +188 -0
  292. package/dist/mcp/transport.d.ts.map +1 -0
  293. package/dist/mcp/transport.js +343 -0
  294. package/dist/mcp/transport.js.map +1 -0
  295. package/dist/mcp/version.d.ts +19 -0
  296. package/dist/mcp/version.d.ts.map +1 -0
  297. package/dist/mcp/version.js +71 -0
  298. package/dist/mcp/version.js.map +1 -0
  299. package/dist/resolution/callback-synthesizer.d.ts +10 -0
  300. package/dist/resolution/callback-synthesizer.d.ts.map +1 -0
  301. package/dist/resolution/callback-synthesizer.js +1300 -0
  302. package/dist/resolution/callback-synthesizer.js.map +1 -0
  303. package/dist/resolution/frameworks/cargo-workspace.d.ts +18 -0
  304. package/dist/resolution/frameworks/cargo-workspace.d.ts.map +1 -0
  305. package/dist/resolution/frameworks/cargo-workspace.js +225 -0
  306. package/dist/resolution/frameworks/cargo-workspace.js.map +1 -0
  307. package/dist/resolution/frameworks/csharp.d.ts +8 -0
  308. package/dist/resolution/frameworks/csharp.d.ts.map +1 -0
  309. package/dist/resolution/frameworks/csharp.js +241 -0
  310. package/dist/resolution/frameworks/csharp.js.map +1 -0
  311. package/dist/resolution/frameworks/drupal.d.ts +51 -0
  312. package/dist/resolution/frameworks/drupal.d.ts.map +1 -0
  313. package/dist/resolution/frameworks/drupal.js +367 -0
  314. package/dist/resolution/frameworks/drupal.js.map +1 -0
  315. package/dist/resolution/frameworks/expo-modules.d.ts +3 -0
  316. package/dist/resolution/frameworks/expo-modules.d.ts.map +1 -0
  317. package/dist/resolution/frameworks/expo-modules.js +143 -0
  318. package/dist/resolution/frameworks/expo-modules.js.map +1 -0
  319. package/dist/resolution/frameworks/express.d.ts +8 -0
  320. package/dist/resolution/frameworks/express.d.ts.map +1 -0
  321. package/dist/resolution/frameworks/express.js +308 -0
  322. package/dist/resolution/frameworks/express.js.map +1 -0
  323. package/dist/resolution/frameworks/fabric.d.ts +3 -0
  324. package/dist/resolution/frameworks/fabric.d.ts.map +1 -0
  325. package/dist/resolution/frameworks/fabric.js +354 -0
  326. package/dist/resolution/frameworks/fabric.js.map +1 -0
  327. package/dist/resolution/frameworks/go.d.ts +8 -0
  328. package/dist/resolution/frameworks/go.d.ts.map +1 -0
  329. package/dist/resolution/frameworks/go.js +161 -0
  330. package/dist/resolution/frameworks/go.js.map +1 -0
  331. package/dist/resolution/frameworks/index.d.ts +48 -0
  332. package/dist/resolution/frameworks/index.d.ts.map +1 -0
  333. package/dist/resolution/frameworks/index.js +161 -0
  334. package/dist/resolution/frameworks/index.js.map +1 -0
  335. package/dist/resolution/frameworks/java.d.ts +8 -0
  336. package/dist/resolution/frameworks/java.d.ts.map +1 -0
  337. package/dist/resolution/frameworks/java.js +504 -0
  338. package/dist/resolution/frameworks/java.js.map +1 -0
  339. package/dist/resolution/frameworks/laravel.d.ts +13 -0
  340. package/dist/resolution/frameworks/laravel.d.ts.map +1 -0
  341. package/dist/resolution/frameworks/laravel.js +257 -0
  342. package/dist/resolution/frameworks/laravel.js.map +1 -0
  343. package/dist/resolution/frameworks/nestjs.d.ts +26 -0
  344. package/dist/resolution/frameworks/nestjs.d.ts.map +1 -0
  345. package/dist/resolution/frameworks/nestjs.js +698 -0
  346. package/dist/resolution/frameworks/nestjs.js.map +1 -0
  347. package/dist/resolution/frameworks/play.d.ts +19 -0
  348. package/dist/resolution/frameworks/play.d.ts.map +1 -0
  349. package/dist/resolution/frameworks/play.js +111 -0
  350. package/dist/resolution/frameworks/play.js.map +1 -0
  351. package/dist/resolution/frameworks/python.d.ts +10 -0
  352. package/dist/resolution/frameworks/python.d.ts.map +1 -0
  353. package/dist/resolution/frameworks/python.js +396 -0
  354. package/dist/resolution/frameworks/python.js.map +1 -0
  355. package/dist/resolution/frameworks/react-native.d.ts +3 -0
  356. package/dist/resolution/frameworks/react-native.d.ts.map +1 -0
  357. package/dist/resolution/frameworks/react-native.js +360 -0
  358. package/dist/resolution/frameworks/react-native.js.map +1 -0
  359. package/dist/resolution/frameworks/react.d.ts +8 -0
  360. package/dist/resolution/frameworks/react.d.ts.map +1 -0
  361. package/dist/resolution/frameworks/react.js +365 -0
  362. package/dist/resolution/frameworks/react.js.map +1 -0
  363. package/dist/resolution/frameworks/ruby.d.ts +8 -0
  364. package/dist/resolution/frameworks/ruby.d.ts.map +1 -0
  365. package/dist/resolution/frameworks/ruby.js +302 -0
  366. package/dist/resolution/frameworks/ruby.js.map +1 -0
  367. package/dist/resolution/frameworks/rust.d.ts +8 -0
  368. package/dist/resolution/frameworks/rust.d.ts.map +1 -0
  369. package/dist/resolution/frameworks/rust.js +304 -0
  370. package/dist/resolution/frameworks/rust.js.map +1 -0
  371. package/dist/resolution/frameworks/svelte.d.ts +9 -0
  372. package/dist/resolution/frameworks/svelte.d.ts.map +1 -0
  373. package/dist/resolution/frameworks/svelte.js +249 -0
  374. package/dist/resolution/frameworks/svelte.js.map +1 -0
  375. package/dist/resolution/frameworks/swift-objc.d.ts +37 -0
  376. package/dist/resolution/frameworks/swift-objc.d.ts.map +1 -0
  377. package/dist/resolution/frameworks/swift-objc.js +252 -0
  378. package/dist/resolution/frameworks/swift-objc.js.map +1 -0
  379. package/dist/resolution/frameworks/swift.d.ts +10 -0
  380. package/dist/resolution/frameworks/swift.d.ts.map +1 -0
  381. package/dist/resolution/frameworks/swift.js +400 -0
  382. package/dist/resolution/frameworks/swift.js.map +1 -0
  383. package/dist/resolution/frameworks/vue.d.ts +9 -0
  384. package/dist/resolution/frameworks/vue.d.ts.map +1 -0
  385. package/dist/resolution/frameworks/vue.js +306 -0
  386. package/dist/resolution/frameworks/vue.js.map +1 -0
  387. package/dist/resolution/go-module.d.ts +26 -0
  388. package/dist/resolution/go-module.d.ts.map +1 -0
  389. package/dist/resolution/go-module.js +78 -0
  390. package/dist/resolution/go-module.js.map +1 -0
  391. package/dist/resolution/import-resolver.d.ts +68 -0
  392. package/dist/resolution/import-resolver.d.ts.map +1 -0
  393. package/dist/resolution/import-resolver.js +1275 -0
  394. package/dist/resolution/import-resolver.js.map +1 -0
  395. package/dist/resolution/index.d.ts +117 -0
  396. package/dist/resolution/index.d.ts.map +1 -0
  397. package/dist/resolution/index.js +895 -0
  398. package/dist/resolution/index.js.map +1 -0
  399. package/dist/resolution/lru-cache.d.ts +24 -0
  400. package/dist/resolution/lru-cache.d.ts.map +1 -0
  401. package/dist/resolution/lru-cache.js +62 -0
  402. package/dist/resolution/lru-cache.js.map +1 -0
  403. package/dist/resolution/name-matcher.d.ts +32 -0
  404. package/dist/resolution/name-matcher.d.ts.map +1 -0
  405. package/dist/resolution/name-matcher.js +596 -0
  406. package/dist/resolution/name-matcher.js.map +1 -0
  407. package/dist/resolution/path-aliases.d.ts +68 -0
  408. package/dist/resolution/path-aliases.d.ts.map +1 -0
  409. package/dist/resolution/path-aliases.js +238 -0
  410. package/dist/resolution/path-aliases.js.map +1 -0
  411. package/dist/resolution/spec-link-resolver.d.ts +103 -0
  412. package/dist/resolution/spec-link-resolver.d.ts.map +1 -0
  413. package/dist/resolution/spec-link-resolver.js +259 -0
  414. package/dist/resolution/spec-link-resolver.js.map +1 -0
  415. package/dist/resolution/strip-comments.d.ts +27 -0
  416. package/dist/resolution/strip-comments.d.ts.map +1 -0
  417. package/dist/resolution/strip-comments.js +441 -0
  418. package/dist/resolution/strip-comments.js.map +1 -0
  419. package/dist/resolution/swift-objc-bridge.d.ts +134 -0
  420. package/dist/resolution/swift-objc-bridge.d.ts.map +1 -0
  421. package/dist/resolution/swift-objc-bridge.js +256 -0
  422. package/dist/resolution/swift-objc-bridge.js.map +1 -0
  423. package/dist/resolution/types.d.ts +216 -0
  424. package/dist/resolution/types.d.ts.map +1 -0
  425. package/dist/resolution/types.js +8 -0
  426. package/dist/resolution/types.js.map +1 -0
  427. package/dist/resolution/workspace-packages.d.ts +48 -0
  428. package/dist/resolution/workspace-packages.d.ts.map +1 -0
  429. package/dist/resolution/workspace-packages.js +208 -0
  430. package/dist/resolution/workspace-packages.js.map +1 -0
  431. package/dist/search/query-parser.d.ts +57 -0
  432. package/dist/search/query-parser.d.ts.map +1 -0
  433. package/dist/search/query-parser.js +177 -0
  434. package/dist/search/query-parser.js.map +1 -0
  435. package/dist/search/query-utils.d.ts +71 -0
  436. package/dist/search/query-utils.d.ts.map +1 -0
  437. package/dist/search/query-utils.js +380 -0
  438. package/dist/search/query-utils.js.map +1 -0
  439. package/dist/server/cli.js +152 -0
  440. package/dist/server/index.js +12 -0
  441. package/dist/server/ingest/index.js +18 -0
  442. package/dist/server/ingest/ingestor.js +406 -0
  443. package/dist/server/ingest/parser.js +104 -0
  444. package/dist/server/ingest/pricing.js +78 -0
  445. package/dist/server/ingest/types.js +9 -0
  446. package/dist/server/ingest/watcher.js +77 -0
  447. package/dist/server/package.json +3 -0
  448. package/dist/server/project-registry.js +101 -0
  449. package/dist/server/routes/claude.js +480 -0
  450. package/dist/server/routes/graph.js +149 -0
  451. package/dist/server/routes/memory.js +272 -0
  452. package/dist/server/routes/projects.js +197 -0
  453. package/dist/server/routes/spec.js +105 -0
  454. package/dist/server/routes/status.js +35 -0
  455. package/dist/server/routes/workflow.js +184 -0
  456. package/dist/server/server.js +202 -0
  457. package/dist/sync/git-hooks.d.ts +45 -0
  458. package/dist/sync/git-hooks.d.ts.map +1 -0
  459. package/dist/sync/git-hooks.js +225 -0
  460. package/dist/sync/git-hooks.js.map +1 -0
  461. package/dist/sync/index.d.ts +19 -0
  462. package/dist/sync/index.d.ts.map +1 -0
  463. package/dist/sync/index.js +35 -0
  464. package/dist/sync/index.js.map +1 -0
  465. package/dist/sync/watch-policy.d.ts +48 -0
  466. package/dist/sync/watch-policy.d.ts.map +1 -0
  467. package/dist/sync/watch-policy.js +124 -0
  468. package/dist/sync/watch-policy.js.map +1 -0
  469. package/dist/sync/watcher.d.ts +283 -0
  470. package/dist/sync/watcher.d.ts.map +1 -0
  471. package/dist/sync/watcher.js +606 -0
  472. package/dist/sync/watcher.js.map +1 -0
  473. package/dist/sync/worktree.d.ts +54 -0
  474. package/dist/sync/worktree.d.ts.map +1 -0
  475. package/dist/sync/worktree.js +137 -0
  476. package/dist/sync/worktree.js.map +1 -0
  477. package/dist/types.d.ts +623 -0
  478. package/dist/types.d.ts.map +1 -0
  479. package/dist/types.js +108 -0
  480. package/dist/types.js.map +1 -0
  481. package/dist/ui/glyphs.d.ts +42 -0
  482. package/dist/ui/glyphs.d.ts.map +1 -0
  483. package/dist/ui/glyphs.js +78 -0
  484. package/dist/ui/glyphs.js.map +1 -0
  485. package/dist/ui/shimmer-progress.d.ts +11 -0
  486. package/dist/ui/shimmer-progress.d.ts.map +1 -0
  487. package/dist/ui/shimmer-progress.js +90 -0
  488. package/dist/ui/shimmer-progress.js.map +1 -0
  489. package/dist/ui/shimmer-worker.d.ts +2 -0
  490. package/dist/ui/shimmer-worker.d.ts.map +1 -0
  491. package/dist/ui/shimmer-worker.js +118 -0
  492. package/dist/ui/shimmer-worker.js.map +1 -0
  493. package/dist/ui/types.d.ts +17 -0
  494. package/dist/ui/types.d.ts.map +1 -0
  495. package/dist/ui/types.js +3 -0
  496. package/dist/ui/types.js.map +1 -0
  497. package/dist/utils.d.ts +205 -0
  498. package/dist/utils.d.ts.map +1 -0
  499. package/dist/utils.js +549 -0
  500. package/dist/utils.js.map +1 -0
  501. package/dist/web/chunk-2YZXEHZ2.js +1 -0
  502. package/dist/web/chunk-3GIC555L.js +18 -0
  503. package/dist/web/chunk-3IIIGRMT.js +1 -0
  504. package/dist/web/chunk-47QYKLE5.js +1 -0
  505. package/dist/web/chunk-4LHBWWP7.js +1 -0
  506. package/dist/web/chunk-4OAZLD5W.js +1 -0
  507. package/dist/web/chunk-5OQKAJAE.js +1 -0
  508. package/dist/web/chunk-7B525GKQ.js +1 -0
  509. package/dist/web/chunk-BPDXCOOZ.js +1 -0
  510. package/dist/web/chunk-DT37HTZB.js +1 -0
  511. package/dist/web/chunk-EIMUHJND.js +1 -0
  512. package/dist/web/chunk-FTESTUEO.js +1 -0
  513. package/dist/web/chunk-GLJZV6MU.js +1 -0
  514. package/dist/web/chunk-I7LS67U5.js +1 -0
  515. package/dist/web/chunk-L4TVIPSR.js +1 -0
  516. package/dist/web/chunk-MASCULC2.js +1 -0
  517. package/dist/web/chunk-MW7ICSRM.js +1 -0
  518. package/dist/web/chunk-OI5VP2A3.js +1 -0
  519. package/dist/web/chunk-RA6EBF6I.js +1 -0
  520. package/dist/web/chunk-RP3WU5Y6.js +1 -0
  521. package/dist/web/chunk-RQDRMTXN.js +1 -0
  522. package/dist/web/chunk-TQMT6UDU.js +1 -0
  523. package/dist/web/chunk-U7IYOV7T.js +1 -0
  524. package/dist/web/chunk-UE227MWF.js +1 -0
  525. package/dist/web/chunk-WV573J4K.js +1 -0
  526. package/dist/web/chunk-WVCKOJZL.js +4 -0
  527. package/dist/web/chunk-XZKLVPHE.js +1 -0
  528. package/dist/web/chunk-ZABKKHJ3.js +1 -0
  529. package/dist/web/favicon-16.png +0 -0
  530. package/dist/web/favicon-180.png +0 -0
  531. package/dist/web/favicon-32.png +0 -0
  532. package/dist/web/favicon-512.png +0 -0
  533. package/dist/web/favicon-small.svg +15 -0
  534. package/dist/web/favicon.ico +0 -0
  535. package/dist/web/favicon.svg +20 -0
  536. package/dist/web/index.html +145 -0
  537. package/dist/web/main-RI5CO5Z4.js +1 -0
  538. package/dist/web/styles-CYN7IKT4.css +1 -0
  539. package/dist/workflows/condition-evaluator.d.ts +75 -0
  540. package/dist/workflows/condition-evaluator.d.ts.map +1 -0
  541. package/dist/workflows/condition-evaluator.js +282 -0
  542. package/dist/workflows/condition-evaluator.js.map +1 -0
  543. package/dist/workflows/defaults/index.d.ts +26 -0
  544. package/dist/workflows/defaults/index.d.ts.map +1 -0
  545. package/dist/workflows/defaults/index.js +94 -0
  546. package/dist/workflows/defaults/index.js.map +1 -0
  547. package/dist/workflows/defaults/spec-fix.yaml +110 -0
  548. package/dist/workflows/defaults/spec-implement.yaml +150 -0
  549. package/dist/workflows/defaults/spec-relink.yaml +81 -0
  550. package/dist/workflows/defaults/spec-verify.yaml +51 -0
  551. package/dist/workflows/discovery.d.ts +46 -0
  552. package/dist/workflows/discovery.d.ts.map +1 -0
  553. package/dist/workflows/discovery.js +193 -0
  554. package/dist/workflows/discovery.js.map +1 -0
  555. package/dist/workflows/executor.d.ts +83 -0
  556. package/dist/workflows/executor.d.ts.map +1 -0
  557. package/dist/workflows/executor.js +623 -0
  558. package/dist/workflows/executor.js.map +1 -0
  559. package/dist/workflows/runners/approval.d.ts +18 -0
  560. package/dist/workflows/runners/approval.d.ts.map +1 -0
  561. package/dist/workflows/runners/approval.js +34 -0
  562. package/dist/workflows/runners/approval.js.map +1 -0
  563. package/dist/workflows/runners/bash.d.ts +13 -0
  564. package/dist/workflows/runners/bash.d.ts.map +1 -0
  565. package/dist/workflows/runners/bash.js +143 -0
  566. package/dist/workflows/runners/bash.js.map +1 -0
  567. package/dist/workflows/runners/cancel.d.ts +10 -0
  568. package/dist/workflows/runners/cancel.d.ts.map +1 -0
  569. package/dist/workflows/runners/cancel.js +19 -0
  570. package/dist/workflows/runners/cancel.js.map +1 -0
  571. package/dist/workflows/runners/prompt.d.ts +28 -0
  572. package/dist/workflows/runners/prompt.d.ts.map +1 -0
  573. package/dist/workflows/runners/prompt.js +212 -0
  574. package/dist/workflows/runners/prompt.js.map +1 -0
  575. package/dist/workflows/runners/script.d.ts +17 -0
  576. package/dist/workflows/runners/script.d.ts.map +1 -0
  577. package/dist/workflows/runners/script.js +155 -0
  578. package/dist/workflows/runners/script.js.map +1 -0
  579. package/dist/workflows/runners/types.d.ts +51 -0
  580. package/dist/workflows/runners/types.d.ts.map +1 -0
  581. package/dist/workflows/runners/types.js +13 -0
  582. package/dist/workflows/runners/types.js.map +1 -0
  583. package/dist/workflows/schemas/workflow.d.ts +166 -0
  584. package/dist/workflows/schemas/workflow.d.ts.map +1 -0
  585. package/dist/workflows/schemas/workflow.js +437 -0
  586. package/dist/workflows/schemas/workflow.js.map +1 -0
  587. package/hooks/hooks.json +27 -0
  588. package/package.json +67 -0
  589. package/scripts/add-lang/bench.sh +60 -0
  590. package/scripts/add-lang/check-grammar.mjs +75 -0
  591. package/scripts/add-lang/dump-ast.mjs +103 -0
  592. package/scripts/add-lang/verify-extraction.mjs +70 -0
  593. package/scripts/agent-eval/arms-F.sh +21 -0
  594. package/scripts/agent-eval/arms-matrix.sh +37 -0
  595. package/scripts/agent-eval/audit.sh +68 -0
  596. package/scripts/agent-eval/bench-readme.sh +28 -0
  597. package/scripts/agent-eval/bench-why-repo.sh +22 -0
  598. package/scripts/agent-eval/block-read-hook.sh +19 -0
  599. package/scripts/agent-eval/hook-settings.json +15 -0
  600. package/scripts/agent-eval/itrun.sh +120 -0
  601. package/scripts/agent-eval/parse-arms.mjs +116 -0
  602. package/scripts/agent-eval/parse-bench-readme.mjs +84 -0
  603. package/scripts/agent-eval/parse-run.mjs +45 -0
  604. package/scripts/agent-eval/parse-session.mjs +93 -0
  605. package/scripts/agent-eval/probe-context.mjs +21 -0
  606. package/scripts/agent-eval/probe-explore.mjs +40 -0
  607. package/scripts/agent-eval/probe-node.mjs +20 -0
  608. package/scripts/agent-eval/probe-sweep.mjs +119 -0
  609. package/scripts/agent-eval/probe-trace.mjs +20 -0
  610. package/scripts/agent-eval/run-agent.sh +34 -0
  611. package/scripts/agent-eval/run-all.sh +67 -0
  612. package/scripts/agent-eval/run-arms.sh +56 -0
  613. package/scripts/agent-eval/seq-matrix.mjs +137 -0
  614. package/scripts/build-bundle.sh +118 -0
  615. package/scripts/build-server-bundle.mjs +80 -0
  616. package/scripts/build-web-bundle.mjs +66 -0
  617. package/scripts/extract-release-notes.mjs +130 -0
  618. package/scripts/local-install.sh +41 -0
  619. package/scripts/npm-sdk.js +75 -0
  620. package/scripts/npm-shim.js +246 -0
  621. package/scripts/offline-install.ps1 +148 -0
  622. package/scripts/offline-install.sh +136 -0
  623. package/scripts/pack-npm.sh +119 -0
  624. package/scripts/prepare-release.mjs +270 -0
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Graph routes — node detail + explore + search.
3
+ *
4
+ * These are thin wrappers around methods on a SpecShip instance. Every
5
+ * route resolves the active instance via `app.activeCg(req)`, which honors
6
+ * `?project=<slug>` and falls back to the boot-time primary. When neither
7
+ * is available, returns 409 / `no_project` so the desktop UI can show its
8
+ * project picker instead of an opaque 500.
9
+ *
10
+ * The MCP layer's `specship_*` tools are markdown-formatted for agents;
11
+ * here we return raw JSON so the UI can render visually.
12
+ */
13
+ /** Returns the active SpecShip or sends a 409. */
14
+ async function resolveCg(app, req, reply) {
15
+ const cg = await app.activeCg(req);
16
+ if (!cg) {
17
+ reply.code(409).send({ error: 'no project selected', code: 'no_project' });
18
+ return null;
19
+ }
20
+ return cg;
21
+ }
22
+ export async function registerGraphRoutes(app) {
23
+ app.get('/api/graph/stats', async (req, reply) => {
24
+ const cg = await resolveCg(app, req, reply);
25
+ if (!cg)
26
+ return;
27
+ return cg.getStats();
28
+ });
29
+ /**
30
+ * List nodes with optional kind / file / limit filtering. For node-explorer
31
+ * UI surfaces that want a flat paginated list.
32
+ */
33
+ app.get('/api/graph/nodes', async (req, reply) => {
34
+ const cg = await resolveCg(app, req, reply);
35
+ if (!cg)
36
+ return;
37
+ const limit = Math.min(Math.max(parseInt(req.query.limit ?? '100', 10) || 100, 1), 1000);
38
+ const offset = Math.max(parseInt(req.query.offset ?? '0', 10) || 0, 0);
39
+ const results = cg.searchNodes(req.query.kind ?? '', { limit: limit + offset });
40
+ return {
41
+ total: results.length,
42
+ offset,
43
+ limit,
44
+ nodes: results.slice(offset, offset + limit).map((r) => r.node),
45
+ };
46
+ });
47
+ /**
48
+ * GET /api/graph/node — by symbol name (with optional file/line disambiguation)
49
+ * or by exact id. Returns the node + callers + callees + linked specs.
50
+ */
51
+ app.get('/api/graph/node', async (req, reply) => {
52
+ const cg = await resolveCg(app, req, reply);
53
+ if (!cg)
54
+ return;
55
+ const { symbol, id, file, line } = req.query;
56
+ if (!symbol && !id) {
57
+ return reply.code(400).send({ error: 'symbol or id is required' });
58
+ }
59
+ let matches = [];
60
+ if (id) {
61
+ const node = cg.getNode(id);
62
+ if (node)
63
+ matches = [node];
64
+ }
65
+ else if (symbol) {
66
+ matches = cg.searchNodes(symbol, { limit: 50 }).map((r) => r.node);
67
+ if (file) {
68
+ const f = file.toLowerCase();
69
+ const narrowed = matches.filter((n) => n.filePath.toLowerCase().includes(f));
70
+ if (narrowed.length > 0)
71
+ matches = narrowed;
72
+ }
73
+ if (line) {
74
+ const ln = parseInt(line, 10);
75
+ if (Number.isFinite(ln)) {
76
+ const containing = matches.filter((n) => n.startLine <= ln && (n.endLine ?? n.startLine) >= ln);
77
+ matches = containing.length > 0 ? containing : matches.slice(0, 1);
78
+ }
79
+ }
80
+ }
81
+ if (matches.length === 0)
82
+ return reply.code(404).send({ error: 'not found' });
83
+ const enriched = matches.map((n) => {
84
+ const callers = cg.getCallers(n.id).map((e) => e.node);
85
+ const callees = cg.getCallees(n.id).map((e) => e.node);
86
+ const links = cg.getSpecQueries().getLinksByNode(n.id);
87
+ return { ...n, callers, callees, linkedSpecs: links };
88
+ });
89
+ return { matches: enriched };
90
+ });
91
+ app.post('/api/graph/explore', async (req, reply) => {
92
+ const cg = await resolveCg(app, req, reply);
93
+ if (!cg)
94
+ return;
95
+ const { query, maxFiles } = req.body ?? { query: '' };
96
+ if (!query)
97
+ return reply.code(400).send({ error: 'query is required' });
98
+ void maxFiles;
99
+ const ctx = await cg.buildContext(query, {
100
+ maxNodes: 80,
101
+ includeCode: true,
102
+ format: 'markdown',
103
+ });
104
+ return ctx;
105
+ });
106
+ app.get('/api/graph/search', async (req, reply) => {
107
+ const cg = await resolveCg(app, req, reply);
108
+ if (!cg)
109
+ return;
110
+ const q = req.query.q ?? '';
111
+ const limit = Math.min(parseInt(req.query.limit ?? '20', 10) || 20, 200);
112
+ if (!q)
113
+ return { results: [] };
114
+ const kinds = req.query.kind ? [req.query.kind] : undefined;
115
+ const results = cg.searchNodes(q, { kinds, limit });
116
+ return { results };
117
+ });
118
+ app.get('/api/graph/callers/:id', async (req, reply) => {
119
+ const cg = await resolveCg(app, req, reply);
120
+ if (!cg)
121
+ return;
122
+ const node = cg.getNode(req.params.id);
123
+ if (!node)
124
+ return reply.code(404).send({ error: 'node not found' });
125
+ return { callers: cg.getCallers(req.params.id).map((e) => e.node) };
126
+ });
127
+ app.get('/api/graph/callees/:id', async (req, reply) => {
128
+ const cg = await resolveCg(app, req, reply);
129
+ if (!cg)
130
+ return;
131
+ const node = cg.getNode(req.params.id);
132
+ if (!node)
133
+ return reply.code(404).send({ error: 'node not found' });
134
+ return { callees: cg.getCallees(req.params.id).map((e) => e.node) };
135
+ });
136
+ app.get('/api/graph/impact/:id', async (req, reply) => {
137
+ const cg = await resolveCg(app, req, reply);
138
+ if (!cg)
139
+ return;
140
+ const depth = Math.min(parseInt(req.query.depth ?? '2', 10) || 2, 5);
141
+ return { impact: cg.getImpactRadius(req.params.id, depth) };
142
+ });
143
+ app.get('/api/graph/files', async (req, reply) => {
144
+ const cg = await resolveCg(app, req, reply);
145
+ if (!cg)
146
+ return;
147
+ return { files: cg.getFiles() };
148
+ });
149
+ }
@@ -0,0 +1,272 @@
1
+ /**
2
+ * GET /api/memory — CLAUDE.md hierarchy + ~/.claude/memory notes.
3
+ *
4
+ * Walks the standard Claude Code memory locations and returns a flattened
5
+ * list of MemoryFile records the desktop UI can render directly. Resolves
6
+ * `@path` imports from each instruction file.
7
+ *
8
+ * Levels:
9
+ * - enterprise — `/Library/Application Support/ClaudeCode/CLAUDE.md` (read-only)
10
+ * - user — `~/.claude/CLAUDE.md`
11
+ * - project — `<projectRoot>/CLAUDE.md`
12
+ * - subdir — `<projectRoot>/<sub>/CLAUDE.md` (one level deep; common case)
13
+ * - import — files referenced as `@path` from a project/subdir CLAUDE.md
14
+ * - note — `~/.claude/memory/*.md` (agent-written via memory tool)
15
+ */
16
+ import { promises as fs } from 'node:fs';
17
+ import path from 'node:path';
18
+ import os from 'node:os';
19
+ import { decodeProjectSlug } from '../ingest/ingestor.js';
20
+ const ENTERPRISE_PATHS = [
21
+ '/Library/Application Support/ClaudeCode/CLAUDE.md',
22
+ '/etc/ClaudeCode/CLAUDE.md',
23
+ process.env['ProgramData'] ? path.join(process.env['ProgramData'], 'ClaudeCode', 'CLAUDE.md') : '',
24
+ ].filter(Boolean);
25
+ function estimateTokens(body) {
26
+ // Rough heuristic: ~4 chars per token. Cheap; the UI tolerates approximation.
27
+ return Math.max(1, Math.round(body.length / 4));
28
+ }
29
+ function relativeTime(ms) {
30
+ const diff = Date.now() - ms;
31
+ const sec = Math.round(diff / 1000);
32
+ if (sec < 60)
33
+ return `${sec}s ago`;
34
+ const min = Math.round(sec / 60);
35
+ if (min < 60)
36
+ return `${min}m ago`;
37
+ const hr = Math.round(min / 60);
38
+ if (hr < 24)
39
+ return `${hr}h ago`;
40
+ const day = Math.round(hr / 24);
41
+ if (day < 7)
42
+ return `${day}d ago`;
43
+ const d = new Date(ms);
44
+ return d.toLocaleDateString();
45
+ }
46
+ async function tryRead(filePath) {
47
+ try {
48
+ const [body, stat] = await Promise.all([fs.readFile(filePath, 'utf-8'), fs.stat(filePath)]);
49
+ return { body, modified: stat.mtimeMs };
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ function extractImports(body) {
56
+ const out = [];
57
+ for (const line of body.split('\n')) {
58
+ const m = line.match(/^@(\S+)/);
59
+ if (m && m[1])
60
+ out.push(m[1]);
61
+ }
62
+ return out;
63
+ }
64
+ function expandHome(p) {
65
+ if (p.startsWith('~/'))
66
+ return path.join(os.homedir(), p.slice(2));
67
+ return p;
68
+ }
69
+ function pathForUi(absolute) {
70
+ const home = os.homedir();
71
+ if (absolute.startsWith(home + path.sep))
72
+ return '~/' + absolute.slice(home.length + 1);
73
+ return absolute;
74
+ }
75
+ function makeFile(opts) {
76
+ return {
77
+ id: opts.id,
78
+ level: opts.level,
79
+ type: opts.type,
80
+ name: opts.name,
81
+ scope: opts.scope,
82
+ path: pathForUi(opts.absolutePath),
83
+ tokens: estimateTokens(opts.body),
84
+ lines: opts.body.split('\n').length,
85
+ modified: relativeTime(opts.modifiedMs),
86
+ body: opts.body,
87
+ readOnly: opts.readOnly,
88
+ imports: opts.imports?.length ? opts.imports : undefined,
89
+ };
90
+ }
91
+ async function collectEnterprise() {
92
+ const out = [];
93
+ for (const p of ENTERPRISE_PATHS) {
94
+ const file = await tryRead(p);
95
+ if (file) {
96
+ out.push(makeFile({
97
+ id: 'm-ent',
98
+ level: 'enterprise',
99
+ type: 'instruction',
100
+ name: 'CLAUDE.md',
101
+ scope: 'managed',
102
+ absolutePath: p,
103
+ body: file.body,
104
+ modifiedMs: file.modified,
105
+ readOnly: true,
106
+ }));
107
+ break;
108
+ }
109
+ }
110
+ return out;
111
+ }
112
+ async function collectUser() {
113
+ const p = path.join(os.homedir(), '.claude', 'CLAUDE.md');
114
+ const file = await tryRead(p);
115
+ if (!file)
116
+ return [];
117
+ return [makeFile({
118
+ id: 'm-user',
119
+ level: 'user',
120
+ type: 'instruction',
121
+ name: 'CLAUDE.md',
122
+ scope: '~/.claude',
123
+ absolutePath: p,
124
+ body: file.body,
125
+ modifiedMs: file.modified,
126
+ })];
127
+ }
128
+ async function collectProject(projectRoot) {
129
+ const out = [];
130
+ const proj = path.join(projectRoot, 'CLAUDE.md');
131
+ const projFile = await tryRead(proj);
132
+ const importMap = new Map();
133
+ if (projFile) {
134
+ const imports = extractImports(projFile.body);
135
+ const resolvedIds = [];
136
+ for (const imp of imports) {
137
+ const abs = path.isAbsolute(imp) ? imp : path.join(projectRoot, imp);
138
+ const sub = await tryRead(abs);
139
+ if (sub) {
140
+ const id = 'm-imp-' + Buffer.from(imp).toString('base64').replace(/=/g, '').slice(0, 10);
141
+ importMap.set(imp, makeFile({
142
+ id,
143
+ level: 'import',
144
+ type: 'import',
145
+ name: path.basename(abs),
146
+ scope: '@import',
147
+ absolutePath: abs,
148
+ body: sub.body,
149
+ modifiedMs: sub.modified,
150
+ }));
151
+ resolvedIds.push(id);
152
+ }
153
+ }
154
+ out.push(makeFile({
155
+ id: 'm-proj',
156
+ level: 'project',
157
+ type: 'instruction',
158
+ name: 'CLAUDE.md',
159
+ scope: 'project root',
160
+ absolutePath: proj,
161
+ body: projFile.body,
162
+ modifiedMs: projFile.modified,
163
+ imports: resolvedIds,
164
+ }));
165
+ for (const im of importMap.values())
166
+ out.push(im);
167
+ }
168
+ // Scan one level of subdirectories for nested CLAUDE.md files. Keeps the
169
+ // walk cheap — going deeper would need ignoring node_modules etc.
170
+ try {
171
+ const entries = await fs.readdir(projectRoot, { withFileTypes: true });
172
+ for (const ent of entries) {
173
+ if (!ent.isDirectory())
174
+ continue;
175
+ if (ent.name.startsWith('.') || ent.name === 'node_modules' || ent.name === 'dist')
176
+ continue;
177
+ const subPath = path.join(projectRoot, ent.name, 'CLAUDE.md');
178
+ const sub = await tryRead(subPath);
179
+ if (sub) {
180
+ out.push(makeFile({
181
+ id: 'm-sub-' + ent.name,
182
+ level: 'subdir',
183
+ type: 'instruction',
184
+ name: 'CLAUDE.md',
185
+ scope: ent.name,
186
+ absolutePath: subPath,
187
+ body: sub.body,
188
+ modifiedMs: sub.modified,
189
+ }));
190
+ }
191
+ }
192
+ }
193
+ catch {
194
+ // Project root not readable — ignore, return what we already have.
195
+ }
196
+ return out;
197
+ }
198
+ async function collectNotes() {
199
+ const dir = path.join(os.homedir(), '.claude', 'memory');
200
+ const out = [];
201
+ let entries;
202
+ try {
203
+ entries = await fs.readdir(dir, { withFileTypes: true });
204
+ }
205
+ catch {
206
+ return out;
207
+ }
208
+ for (const ent of entries) {
209
+ if (!ent.isFile() || !ent.name.endsWith('.md'))
210
+ continue;
211
+ const abs = path.join(dir, ent.name);
212
+ const file = await tryRead(abs);
213
+ if (!file)
214
+ continue;
215
+ const tags = Array.from(file.body.matchAll(/#([A-Za-z][A-Za-z0-9_-]*)/g))
216
+ .map((m) => m[1])
217
+ .filter((t) => typeof t === 'string')
218
+ .slice(0, 6);
219
+ out.push(makeFile({
220
+ id: 'n-' + ent.name.replace(/\.md$/, ''),
221
+ level: 'note',
222
+ type: 'note',
223
+ name: ent.name,
224
+ scope: 'memory tool',
225
+ absolutePath: abs,
226
+ body: file.body,
227
+ modifiedMs: file.modified,
228
+ }));
229
+ if (tags.length) {
230
+ const last = out[out.length - 1];
231
+ if (last)
232
+ last.tags = tags;
233
+ }
234
+ }
235
+ return out;
236
+ }
237
+ export async function registerMemoryRoutes(app) {
238
+ app.get('/api/memory', async (req) => {
239
+ // Memory has three global tiers (enterprise / user / notes) and one
240
+ // project tier. The global tiers always render; the project tier
241
+ // requires a project path. We try, in order:
242
+ // 1. The `?project=<slug>` query arg (decoded from Claude's slug).
243
+ // 2. The `?projectPath=` arg if the caller has the absolute path handy.
244
+ // 3. The primary SpecShip's projectRoot, if configured at boot.
245
+ // When none is available, the response just omits the project tier
246
+ // (no 409 — the user can still see their global memory).
247
+ let projectRoot = null;
248
+ if (req.query.project)
249
+ projectRoot = decodeProjectSlug(req.query.project);
250
+ else if (req.query.projectPath)
251
+ projectRoot = req.query.projectPath;
252
+ else if (app.primaryCg?.getProjectRoot)
253
+ projectRoot = app.primaryCg.getProjectRoot();
254
+ const [enterprise, user, project, notes] = await Promise.all([
255
+ collectEnterprise(),
256
+ collectUser(),
257
+ projectRoot ? collectProject(projectRoot) : Promise.resolve([]),
258
+ collectNotes(),
259
+ ]);
260
+ const files = [...enterprise, ...user, ...project, ...notes];
261
+ const resp = {
262
+ totalTokens: files.reduce((a, b) => a + b.tokens, 0),
263
+ instructionCount: files.filter((f) => f.type === 'instruction').length,
264
+ noteCount: files.filter((f) => f.type === 'note').length,
265
+ importCount: files.filter((f) => f.type === 'import').length,
266
+ files,
267
+ };
268
+ return resp;
269
+ });
270
+ }
271
+ // Re-export the helper for tests
272
+ export const _testing = { expandHome, pathForUi, estimateTokens, extractImports };
@@ -0,0 +1,197 @@
1
+ /**
2
+ * GET /api/projects — enumerate every project visible to Claude Code
3
+ * (every dir under ~/.claude/projects/).
4
+ * GET /api/projects/events — SSE stream that pushes a refresh whenever the
5
+ * watcher sees the project list change. Used by
6
+ * the desktop UI's Project picker so newly opened
7
+ * projects appear without a refresh.
8
+ *
9
+ * Why this lives in its own route file: the rest of the analytics endpoints
10
+ * already read ~/.claude/projects/ via the ingest pipeline. This route adds
11
+ * a *navigation* primitive on top of that data — "what projects does the
12
+ * user have at all?" — without coupling to the specship instance.
13
+ */
14
+ import { EventEmitter } from 'node:events';
15
+ import { promises as fs, watch } from 'node:fs';
16
+ import os from 'node:os';
17
+ import path from 'node:path';
18
+ import { decodeProjectSlug } from '../ingest/ingestor.js';
19
+ const projectsBus = new EventEmitter();
20
+ projectsBus.setMaxListeners(0); // many SSE subscribers + internal
21
+ async function enumerate(claudeRoot) {
22
+ let entries;
23
+ try {
24
+ entries = await fs.readdir(claudeRoot, { withFileTypes: true });
25
+ }
26
+ catch {
27
+ return [];
28
+ }
29
+ const out = [];
30
+ for (const ent of entries) {
31
+ if (!ent.isDirectory())
32
+ continue;
33
+ const slug = ent.name;
34
+ const decoded = decodeProjectSlug(slug);
35
+ const projDir = path.join(claudeRoot, slug);
36
+ let sessionCount = 0;
37
+ let lastModifiedMs = 0;
38
+ try {
39
+ const inside = await fs.readdir(projDir, { withFileTypes: true });
40
+ for (const f of inside) {
41
+ if (f.isFile() && f.name.toLowerCase().endsWith('.jsonl')) {
42
+ sessionCount++;
43
+ try {
44
+ const stat = await fs.stat(path.join(projDir, f.name));
45
+ if (stat.mtimeMs > lastModifiedMs)
46
+ lastModifiedMs = stat.mtimeMs;
47
+ }
48
+ catch { /* ignore */ }
49
+ }
50
+ }
51
+ }
52
+ catch { /* project dir disappeared mid-scan — skip its files */ }
53
+ let exists = false;
54
+ let initialized = false;
55
+ try {
56
+ const st = await fs.stat(decoded);
57
+ exists = st.isDirectory();
58
+ if (exists) {
59
+ try {
60
+ const cg = await fs.stat(path.join(decoded, '.specship'));
61
+ initialized = cg.isDirectory();
62
+ }
63
+ catch { /* not initialized — OK */ }
64
+ }
65
+ }
66
+ catch { /* path missing — OK, still surface it so UI can flag it */ }
67
+ out.push({ slug, path: decoded, exists, initialized, sessionCount, lastModifiedMs });
68
+ }
69
+ // Recent activity first; ties broken by path.
70
+ out.sort((a, b) => {
71
+ if (b.lastModifiedMs !== a.lastModifiedMs)
72
+ return b.lastModifiedMs - a.lastModifiedMs;
73
+ return a.path.localeCompare(b.path);
74
+ });
75
+ return out;
76
+ }
77
+ // --- Watcher (singleton across the process) ---------------------------------
78
+ let watcher = null;
79
+ let lastSlugs = new Set();
80
+ let debounceHandle = null;
81
+ let watchedRoot = null;
82
+ function startWatcher(claudeRoot, verbose) {
83
+ if (watcher)
84
+ return;
85
+ watchedRoot = claudeRoot;
86
+ try {
87
+ watcher = watch(claudeRoot, { persistent: true }, () => {
88
+ scheduleRescan(claudeRoot);
89
+ });
90
+ if (verbose)
91
+ console.error(`[projects] watching ${claudeRoot}`);
92
+ // Warm the cache so the first SSE snapshot has accurate state and the
93
+ // first change diff is meaningful.
94
+ void enumerate(claudeRoot).then((list) => {
95
+ lastSlugs = new Set(list.map((p) => p.slug));
96
+ });
97
+ }
98
+ catch (err) {
99
+ if (verbose)
100
+ console.error(`[projects] watch failed for ${claudeRoot}:`, err instanceof Error ? err.message : String(err));
101
+ // Directory may not exist yet — that's OK, every /api/projects GET
102
+ // returns [] until it does. We don't retry here; if the watcher is
103
+ // critical the user can restart the server after creating it.
104
+ }
105
+ }
106
+ function scheduleRescan(claudeRoot) {
107
+ if (debounceHandle)
108
+ clearTimeout(debounceHandle);
109
+ debounceHandle = setTimeout(async () => {
110
+ debounceHandle = null;
111
+ const list = await enumerate(claudeRoot);
112
+ const newSlugs = new Set(list.map((p) => p.slug));
113
+ const added = list.filter((p) => !lastSlugs.has(p.slug));
114
+ const removed = [...lastSlugs].filter((s) => !newSlugs.has(s));
115
+ lastSlugs = newSlugs;
116
+ if (added.length || removed.length) {
117
+ projectsBus.emit('change', { added, removed, list });
118
+ }
119
+ // Refresh is always emitted so subscribers can pick up lastModified /
120
+ // sessionCount drift even without slug-level changes.
121
+ projectsBus.emit('refresh', { list });
122
+ }, 350);
123
+ }
124
+ function stopWatcher() {
125
+ if (watcher) {
126
+ try {
127
+ watcher.close();
128
+ }
129
+ catch { /* ignore */ }
130
+ watcher = null;
131
+ }
132
+ if (debounceHandle) {
133
+ clearTimeout(debounceHandle);
134
+ debounceHandle = null;
135
+ }
136
+ watchedRoot = null;
137
+ }
138
+ // --- Route registration -----------------------------------------------------
139
+ export async function registerProjectsRoutes(app) {
140
+ const claudeRoot = path.join(os.homedir(), '.claude', 'projects');
141
+ startWatcher(claudeRoot, false);
142
+ app.addHook('onClose', async () => { stopWatcher(); });
143
+ app.get('/api/projects', async () => {
144
+ return { claudeRoot, projects: await enumerate(claudeRoot) };
145
+ });
146
+ app.get('/api/projects/events', async (req, reply) => {
147
+ reply.raw.setHeader('Content-Type', 'text/event-stream');
148
+ reply.raw.setHeader('Cache-Control', 'no-cache');
149
+ reply.raw.setHeader('Connection', 'keep-alive');
150
+ reply.raw.flushHeaders();
151
+ let closed = false;
152
+ const onClose = () => {
153
+ closed = true;
154
+ projectsBus.off('change', onChange);
155
+ projectsBus.off('refresh', onRefresh);
156
+ clearInterval(hb);
157
+ };
158
+ req.raw.on('close', onClose);
159
+ const writeEvent = (event, data) => {
160
+ if (closed)
161
+ return;
162
+ try {
163
+ reply.raw.write(`event: ${event}\n`);
164
+ reply.raw.write(`data: ${JSON.stringify(data)}\n\n`);
165
+ }
166
+ catch { /* socket gone */
167
+ closed = true;
168
+ }
169
+ };
170
+ const initial = await enumerate(claudeRoot);
171
+ lastSlugs = new Set(initial.map((p) => p.slug));
172
+ writeEvent('snapshot', { claudeRoot, projects: initial });
173
+ const onChange = (payload) => {
174
+ writeEvent('change', payload);
175
+ };
176
+ const onRefresh = (payload) => {
177
+ writeEvent('refresh', payload);
178
+ };
179
+ projectsBus.on('change', onChange);
180
+ projectsBus.on('refresh', onRefresh);
181
+ // Heartbeat keeps proxies / NATs from dropping the idle connection.
182
+ const hb = setInterval(() => {
183
+ if (closed) {
184
+ clearInterval(hb);
185
+ return;
186
+ }
187
+ try {
188
+ reply.raw.write(`: ping\n\n`);
189
+ }
190
+ catch {
191
+ onClose();
192
+ }
193
+ }, 30000);
194
+ });
195
+ }
196
+ /** Test seam — re-export so __tests__ can stop the watcher between cases. */
197
+ export const _testing = { stopWatcher, watchedRoot: () => watchedRoot };