@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,101 @@
1
+ /**
2
+ * ProjectRegistry — lazy-opens and caches SpecShip instances per project
3
+ * path, keyed by absolute path. LRU evicts when the cache exceeds maxOpen
4
+ * so a long-running server doesn't hold every project's SQLite handle
5
+ * forever.
6
+ *
7
+ * Used by the Fastify routes (via `app.activeCg(req)`) to back the desktop
8
+ * app's project picker: when the user switches projects in the UI, the
9
+ * specship-scoped surfaces (graph, specs, drift, workflows, status) start
10
+ * reading from the matching instance — opened on demand if it isn't
11
+ * already cached.
12
+ *
13
+ * Analytics endpoints (`/api/claude/*`, `/api/memory`) read from a single
14
+ * "primary" instance instead, since their data (JSONL ingest, CLAUDE.md
15
+ * hierarchy) is global to the user, not scoped to one project.
16
+ */
17
+ import { decodeProjectSlug } from './ingest/ingestor.js';
18
+ export class ProjectRegistry {
19
+ cache = new Map();
20
+ maxOpen;
21
+ verbose;
22
+ openImpl;
23
+ constructor(opts, defaultOpenImpl) {
24
+ this.maxOpen = opts.maxOpen ?? 5;
25
+ this.verbose = opts.verbose ?? false;
26
+ this.openImpl = opts.openImpl ?? defaultOpenImpl;
27
+ }
28
+ /**
29
+ * Return the cached SpecShip for `projectPath`, opening it on demand.
30
+ * Returns null when the path isn't initialized (no `.specship/`) or
31
+ * the open throws — callers surface a friendly UI error.
32
+ */
33
+ async get(projectPath) {
34
+ const existing = this.cache.get(projectPath);
35
+ if (existing) {
36
+ existing.lastAccess = Date.now();
37
+ return existing.cg;
38
+ }
39
+ try {
40
+ const cg = await this.openImpl(projectPath);
41
+ this.cache.set(projectPath, { cg, lastAccess: Date.now() });
42
+ this.evictIfNeeded();
43
+ if (this.verbose)
44
+ console.error(`[registry] opened ${projectPath} (cache size ${this.cache.size})`);
45
+ return cg;
46
+ }
47
+ catch (err) {
48
+ if (this.verbose)
49
+ console.error(`[registry] open failed for ${projectPath}: ${err instanceof Error ? err.message : String(err)}`);
50
+ return null;
51
+ }
52
+ }
53
+ /**
54
+ * Look up by Claude Code's slug-encoded project name
55
+ * (`-Users-alice-foo` → `/Users/alice/foo`).
56
+ */
57
+ async getBySlug(slug) {
58
+ return this.get(decodeProjectSlug(slug));
59
+ }
60
+ /** Currently-cached project paths (most-recent-access first). */
61
+ openPaths() {
62
+ return [...this.cache.entries()]
63
+ .sort((a, b) => b[1].lastAccess - a[1].lastAccess)
64
+ .map(([k]) => k);
65
+ }
66
+ has(projectPath) { return this.cache.has(projectPath); }
67
+ /** Close + drop every cached instance. Called on server shutdown. */
68
+ closeAll() {
69
+ for (const [, v] of this.cache) {
70
+ try {
71
+ v.cg.close();
72
+ }
73
+ catch { /* ignore */ }
74
+ }
75
+ this.cache.clear();
76
+ }
77
+ evictIfNeeded() {
78
+ if (this.cache.size <= this.maxOpen)
79
+ return;
80
+ let oldestKey = null;
81
+ let oldestTime = Infinity;
82
+ for (const [k, v] of this.cache) {
83
+ if (v.lastAccess < oldestTime) {
84
+ oldestTime = v.lastAccess;
85
+ oldestKey = k;
86
+ }
87
+ }
88
+ if (oldestKey) {
89
+ const entry = this.cache.get(oldestKey);
90
+ if (entry) {
91
+ try {
92
+ entry.cg.close();
93
+ }
94
+ catch { /* ignore */ }
95
+ }
96
+ this.cache.delete(oldestKey);
97
+ if (this.verbose)
98
+ console.error(`[registry] evicted ${oldestKey}`);
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,480 @@
1
+ /**
2
+ * Claude Code analytics routes.
3
+ *
4
+ * All queries hit specship's SQLite directly. The ingest worker
5
+ * (`@selvakumaresra/specship-ingest`) writes to claude_* tables; this layer
6
+ * just rolls up.
7
+ *
8
+ * Endpoints:
9
+ * GET /api/claude/projects — every indexed Claude project
10
+ * GET /api/claude/sessions?project=&limit= — sessions list
11
+ * GET /api/claude/session/:id — session detail (prompts + tools)
12
+ * GET /api/claude/heatmap?range= — file/tool/subagent heatmaps
13
+ * GET /api/claude/costs?range= — cost rollup, timeseries, per-model
14
+ * GET /api/claude/compare — per-project cost comparison
15
+ * GET /api/claude/tips — rule-based tips engine output
16
+ * POST /api/claude/ingest — force a one-shot ingest pass
17
+ */
18
+ const RANGE_WINDOW_MS = {
19
+ today: 24 * 60 * 60 * 1000,
20
+ week: 7 * 24 * 60 * 60 * 1000,
21
+ month: 30 * 24 * 60 * 60 * 1000,
22
+ all: Number.MAX_SAFE_INTEGER,
23
+ };
24
+ function rangeKey(input) {
25
+ if (input === 'today' || input === 'week' || input === 'month' || input === 'all')
26
+ return input;
27
+ return 'week';
28
+ }
29
+ function rangeStart(key) {
30
+ if (key === 'all')
31
+ return 0;
32
+ return Date.now() - RANGE_WINDOW_MS[key];
33
+ }
34
+ /**
35
+ * Get the internal SQLite handle off the DatabaseConnection so we can run
36
+ * Claude-specific aggregate queries directly. SpecShip exposes this via
37
+ * `getDb()`-style accessors. Falls back to digging via the queries property.
38
+ */
39
+ function getDb(cg) {
40
+ // SpecShip exposes the underlying DB via its DatabaseConnection. Look it
41
+ // up via the private field as a fallback — works because it's the same
42
+ // shape regardless of which adapter (better-sqlite3 / node:sqlite) is active.
43
+ const anyCg = cg;
44
+ if (anyCg.db?.getDb)
45
+ return anyCg.db.getDb();
46
+ if (anyCg.queries?.db)
47
+ return anyCg.queries.db;
48
+ throw new Error('specship DB handle not accessible from server context');
49
+ }
50
+ export async function registerClaudeRoutes(app) {
51
+ /**
52
+ * Analytics routes share one SQLite — the boot-time "primary" project's
53
+ * specship.db hosts the cross-project claude_* tables. Without a primary
54
+ * the JSONL ingest has nowhere to write and there's nothing to query, so
55
+ * every analytics handler asks here first.
56
+ */
57
+ function requirePrimary(reply) {
58
+ if (!app.primaryCg) {
59
+ reply.code(409).send({ error: 'analytics unavailable: no primary project configured', code: 'no_primary' });
60
+ return null;
61
+ }
62
+ return app.primaryCg;
63
+ }
64
+ app.get('/api/claude/projects', async (_req, reply) => {
65
+ const cg = requirePrimary(reply);
66
+ if (!cg)
67
+ return;
68
+ const db = getDb(cg);
69
+ const rows = db.prepare(`
70
+ SELECT p.path, p.name, p.first_seen, p.last_seen,
71
+ COUNT(s.id) as sessions,
72
+ COALESCE(SUM(s.total_cost_usd), 0) as cost,
73
+ COALESCE(SUM(s.total_cache_read_tokens), 0) as cacheRead,
74
+ COALESCE(SUM(s.total_cache_creation_tokens + s.total_input_tokens), 0) as totalInput,
75
+ COALESCE(SUM(s.prompt_count), 0) as prompts
76
+ FROM claude_projects p
77
+ LEFT JOIN claude_sessions s ON s.project_path = p.path
78
+ GROUP BY p.path
79
+ ORDER BY cost DESC
80
+ `).all();
81
+ return { projects: rows };
82
+ });
83
+ app.get('/api/claude/sessions', async (req, reply) => {
84
+ const cg = requirePrimary(reply);
85
+ if (!cg)
86
+ return;
87
+ const db = getDb(cg);
88
+ const limit = Math.min(parseInt(req.query.limit ?? '100', 10) || 100, 500);
89
+ const since = rangeStart(rangeKey(req.query.range));
90
+ const params = [since];
91
+ let whereProject = '';
92
+ if (req.query.project) {
93
+ whereProject = ' AND project_path = ?';
94
+ params.push(req.query.project);
95
+ }
96
+ params.push(limit);
97
+ const sessions = db.prepare(`
98
+ SELECT * FROM claude_sessions
99
+ WHERE started_at >= ?${whereProject}
100
+ ORDER BY started_at DESC
101
+ LIMIT ?
102
+ `).all(...params);
103
+ return { sessions };
104
+ });
105
+ app.get('/api/claude/session/:id', async (req, reply) => {
106
+ const cg = requirePrimary(reply);
107
+ if (!cg)
108
+ return;
109
+ const db = getDb(cg);
110
+ const session = db.prepare('SELECT * FROM claude_sessions WHERE id = ?').get(req.params.id);
111
+ if (!session)
112
+ return reply.code(404).send({ error: 'session not found' });
113
+ const prompts = db.prepare(`
114
+ SELECT * FROM claude_prompts WHERE session_id = ? ORDER BY ts ASC
115
+ `).all(req.params.id);
116
+ const toolCalls = db.prepare(`
117
+ SELECT * FROM claude_tool_calls WHERE session_id = ? ORDER BY ts ASC
118
+ `).all(req.params.id);
119
+ return { session, prompts, toolCalls };
120
+ });
121
+ app.get('/api/claude/heatmap', async (req, reply) => {
122
+ const cg = requirePrimary(reply);
123
+ if (!cg)
124
+ return;
125
+ const db = getDb(cg);
126
+ const since = rangeStart(rangeKey(req.query.range));
127
+ // Files heatmap — input_summary doubles as the file path for Read/Edit/Write.
128
+ const files = db.prepare(`
129
+ SELECT input_summary as path, COUNT(*) as calls, SUM(result_length) as resultBytes
130
+ FROM claude_tool_calls
131
+ WHERE ts >= ? AND tool_name IN ('Read','Edit','Write','NotebookEdit') AND input_summary != ''
132
+ GROUP BY input_summary
133
+ ORDER BY calls DESC
134
+ LIMIT 100
135
+ `).all(since);
136
+ // Tools heatmap.
137
+ const tools = db.prepare(`
138
+ SELECT tool_name as name, COUNT(*) as calls, SUM(result_length) as resultBytes
139
+ FROM claude_tool_calls
140
+ WHERE ts >= ?
141
+ GROUP BY tool_name
142
+ ORDER BY calls DESC
143
+ `).all(since);
144
+ // Subagent attribution (is_sidechain at the prompt level — main vs. sidechain rollup).
145
+ const subagents = db.prepare(`
146
+ SELECT
147
+ CASE WHEN p.is_sidechain = 1 THEN 'subagent' ELSE 'main' END as type,
148
+ COUNT(*) as prompts,
149
+ SUM(p.input_tokens + p.output_tokens + p.cache_creation_tokens + p.cache_read_tokens) as tokens,
150
+ SUM(p.cost_usd) as cost
151
+ FROM claude_prompts p
152
+ WHERE p.ts >= ?
153
+ GROUP BY type
154
+ `).all(since);
155
+ // Subagent breakdown by name — Task tool calls grouped by subagent_type.
156
+ // input_summary is the JSON-serialized tool input; json_extract pulls
157
+ // out subagent_type (defaults to 'general-purpose' when unset).
158
+ const subagentByName = db.prepare(`
159
+ SELECT
160
+ COALESCE(NULLIF(json_extract(input_summary, '$.subagent_type'), ''), 'general-purpose') as name,
161
+ COUNT(*) as calls,
162
+ MIN(ts) as firstSeen,
163
+ MAX(ts) as lastSeen
164
+ FROM claude_tool_calls
165
+ WHERE ts >= ? AND tool_name = 'Task'
166
+ GROUP BY name
167
+ ORDER BY calls DESC
168
+ `).all(since);
169
+ return { files, tools, subagents, subagentByName };
170
+ });
171
+ /**
172
+ * Drill-down: which sessions touched a given file (via Read/Edit/Write).
173
+ * Used by the heatmap page when the user clicks a file cell.
174
+ */
175
+ app.get('/api/claude/heatmap/file', async (req, reply) => {
176
+ const path = req.query.path;
177
+ if (!path)
178
+ return reply.code(400).send({ error: 'path required' });
179
+ const cg = requirePrimary(reply);
180
+ if (!cg)
181
+ return;
182
+ const db = getDb(cg);
183
+ const since = rangeStart(rangeKey(req.query.range));
184
+ const sessions = db.prepare(`
185
+ SELECT t.session_id, s.last_model, s.project_path,
186
+ COUNT(*) as calls, COALESCE(SUM(t.result_length), 0) as bytes,
187
+ MAX(t.ts) as lastTs, MIN(t.ts) as firstTs
188
+ FROM claude_tool_calls t
189
+ LEFT JOIN claude_sessions s ON s.id = t.session_id
190
+ WHERE t.input_summary = ? AND t.ts >= ? AND t.tool_name IN ('Read','Edit','Write','NotebookEdit')
191
+ GROUP BY t.session_id
192
+ ORDER BY calls DESC
193
+ LIMIT 50
194
+ `).all(path, since);
195
+ const byTool = db.prepare(`
196
+ SELECT tool_name as name, COUNT(*) as calls, COALESCE(SUM(result_length), 0) as bytes
197
+ FROM claude_tool_calls
198
+ WHERE input_summary = ? AND ts >= ? AND tool_name IN ('Read','Edit','Write','NotebookEdit')
199
+ GROUP BY tool_name
200
+ ORDER BY calls DESC
201
+ `).all(path, since);
202
+ return { path, sessions, byTool };
203
+ });
204
+ /**
205
+ * Drill-down: the top distinct inputs for a given tool (file paths for
206
+ * Read/Edit/Write, patterns for Grep/Glob, commands for Bash).
207
+ */
208
+ app.get('/api/claude/heatmap/tool', async (req, reply) => {
209
+ const name = req.query.name;
210
+ if (!name)
211
+ return reply.code(400).send({ error: 'name required' });
212
+ const cg = requirePrimary(reply);
213
+ if (!cg)
214
+ return;
215
+ const db = getDb(cg);
216
+ const since = rangeStart(rangeKey(req.query.range));
217
+ const totals = db.prepare(`
218
+ SELECT COUNT(*) as calls, COALESCE(SUM(result_length), 0) as bytes,
219
+ COUNT(DISTINCT session_id) as sessions
220
+ FROM claude_tool_calls
221
+ WHERE tool_name = ? AND ts >= ?
222
+ `).get(name, since);
223
+ const inputs = db.prepare(`
224
+ SELECT
225
+ CASE WHEN length(input_summary) > 120 THEN substr(input_summary, 1, 120) || '…'
226
+ ELSE input_summary END as input,
227
+ COUNT(*) as calls,
228
+ COALESCE(SUM(result_length), 0) as bytes,
229
+ MAX(ts) as lastTs
230
+ FROM claude_tool_calls
231
+ WHERE tool_name = ? AND ts >= ? AND input_summary != ''
232
+ GROUP BY input_summary
233
+ ORDER BY calls DESC
234
+ LIMIT 30
235
+ `).all(name, since);
236
+ const recentSessions = db.prepare(`
237
+ SELECT t.session_id, s.last_model, s.project_path, COUNT(*) as calls, MAX(t.ts) as lastTs
238
+ FROM claude_tool_calls t
239
+ LEFT JOIN claude_sessions s ON s.id = t.session_id
240
+ WHERE t.tool_name = ? AND t.ts >= ?
241
+ GROUP BY t.session_id
242
+ ORDER BY lastTs DESC
243
+ LIMIT 20
244
+ `).all(name, since);
245
+ return { tool: name, totals, inputs, recentSessions };
246
+ });
247
+ /**
248
+ * Drill-down: invocations of a specific subagent (by subagent_type name).
249
+ */
250
+ app.get('/api/claude/heatmap/subagent', async (req, reply) => {
251
+ const type = req.query.type;
252
+ if (!type)
253
+ return reply.code(400).send({ error: 'type required' });
254
+ const cg = requirePrimary(reply);
255
+ if (!cg)
256
+ return;
257
+ const db = getDb(cg);
258
+ const since = rangeStart(rangeKey(req.query.range));
259
+ const totals = db.prepare(`
260
+ SELECT COUNT(*) as calls, COUNT(DISTINCT session_id) as sessions
261
+ FROM claude_tool_calls
262
+ WHERE tool_name = 'Task' AND ts >= ?
263
+ AND COALESCE(NULLIF(json_extract(input_summary, '$.subagent_type'), ''), 'general-purpose') = ?
264
+ `).get(since, type);
265
+ const invocations = db.prepare(`
266
+ SELECT
267
+ t.session_id,
268
+ t.ts,
269
+ COALESCE(json_extract(t.input_summary, '$.description'), '') as description,
270
+ COALESCE(json_extract(t.input_summary, '$.prompt'), '') as prompt,
271
+ s.last_model
272
+ FROM claude_tool_calls t
273
+ LEFT JOIN claude_sessions s ON s.id = t.session_id
274
+ WHERE t.tool_name = 'Task' AND t.ts >= ?
275
+ AND COALESCE(NULLIF(json_extract(t.input_summary, '$.subagent_type'), ''), 'general-purpose') = ?
276
+ ORDER BY t.ts DESC
277
+ LIMIT 50
278
+ `).all(since, type);
279
+ return { subagent: type, totals, invocations };
280
+ });
281
+ app.get('/api/claude/cache', async (req, reply) => {
282
+ const cg = requirePrimary(reply);
283
+ if (!cg)
284
+ return;
285
+ const db = getDb(cg);
286
+ const since = rangeStart(rangeKey(req.query.range));
287
+ // Aggregate cache totals from claude_sessions for sessions in window.
288
+ const agg = db.prepare(`
289
+ SELECT
290
+ COALESCE(SUM(total_input_tokens), 0) as inp,
291
+ COALESCE(SUM(total_output_tokens), 0) as out,
292
+ COALESCE(SUM(total_cache_creation_tokens), 0) as cw,
293
+ COALESCE(SUM(total_cache_read_tokens), 0) as cr,
294
+ COALESCE(SUM(total_cost_usd), 0) as cost
295
+ FROM claude_sessions
296
+ WHERE started_at >= ?
297
+ `).get(since);
298
+ const total = (agg.inp ?? 0) + (agg.cw ?? 0) + (agg.cr ?? 0);
299
+ const readRate = total > 0 ? agg.cr / total : 0;
300
+ // Dollars saved estimate: cache_read tokens billed at ~10% of input;
301
+ // savings vs charging them at input rate ≈ 0.9 × (cr / 1M) × inputRate.
302
+ // Use Opus 4-7 input ($15/M) as a generous upper bound — the UI shows
303
+ // this as an approximation.
304
+ const dollarsSaved = ((agg.cr ?? 0) / 1_000_000) * 15 * 0.9;
305
+ return {
306
+ readRate,
307
+ creationTokens: agg.cw ?? 0,
308
+ readTokens: agg.cr ?? 0,
309
+ inputTokens: agg.inp ?? 0,
310
+ outputTokens: agg.out ?? 0,
311
+ totalCost: agg.cost ?? 0,
312
+ dollarsSaved,
313
+ // wowDelta would need historical snapshotting; placeholder until we
314
+ // add a rolling-window aggregate table. UI shows 0% with no arrow.
315
+ wowDelta: 0,
316
+ };
317
+ });
318
+ app.get('/api/claude/costs', async (req, reply) => {
319
+ const cg = requirePrimary(reply);
320
+ if (!cg)
321
+ return;
322
+ const db = getDb(cg);
323
+ const since = rangeStart(rangeKey(req.query.range));
324
+ const total = db.prepare(`SELECT SUM(total_cost_usd) as t FROM claude_sessions WHERE started_at >= ?`).get(since);
325
+ // Top prompts by cost.
326
+ const topPrompts = db.prepare(`
327
+ SELECT id, session_id, substr(text, 1, 200) as text, model, cost_usd,
328
+ input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens, ts
329
+ FROM claude_prompts
330
+ WHERE ts >= ? AND cost_usd > 0
331
+ ORDER BY cost_usd DESC
332
+ LIMIT 50
333
+ `).all(since);
334
+ // Daily timeseries: bucket by 24h windows.
335
+ const days = 30;
336
+ const dayMs = 24 * 60 * 60 * 1000;
337
+ const dayBoundary = Math.floor(Date.now() / dayMs) * dayMs - (days - 1) * dayMs;
338
+ const series = db.prepare(`
339
+ SELECT
340
+ CAST((ts - ?) / ? AS INTEGER) as bucket,
341
+ SUM(cost_usd) as cost,
342
+ COUNT(*) as prompts
343
+ FROM claude_prompts
344
+ WHERE ts >= ?
345
+ GROUP BY bucket
346
+ ORDER BY bucket ASC
347
+ `).all(dayBoundary, dayMs, dayBoundary);
348
+ // Densify with zeros for missing days.
349
+ const dense = [];
350
+ for (let i = 0; i < days; i++) {
351
+ const found = series.find((s) => s.bucket === i);
352
+ dense.push({ day: days - 1 - i, cost: found?.cost ?? 0, prompts: found?.prompts ?? 0 });
353
+ }
354
+ // By model.
355
+ const byModel = db.prepare(`
356
+ SELECT model, COUNT(*) as prompts, SUM(cost_usd) as cost
357
+ FROM claude_prompts
358
+ WHERE ts >= ? AND model IS NOT NULL
359
+ GROUP BY model
360
+ ORDER BY cost DESC
361
+ `).all(since);
362
+ return { total: total.t ?? 0, topPrompts, series: dense, byModel };
363
+ });
364
+ app.get('/api/claude/compare', async (_req, reply) => {
365
+ const cg = requirePrimary(reply);
366
+ if (!cg)
367
+ return;
368
+ const db = getDb(cg);
369
+ const rows = db.prepare(`
370
+ SELECT
371
+ p.path, p.name,
372
+ COUNT(s.id) as sessions,
373
+ COALESCE(SUM(s.total_cost_usd), 0) as cost,
374
+ COALESCE(AVG(s.total_cost_usd), 0) as avgCost,
375
+ COALESCE(SUM(s.prompt_count), 0) as prompts,
376
+ CASE WHEN SUM(s.total_input_tokens + s.total_cache_creation_tokens + s.total_cache_read_tokens) > 0
377
+ THEN CAST(SUM(s.total_cache_read_tokens) AS REAL) / SUM(s.total_input_tokens + s.total_cache_creation_tokens + s.total_cache_read_tokens)
378
+ ELSE 0 END as cacheHit
379
+ FROM claude_projects p
380
+ LEFT JOIN claude_sessions s ON s.project_path = p.path
381
+ GROUP BY p.path
382
+ ORDER BY cost DESC
383
+ `).all();
384
+ return { projects: rows };
385
+ });
386
+ /**
387
+ * Rule-based tips engine. Each rule is a SQL query that finds a wasteful
388
+ * pattern in the user's recent transcripts; the result is shaped into a
389
+ * tip card matching the design system's voice.
390
+ */
391
+ app.get('/api/claude/tips', async (_req, reply) => {
392
+ const cg = requirePrimary(reply);
393
+ if (!cg)
394
+ return;
395
+ const db = getDb(cg);
396
+ const tips = [];
397
+ // Rule 1: "you read X N times" — same file path Read more than 10 times in
398
+ // a single session.
399
+ const wastefulReads = db.prepare(`
400
+ SELECT session_id, input_summary as file, COUNT(*) as n
401
+ FROM claude_tool_calls
402
+ WHERE tool_name = 'Read' AND input_summary != ''
403
+ GROUP BY session_id, input_summary
404
+ HAVING n >= 10
405
+ ORDER BY n DESC
406
+ LIMIT 5
407
+ `).all();
408
+ for (const r of wastefulReads) {
409
+ tips.push({
410
+ id: 'wasteful_reads:' + r.session_id + ':' + r.file,
411
+ severity: 'error',
412
+ icon: 'wrench',
413
+ title: `You read ${r.file.split('/').pop()} ${r.n}× in one session — specship_explore covers it`,
414
+ why: 'Re-reading the same file burns input tokens every turn. A single structural query returns callers, callees, and linked specs at once.',
415
+ evidence: { session: r.session_id, detail: `Read(${r.file}) × ${r.n}` },
416
+ fix: `specship_explore --symbol ${r.file.replace(/\.\w+$/, '').split('/').pop()}`,
417
+ saving: '≈$0.10/read avoided',
418
+ });
419
+ }
420
+ // Rule 2: "tool returned X tokens" — any single tool call with result_length > 50000.
421
+ const heavyResults = db.prepare(`
422
+ SELECT id, session_id, tool_name, input_summary, result_length
423
+ FROM claude_tool_calls
424
+ WHERE result_length > 50000
425
+ ORDER BY result_length DESC
426
+ LIMIT 5
427
+ `).all();
428
+ for (const r of heavyResults) {
429
+ tips.push({
430
+ id: 'heavy_result:' + r.id,
431
+ severity: 'error',
432
+ icon: 'flame',
433
+ title: `${r.tool_name} returned ${Math.round(r.result_length / 1000)}k tokens — try a structural query`,
434
+ why: 'Tools that dump raw content into context are the dominant cost driver. A structural query returns just what the agent needs.',
435
+ evidence: { session: r.session_id, detail: `${r.tool_name}(${r.input_summary.slice(0, 100)}) → ${r.result_length} tokens` },
436
+ fix: r.tool_name === 'Bash' ? 'specship_search instead of Bash(grep)' : 'specship_explore on the symbol',
437
+ saving: `~$${((r.result_length / 1_000_000) * 15).toFixed(2)} on this call`,
438
+ });
439
+ }
440
+ // Rule 3: "cache miss rate" — sessions with > 10 prompts and cache_read_rate < 0.3.
441
+ const lowCache = db.prepare(`
442
+ SELECT s.id, s.total_cache_read_tokens as cr, s.total_input_tokens as ti, s.total_cache_creation_tokens as cw, s.prompt_count, s.last_model
443
+ FROM claude_sessions s
444
+ WHERE s.prompt_count >= 10
445
+ AND (s.total_input_tokens + s.total_cache_creation_tokens + s.total_cache_read_tokens) > 0
446
+ ORDER BY (CAST(s.total_cache_read_tokens AS REAL) / (s.total_input_tokens + s.total_cache_creation_tokens + s.total_cache_read_tokens)) ASC
447
+ LIMIT 3
448
+ `).all();
449
+ for (const r of lowCache) {
450
+ const total = r.ti + r.cw + r.cr;
451
+ const rate = total > 0 ? r.cr / total : 0;
452
+ if (rate >= 0.3)
453
+ continue;
454
+ tips.push({
455
+ id: 'low_cache:' + r.id,
456
+ severity: 'warn',
457
+ icon: 'database',
458
+ title: `Cache read rate is ${Math.round(rate * 100)}% on a ${r.prompt_count}-prompt session`,
459
+ why: 'When the prompt prefix changes every turn, the 1h cache gets invalidated. Pinning a stable system-prompt prefix lets the cache absorb most of your input.',
460
+ evidence: { session: r.id, detail: `cache_read=${(r.cr / 1_000_000).toFixed(2)}M / total=${(total / 1_000_000).toFixed(2)}M` },
461
+ fix: 'Pin a stable system-prompt prefix in .claude/settings.json',
462
+ saving: '~$X.XX / session (model-dependent)',
463
+ });
464
+ }
465
+ // Sort: errors before warns before info, then within bucket by saving heuristic.
466
+ const order = { error: 0, warn: 1, info: 2 };
467
+ tips.sort((a, b) => (order[a.severity] ?? 9) - (order[b.severity] ?? 9));
468
+ return { tips };
469
+ });
470
+ /**
471
+ * Force a one-shot ingest pass. Useful for "Refresh" button in the UI.
472
+ */
473
+ app.post('/api/claude/ingest', async () => {
474
+ const watcher = app.watcher;
475
+ if (!watcher)
476
+ return { ok: false, error: 'watcher not running' };
477
+ const stats = watcher.ingestNow();
478
+ return { ok: true, stats };
479
+ });
480
+ }