@xhulz/nina 0.33.0 → 0.35.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 (264) hide show
  1. package/README.md +4 -2
  2. package/bin/nina.mjs +219 -48
  3. package/package.json +1 -1
  4. package/releases/0.34.0/core/GAPS.md +125 -0
  5. package/releases/0.34.0/core/answered.json +29 -0
  6. package/releases/0.34.0/core/defaults/tree/.claude/agents/architect.md +1 -0
  7. package/releases/0.34.0/core/defaults/tree/.claude/agents/dba.md +3 -0
  8. package/releases/0.34.0/core/defaults/tree/.claude/agents/devops.md +1 -0
  9. package/releases/0.34.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
  10. package/releases/0.34.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
  11. package/releases/0.34.0/core/defaults/tree/.claude/agents/qa.md +2 -0
  12. package/releases/0.34.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
  13. package/releases/0.34.0/core/defaults/tree/.claude/agents/secops.md +3 -0
  14. package/releases/0.34.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
  15. package/releases/0.34.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
  16. package/releases/0.34.0/core/tree/.claude/agents/architect.md +127 -0
  17. package/releases/0.34.0/core/tree/.claude/agents/dba.md +110 -0
  18. package/releases/0.34.0/core/tree/.claude/agents/devops.md +114 -0
  19. package/releases/0.34.0/core/tree/.claude/agents/implementer.md +156 -0
  20. package/releases/0.34.0/core/tree/.claude/agents/integration-tester.md +168 -0
  21. package/releases/0.34.0/core/tree/.claude/agents/planner.md +132 -0
  22. package/releases/0.34.0/core/tree/.claude/agents/qa.md +148 -0
  23. package/releases/0.34.0/core/tree/.claude/agents/reviewer.md +183 -0
  24. package/releases/0.34.0/core/tree/.claude/agents/secops.md +121 -0
  25. package/releases/0.34.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
  26. package/releases/0.34.0/core/tree/.claude/agents/solidity-dev.md +123 -0
  27. package/releases/0.34.0/core/tree/.claude/agents-overview.md +64 -0
  28. package/releases/0.34.0/core/tree/.claude/frontend.md +10 -0
  29. package/releases/0.34.0/core/tree/.claude/graph.md +75 -0
  30. package/releases/0.34.0/core/tree/.claude/patterns.md +213 -0
  31. package/releases/0.34.0/core/tree/.claude/pills/README.md +146 -0
  32. package/releases/0.34.0/core/tree/.claude/pipeline.md +101 -0
  33. package/releases/0.34.0/core/tree/.claude/retrieval.md +103 -0
  34. package/releases/0.34.0/core/tree/.claude/router.md +198 -0
  35. package/releases/0.34.0/core/tree/.claude/templates/integration.md +45 -0
  36. package/releases/0.34.0/core/tree/CLAUDE.md +161 -0
  37. package/releases/0.34.0/core/tree/scripts/cost-watch.mjs +18 -0
  38. package/releases/0.34.0/core/tree/scripts/edit-guard.mjs +16 -0
  39. package/releases/0.34.0/core/tree/scripts/harness-check.mjs +71 -0
  40. package/releases/0.34.0/core/tree/scripts/loop-gate.mjs +19 -0
  41. package/releases/0.34.0/core/vocabulary.json +13 -0
  42. package/releases/0.34.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
  43. package/releases/0.34.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
  44. package/releases/0.34.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
  45. package/releases/0.34.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
  46. package/releases/0.34.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
  47. package/releases/0.34.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
  48. package/releases/0.34.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
  49. package/releases/0.34.0/surfaces/blockchain/tree/.claude/router.md +6 -0
  50. package/releases/0.34.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
  51. package/releases/0.34.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
  52. package/releases/0.34.0/surfaces/db/tree/.claude/agents/dba.md +50 -0
  53. package/releases/0.34.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
  54. package/releases/0.34.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
  55. package/releases/0.34.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
  56. package/releases/0.34.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
  57. package/releases/0.34.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
  58. package/releases/0.34.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
  59. package/releases/0.34.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
  60. package/releases/0.34.0/surfaces/db/tree/.claude/graph.md +8 -0
  61. package/releases/0.34.0/surfaces/db/tree/.claude/patterns.md +19 -0
  62. package/releases/0.34.0/surfaces/db/tree/.claude/pipeline.md +13 -0
  63. package/releases/0.34.0/surfaces/db/tree/.claude/retrieval.md +5 -0
  64. package/releases/0.34.0/surfaces/db/tree/.claude/router.md +4 -0
  65. package/releases/0.34.0/surfaces/db/tree/CLAUDE.md +17 -0
  66. package/releases/0.34.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
  67. package/releases/0.34.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
  68. package/releases/0.34.0/surfaces/edge-cf/tree/.claude/agents/devops.md +32 -0
  69. package/releases/0.34.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
  70. package/releases/0.34.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
  71. package/releases/0.34.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
  72. package/releases/0.34.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
  73. package/releases/0.34.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
  74. package/releases/0.34.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
  75. package/releases/0.34.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
  76. package/releases/0.34.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
  77. package/releases/0.34.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
  78. package/releases/0.34.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
  79. package/releases/0.34.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
  80. package/releases/0.34.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
  81. package/releases/0.34.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
  82. package/releases/0.34.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
  83. package/releases/0.34.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
  84. package/releases/0.34.0/surfaces/frontend/tree/.claude/router.md +14 -0
  85. package/releases/0.34.0/surfaces/frontend/tree/CLAUDE.md +5 -0
  86. package/releases/0.34.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
  87. package/releases/0.34.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
  88. package/releases/0.34.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
  89. package/releases/0.34.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
  90. package/releases/0.34.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
  91. package/releases/0.34.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
  92. package/releases/0.34.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
  93. package/releases/0.34.0/surfaces/integrations/tree/.claude/graph.md +10 -0
  94. package/releases/0.34.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
  95. package/releases/0.34.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
  96. package/releases/0.34.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
  97. package/releases/0.34.0/surfaces/integrations/tree/.claude/router.md +6 -0
  98. package/releases/0.34.0/surfaces/integrations/tree/CLAUDE.md +17 -0
  99. package/releases/0.34.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
  100. package/releases/0.34.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
  101. package/releases/0.34.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
  102. package/releases/0.34.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
  103. package/releases/0.34.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
  104. package/releases/0.34.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
  105. package/releases/0.34.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
  106. package/releases/0.34.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
  107. package/releases/0.34.0/surfaces/money/tree/.claude/graph.md +2 -0
  108. package/releases/0.34.0/surfaces/money/tree/.claude/patterns.md +49 -0
  109. package/releases/0.34.0/surfaces/money/tree/.claude/pipeline.md +2 -0
  110. package/releases/0.34.0/surfaces/money/tree/.claude/retrieval.md +2 -0
  111. package/releases/0.34.0/surfaces/money/tree/CLAUDE.md +11 -0
  112. package/releases/0.34.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
  113. package/releases/0.34.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
  114. package/releases/0.34.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
  115. package/releases/0.34.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
  116. package/releases/0.34.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
  117. package/releases/0.34.0/surfaces/pii/tree/CLAUDE.md +5 -0
  118. package/releases/0.34.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
  119. package/releases/0.34.0/surfaces/prisma/tree/.claude/agents/dba.md +39 -0
  120. package/releases/0.34.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
  121. package/releases/0.34.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
  122. package/releases/0.34.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
  123. package/releases/0.34.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
  124. package/releases/0.34.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
  125. package/releases/0.34.0/surfaces/prisma/tree/CLAUDE.md +5 -0
  126. package/releases/0.35.0/core/GAPS.md +125 -0
  127. package/releases/0.35.0/core/answered.json +29 -0
  128. package/releases/0.35.0/core/defaults/tree/.claude/agents/architect.md +1 -0
  129. package/releases/0.35.0/core/defaults/tree/.claude/agents/dba.md +3 -0
  130. package/releases/0.35.0/core/defaults/tree/.claude/agents/devops.md +1 -0
  131. package/releases/0.35.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
  132. package/releases/0.35.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
  133. package/releases/0.35.0/core/defaults/tree/.claude/agents/qa.md +2 -0
  134. package/releases/0.35.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
  135. package/releases/0.35.0/core/defaults/tree/.claude/agents/secops.md +3 -0
  136. package/releases/0.35.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
  137. package/releases/0.35.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
  138. package/releases/0.35.0/core/tree/.claude/agents/architect.md +127 -0
  139. package/releases/0.35.0/core/tree/.claude/agents/dba.md +110 -0
  140. package/releases/0.35.0/core/tree/.claude/agents/devops.md +114 -0
  141. package/releases/0.35.0/core/tree/.claude/agents/implementer.md +156 -0
  142. package/releases/0.35.0/core/tree/.claude/agents/integration-tester.md +168 -0
  143. package/releases/0.35.0/core/tree/.claude/agents/planner.md +132 -0
  144. package/releases/0.35.0/core/tree/.claude/agents/qa.md +153 -0
  145. package/releases/0.35.0/core/tree/.claude/agents/reviewer.md +183 -0
  146. package/releases/0.35.0/core/tree/.claude/agents/secops.md +121 -0
  147. package/releases/0.35.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
  148. package/releases/0.35.0/core/tree/.claude/agents/solidity-dev.md +123 -0
  149. package/releases/0.35.0/core/tree/.claude/agents-overview.md +64 -0
  150. package/releases/0.35.0/core/tree/.claude/frontend.md +10 -0
  151. package/releases/0.35.0/core/tree/.claude/graph.md +75 -0
  152. package/releases/0.35.0/core/tree/.claude/patterns.md +213 -0
  153. package/releases/0.35.0/core/tree/.claude/pills/README.md +146 -0
  154. package/releases/0.35.0/core/tree/.claude/pipeline.md +101 -0
  155. package/releases/0.35.0/core/tree/.claude/retrieval.md +103 -0
  156. package/releases/0.35.0/core/tree/.claude/router.md +198 -0
  157. package/releases/0.35.0/core/tree/.claude/templates/integration.md +45 -0
  158. package/releases/0.35.0/core/tree/CLAUDE.md +161 -0
  159. package/releases/0.35.0/core/tree/scripts/cost-watch.mjs +18 -0
  160. package/releases/0.35.0/core/tree/scripts/edit-guard.mjs +16 -0
  161. package/releases/0.35.0/core/tree/scripts/harness-check.mjs +71 -0
  162. package/releases/0.35.0/core/tree/scripts/loop-gate.mjs +19 -0
  163. package/releases/0.35.0/core/vocabulary.json +13 -0
  164. package/releases/0.35.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
  165. package/releases/0.35.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
  166. package/releases/0.35.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
  167. package/releases/0.35.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
  168. package/releases/0.35.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
  169. package/releases/0.35.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
  170. package/releases/0.35.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
  171. package/releases/0.35.0/surfaces/blockchain/tree/.claude/router.md +6 -0
  172. package/releases/0.35.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
  173. package/releases/0.35.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
  174. package/releases/0.35.0/surfaces/db/tree/.claude/agents/dba.md +50 -0
  175. package/releases/0.35.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
  176. package/releases/0.35.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
  177. package/releases/0.35.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
  178. package/releases/0.35.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
  179. package/releases/0.35.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
  180. package/releases/0.35.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
  181. package/releases/0.35.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
  182. package/releases/0.35.0/surfaces/db/tree/.claude/graph.md +8 -0
  183. package/releases/0.35.0/surfaces/db/tree/.claude/patterns.md +19 -0
  184. package/releases/0.35.0/surfaces/db/tree/.claude/pipeline.md +13 -0
  185. package/releases/0.35.0/surfaces/db/tree/.claude/retrieval.md +5 -0
  186. package/releases/0.35.0/surfaces/db/tree/.claude/router.md +4 -0
  187. package/releases/0.35.0/surfaces/db/tree/CLAUDE.md +17 -0
  188. package/releases/0.35.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
  189. package/releases/0.35.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
  190. package/releases/0.35.0/surfaces/edge-cf/tree/.claude/agents/devops.md +32 -0
  191. package/releases/0.35.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
  192. package/releases/0.35.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
  193. package/releases/0.35.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
  194. package/releases/0.35.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
  195. package/releases/0.35.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
  196. package/releases/0.35.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
  197. package/releases/0.35.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
  198. package/releases/0.35.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
  199. package/releases/0.35.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
  200. package/releases/0.35.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
  201. package/releases/0.35.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
  202. package/releases/0.35.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
  203. package/releases/0.35.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
  204. package/releases/0.35.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
  205. package/releases/0.35.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
  206. package/releases/0.35.0/surfaces/frontend/tree/.claude/router.md +14 -0
  207. package/releases/0.35.0/surfaces/frontend/tree/CLAUDE.md +5 -0
  208. package/releases/0.35.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
  209. package/releases/0.35.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
  210. package/releases/0.35.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
  211. package/releases/0.35.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
  212. package/releases/0.35.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
  213. package/releases/0.35.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
  214. package/releases/0.35.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
  215. package/releases/0.35.0/surfaces/integrations/tree/.claude/graph.md +10 -0
  216. package/releases/0.35.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
  217. package/releases/0.35.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
  218. package/releases/0.35.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
  219. package/releases/0.35.0/surfaces/integrations/tree/.claude/router.md +6 -0
  220. package/releases/0.35.0/surfaces/integrations/tree/CLAUDE.md +17 -0
  221. package/releases/0.35.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
  222. package/releases/0.35.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
  223. package/releases/0.35.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
  224. package/releases/0.35.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
  225. package/releases/0.35.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
  226. package/releases/0.35.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
  227. package/releases/0.35.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
  228. package/releases/0.35.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
  229. package/releases/0.35.0/surfaces/money/tree/.claude/graph.md +2 -0
  230. package/releases/0.35.0/surfaces/money/tree/.claude/patterns.md +49 -0
  231. package/releases/0.35.0/surfaces/money/tree/.claude/pipeline.md +2 -0
  232. package/releases/0.35.0/surfaces/money/tree/.claude/retrieval.md +2 -0
  233. package/releases/0.35.0/surfaces/money/tree/CLAUDE.md +11 -0
  234. package/releases/0.35.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
  235. package/releases/0.35.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
  236. package/releases/0.35.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
  237. package/releases/0.35.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
  238. package/releases/0.35.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
  239. package/releases/0.35.0/surfaces/pii/tree/CLAUDE.md +5 -0
  240. package/releases/0.35.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
  241. package/releases/0.35.0/surfaces/prisma/tree/.claude/agents/dba.md +39 -0
  242. package/releases/0.35.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
  243. package/releases/0.35.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
  244. package/releases/0.35.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
  245. package/releases/0.35.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
  246. package/releases/0.35.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
  247. package/releases/0.35.0/surfaces/prisma/tree/CLAUDE.md +5 -0
  248. package/src/agentrun.mjs +91 -12
  249. package/src/commands/check.mjs +12 -8
  250. package/src/commands/compose.mjs +85 -9
  251. package/src/commands/export.mjs +9 -7
  252. package/src/commands/init.mjs +13 -4
  253. package/src/commands/langfuse.mjs +25 -10
  254. package/src/commands/learn.mjs +12 -3
  255. package/src/commands/release.mjs +21 -3
  256. package/src/commands/stats.mjs +64 -12
  257. package/src/commands/upgrade.mjs +131 -25
  258. package/src/guard.mjs +7 -37
  259. package/src/langfuse.mjs +31 -11
  260. package/src/paths.mjs +50 -1
  261. package/src/prices.mjs +7 -7
  262. package/src/transcripts.mjs +27 -1
  263. package/src/vocabulary.mjs +12 -1
  264. package/src/wiring.mjs +106 -41
@@ -632,7 +632,7 @@ async function attachAgentDetail(projectDir, dispatches) {
632
632
  if (
633
633
  run.agent_read_bytes === size &&
634
634
  !('resumes' in run) &&
635
- [...records, ...later].every((r) => settled(r) && 'round' in r && 'lessons_read' in r && 'tokens' in r && 'files_touched' in r && 'effort' in r && 'turns' in r)
635
+ [...records, ...later].every((r) => settled(r) && 'round' in r && 'lessons_read' in r && 'tokens' in r && 'files_touched' in r && 'effort' in r && 'turns' in r && 'prompt_chars' in r)
636
636
  ) {
637
637
  continue;
638
638
  }
@@ -662,6 +662,8 @@ async function attachAgentDetail(projectDir, dispatches) {
662
662
  written: new Set(),
663
663
  /** The tool calls the round made, by id: over its turns, how many calls each turn sent at once. */
664
664
  calls: new Set(),
665
+ /** How long the prompt that opened the round was, in characters; never the prompt. */
666
+ promptChars: null,
665
667
  });
666
668
  const rounds = [fresh()];
667
669
  const roundOf = roundsOf();
@@ -681,6 +683,14 @@ async function attachAgentDetail(projectDir, dispatches) {
681
683
  }
682
684
  }
683
685
  const round = rounds[n - 1];
686
+ if (round.promptChars === null && line.includes('"type":"user"')) {
687
+ try {
688
+ const said = promptOf(JSON.parse(line), n);
689
+ if (said !== null) round.promptChars = said.length;
690
+ } catch {
691
+ // A torn line is not the prompt.
692
+ }
693
+ }
684
694
  const hasSkill = line.includes('"Skill"');
685
695
  const hasHandback = line.includes('SubagentHandback');
686
696
  for (const m of line.matchAll(/"type":"tool_use","id":"([^"]+)"/g)) round.calls.add(m[1]);
@@ -770,6 +780,7 @@ async function attachAgentDetail(projectDir, dispatches) {
770
780
  record.tool_calls = round.calls.size;
771
781
  record.context_start = shape.start;
772
782
  record.context_peak = shape.peak;
783
+ record.prompt_chars = round.promptChars;
773
784
  record.usage_model = spent.model;
774
785
  record.effort = spent.effort;
775
786
  record.files_touched = round.written.size;
@@ -802,6 +813,21 @@ async function attachAgentDetail(projectDir, dispatches) {
802
813
  }
803
814
  }
804
815
 
816
+ /**
817
+ * What a round was told, from one transcript row, or null when the row is not it. The dispatch's prompt
818
+ * opens the first round; a later round opens with the message that resumed the run, which Claude Code
819
+ * marks as meta, while a meta row in the first round is a reminder. The snapshot measures it and the
820
+ * Langfuse export sends it, so both read it here.
821
+ *
822
+ * @param {object} row - A parsed transcript row.
823
+ * @param {number} round - The round the row is in, from 1.
824
+ * @returns {string|null}
825
+ */
826
+ export function promptOf(row, round) {
827
+ const content = row?.message?.content;
828
+ return row?.type === 'user' && (round > 1 || !row.isMeta) && typeof content === 'string' ? content : null;
829
+ }
830
+
805
831
  /**
806
832
  * How a round spent its context: how many turns it took, and how large the context each turn re-read was
807
833
  * when the round began and at its largest. Every turn is billed the whole context again, so a round that
@@ -16,6 +16,17 @@
16
16
  import { readFileSync } from 'node:fs';
17
17
  import { join } from 'node:path';
18
18
 
19
+ /**
20
+ * A vocabulary name: capitals, digits and `_`, from a letter — `{{S3_BUCKET}}` as much as `{{OWNER}}`. Every
21
+ * reader of a placeholder reads it with this one grammar. Compose took any name the profile declared while
22
+ * `check`, `init` and `upgrade` looked only for capitals and `_`, so `{{S3_BUCKET}}` was reported as declared
23
+ * and referenced by nothing, and deleting it on that advice composed the placeholder raw, with nobody saying so.
24
+ */
25
+ export const NAME = /^[A-Z][A-Z0-9_]*$/;
26
+
27
+ /** A placeholder in a text, its name captured. */
28
+ export const PLACEHOLDER = /\{\{([A-Z][A-Z0-9_]*)\}\}/g;
29
+
19
30
  /**
20
31
  * The defaults a layer root supplies, name → value. Anything that is not a string under an
21
32
  * upper-case name is ignored rather than trusted: a default reaches every project that pins it.
@@ -26,7 +37,7 @@ import { join } from 'node:path';
26
37
  export function defaultVocabulary(layerRoot) {
27
38
  try {
28
39
  const parsed = JSON.parse(readFileSync(join(layerRoot, 'core', 'vocabulary.json'), 'utf8'));
29
- return Object.fromEntries(Object.entries(parsed).filter(([name, value]) => /^[A-Z_]+$/.test(name) && typeof value === 'string'));
40
+ return Object.fromEntries(Object.entries(parsed).filter(([name, value]) => NAME.test(name) && typeof value === 'string'));
30
41
  } catch {
31
42
  return {};
32
43
  }
package/src/wiring.mjs CHANGED
@@ -205,21 +205,77 @@ function groupsFor(hooks, event) {
205
205
  return Array.isArray(hooks[event]) ? hooks[event] : null;
206
206
  }
207
207
 
208
+ /** Whether one hook command runs a hook's script, recognised by what it runs, not by its exact text. */
209
+ function runsCommand(x, h) {
210
+ const cmd = String(x?.command ?? '');
211
+ return cmd.includes(h.script) && (!h.mode || cmd.includes(h.mode));
212
+ }
213
+
214
+ /** Whether a settings group runs a hook's script. */
215
+ function runs(g, h) {
216
+ return Boolean(g) && typeof g === 'object' && Array.isArray(g.hooks) && g.hooks.some((x) => runsCommand(x, h));
217
+ }
218
+
219
+ /** A matcher that reaches every tool: none, empty, or `*`. */
220
+ const everyTool = (m) => m === undefined || m === null || m === '' || m === '*';
221
+
222
+ /** A matcher this can read and rewrite: every tool, or a list of exact names. A regular expression is its owner's. */
223
+ const plainMatcher = (m) => everyTool(m) || (typeof m === 'string' && /^[A-Za-z0-9_|]+$/.test(m));
224
+
225
+ /** Whether two matchers reach a tool in common — unknown for a regular expression, taken as no. */
226
+ function overlaps(a, b) {
227
+ if (everyTool(a) || everyTool(b)) return true;
228
+ if (!plainMatcher(a) || !plainMatcher(b)) return false;
229
+ const names = new Set(a.split('|'));
230
+ return b.split('|').some((n) => names.has(n));
231
+ }
232
+
233
+ /** Whether two of these groups run on a tool they share, so the hook runs once per group there. */
234
+ const doubled = (groups) => groups.some((g, i) => groups.slice(i + 1).some((other) => overlaps(g.matcher, other.matcher)));
235
+
208
236
  /**
209
237
  * Whether settings already run a hook. Recognised by what the command runs, not by its exact text —
210
238
  * a project may have written its own — and by a matcher that reaches every tool the hook needs.
211
239
  */
212
240
  function covered(hooks, h) {
213
- const groups = groupsFor(hooks, h.event) ?? [];
214
241
  const need = h.matcher ? h.matcher.split('|') : [];
215
- return groups.some((g) => {
216
- if (!g || typeof g !== 'object') return false;
217
- if (!need.every((tool) => matcherReaches(g.matcher, tool))) return false;
218
- return Array.isArray(g.hooks) && g.hooks.some((x) => {
219
- const cmd = String(x?.command ?? '');
220
- return cmd.includes(h.script) && (!h.mode || cmd.includes(h.mode));
221
- });
222
- });
242
+ return (groupsFor(hooks, h.event) ?? []).some((g) => runs(g, h) && need.every((tool) => matcherReaches(g.matcher, tool)));
243
+ }
244
+
245
+ /**
246
+ * The hooks a project runs, from both settings files merged by event — Claude Code runs both, so a hook
247
+ * kept in the local file counts. Every reader asks this one: `wire` used to read the shared file alone,
248
+ * and appended a second copy of each hook a project kept in its local file, which then ran twice.
249
+ *
250
+ * @param {string} target - The project directory.
251
+ * @param {(event: string) => boolean} [wanted] - Events whose malformed lists are worth a problem.
252
+ * @returns {Promise<{merged: Record<string, object[]>, problems: string[]}>} Each merged group says `from` which file.
253
+ */
254
+ async function projectHooks(target, wanted = () => false) {
255
+ const merged = {};
256
+ const problems = [];
257
+ for (const rel of SETTINGS_FILES) {
258
+ const path = join(target, rel);
259
+ if (!existsSync(path)) continue;
260
+ let settings;
261
+ try {
262
+ settings = JSON.parse(await readFile(path, 'utf8'));
263
+ } catch (error) {
264
+ problems.push(`${rel} is not valid JSON — ${error.message}`);
265
+ continue;
266
+ }
267
+ const shape = settings?.hooks;
268
+ if (shape === undefined || shape === null) continue;
269
+ if (typeof shape !== 'object' || Array.isArray(shape)) {
270
+ problems.push(`${rel} has a "hooks" that is not an object of event lists — no hook in it can run`);
271
+ continue;
272
+ }
273
+ for (const [event, groups] of Object.entries(shape)) {
274
+ if (Array.isArray(groups)) (merged[event] ??= []).push(...groups.map((g) => (g && typeof g === 'object' ? { ...g, from: rel } : g)));
275
+ else if (wanted(event)) problems.push(`${rel}'s hooks.${event} is not a list — no hook in it can run`);
276
+ }
277
+ }
278
+ return { merged, problems };
223
279
  }
224
280
 
225
281
  /** The command settings run for a hook, when they run one — the selftest runs it the same way. */
@@ -299,29 +355,16 @@ export async function missingWiring(target, shipped) {
299
355
  }
300
356
  const wanted = HOOKS.filter((h) => shipped.has(h.script));
301
357
  if (wanted.length === 0) return out;
302
- const merged = {};
303
- for (const rel of SETTINGS_FILES) {
304
- const path = join(target, rel);
305
- if (!existsSync(path)) continue;
306
- let settings;
307
- try {
308
- settings = JSON.parse(await readFile(path, 'utf8'));
309
- } catch (error) {
310
- out.push(`${rel} is not valid JSON — ${error.message}`);
311
- continue;
312
- }
313
- const shape = settings?.hooks;
314
- if (shape === undefined || shape === null) continue;
315
- if (typeof shape !== 'object' || Array.isArray(shape)) {
316
- out.push(`${rel} has a "hooks" that is not an object of event lists — no hook in it can run`);
317
- continue;
318
- }
319
- for (const [event, groups] of Object.entries(shape)) {
320
- if (Array.isArray(groups)) (merged[event] ??= []).push(...groups);
321
- else if (wanted.some((h) => h.event === event)) out.push(`${rel}'s hooks.${event} is not a list — no hook in it can run`);
322
- }
323
- }
358
+ const { merged, problems } = await projectHooks(target, (event) => wanted.some((h) => h.event === event));
359
+ out.push(...problems);
324
360
  for (const h of wanted) {
361
+ const running = (merged[h.event] ?? []).filter((g) => runs(g, h));
362
+ if (doubled(running)) {
363
+ out.push(
364
+ `${running.length} ${h.event} groups run \`${h.script}${h.mode ? ` ${h.mode}` : ''}\` for tools they share, so it runs once per group there — ` +
365
+ '`nina wire --apply` makes them one',
366
+ );
367
+ }
325
368
  if (covered(merged, h)) continue;
326
369
  out.push(
327
370
  `.claude/settings.json has no ${h.event} hook${h.matcher ? ` for ${h.matcher}` : ''} running \`${h.script}${h.mode ? ` ${h.mode}` : ''}\` — without it ${h.why}`,
@@ -359,13 +402,9 @@ export async function staleHooks(target, shipped) {
359
402
  * @returns {Promise<string|null>} Null when nothing is missing.
360
403
  */
361
404
  export async function missingFragment(target, shipped) {
362
- let settings = null;
363
- try {
364
- settings = JSON.parse(await readFile(join(target, '.claude', 'settings.json'), 'utf8'));
365
- } catch {
366
- // Absent or unreadable: everything is missing, and `missingWiring` says which.
367
- }
368
- const lacking = HOOKS.filter((h) => shipped.has(h.script) && !covered(settings?.hooks, h));
405
+ // Absent or unreadable files hold nothing: everything they would have held is missing, and `missingWiring` says why.
406
+ const { merged } = await projectHooks(target);
407
+ const lacking = HOOKS.filter((h) => shipped.has(h.script) && !covered(merged, h));
369
408
  return lacking.length === 0 ? null : JSON.stringify({ hooks: settingsHooks(lacking) }, null, 2);
370
409
  }
371
410
 
@@ -405,10 +444,36 @@ export async function applyWiring(target, shipped, { settings: editSettings = tr
405
444
  if (readable) {
406
445
  const hooks = settings.hooks ?? {};
407
446
  const added = [];
447
+ const { merged } = await projectHooks(target);
408
448
  for (const h of HOOKS.filter((h) => shipped.has(h.script))) {
409
- if (covered(hooks, h)) continue;
410
- (hooks[h.event] ??= []).push(group(h));
411
- added.push(`${h.event}${h.matcher ? ` (${h.matcher})` : ''} → ${h.script}${h.mode ? ` ${h.mode}` : ''}`);
449
+ const running = (merged[h.event] ?? []).filter((g) => runs(g, h));
450
+ if (covered(merged, h) && !doubled(running)) continue;
451
+ const named = `${h.event}${h.matcher ? ` (${h.matcher})` : ''} → ${h.script}${h.mode ? ` ${h.mode}` : ''}`;
452
+ if (running.length === 0) {
453
+ (hooks[h.event] ??= []).push(group(h));
454
+ added.push(named);
455
+ continue;
456
+ }
457
+ // The groups that already run the script become one that reaches every tool it needs. A second group
458
+ // beside a narrower one — or two left as they were — runs it on the tools they share once per group, and
459
+ // the gate counts each of those events twice. A group in the local file, or one matched by a regular
460
+ // expression, is its owner's to change: said, and not doubled.
461
+ const foreign = running.find((g) => g.from !== '.claude/settings.json' || !plainMatcher(g.matcher));
462
+ if (foreign) {
463
+ problems.push(
464
+ `${foreign.from} runs ${h.script} for "${foreign.matcher ?? ''}" — make every group that runs it there one, reaching ${h.matcher ?? 'every tool'}; ` +
465
+ 'another group beside it would run it twice',
466
+ );
467
+ continue;
468
+ }
469
+ const mine = (hooks[h.event] ?? []).filter((g) => runs(g, h));
470
+ const keep = mine.find((g) => everyTool(g.matcher)) ?? mine[0];
471
+ if (!everyTool(keep.matcher)) {
472
+ keep.matcher = [...new Set([...mine.flatMap((g) => g.matcher.split('|')), ...(h.matcher ? h.matcher.split('|') : [])])].join('|');
473
+ }
474
+ for (const g of mine) if (g !== keep) g.hooks = g.hooks.filter((x) => !runsCommand(x, h));
475
+ hooks[h.event] = hooks[h.event].filter((g) => !Array.isArray(g?.hooks) || g.hooks.length > 0);
476
+ added.push(`${named} (${mine.length > 1 ? `${mine.length} groups made one, ` : ''}reaching ${everyTool(keep.matcher) ? 'every tool' : keep.matcher})`);
412
477
  }
413
478
  // An old command, exactly as it was written, learns to say when its script cannot start, and how to
414
479
  // install NINA as it is installed now.