@xhulz/nina 0.47.0 → 0.49.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 (274) hide show
  1. package/README.md +4 -0
  2. package/bin/nina.mjs +18 -3
  3. package/package.json +1 -1
  4. package/releases/0.48.0/core/GAPS.md +125 -0
  5. package/releases/0.48.0/core/answered.json +44 -0
  6. package/releases/0.48.0/core/defaults/tree/.claude/agents/architect.md +1 -0
  7. package/releases/0.48.0/core/defaults/tree/.claude/agents/dba.md +3 -0
  8. package/releases/0.48.0/core/defaults/tree/.claude/agents/devops.md +1 -0
  9. package/releases/0.48.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
  10. package/releases/0.48.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
  11. package/releases/0.48.0/core/defaults/tree/.claude/agents/qa.md +7 -0
  12. package/releases/0.48.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
  13. package/releases/0.48.0/core/defaults/tree/.claude/agents/secops.md +3 -0
  14. package/releases/0.48.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
  15. package/releases/0.48.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
  16. package/releases/0.48.0/core/tree/.claude/agents/architect.md +127 -0
  17. package/releases/0.48.0/core/tree/.claude/agents/dba.md +117 -0
  18. package/releases/0.48.0/core/tree/.claude/agents/devops.md +118 -0
  19. package/releases/0.48.0/core/tree/.claude/agents/implementer.md +160 -0
  20. package/releases/0.48.0/core/tree/.claude/agents/integration-tester.md +168 -0
  21. package/releases/0.48.0/core/tree/.claude/agents/planner.md +132 -0
  22. package/releases/0.48.0/core/tree/.claude/agents/qa.md +150 -0
  23. package/releases/0.48.0/core/tree/.claude/agents/reviewer.md +183 -0
  24. package/releases/0.48.0/core/tree/.claude/agents/secops.md +120 -0
  25. package/releases/0.48.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
  26. package/releases/0.48.0/core/tree/.claude/agents/solidity-dev.md +123 -0
  27. package/releases/0.48.0/core/tree/.claude/agents-overview.md +64 -0
  28. package/releases/0.48.0/core/tree/.claude/frontend.md +10 -0
  29. package/releases/0.48.0/core/tree/.claude/graph.md +75 -0
  30. package/releases/0.48.0/core/tree/.claude/patterns.md +251 -0
  31. package/releases/0.48.0/core/tree/.claude/pills/README.md +147 -0
  32. package/releases/0.48.0/core/tree/.claude/pipeline.md +101 -0
  33. package/releases/0.48.0/core/tree/.claude/retrieval.md +103 -0
  34. package/releases/0.48.0/core/tree/.claude/router.md +202 -0
  35. package/releases/0.48.0/core/tree/.claude/templates/integration.md +45 -0
  36. package/releases/0.48.0/core/tree/CLAUDE.md +164 -0
  37. package/releases/0.48.0/core/tree/scripts/cost-watch.mjs +25 -0
  38. package/releases/0.48.0/core/tree/scripts/edit-guard.mjs +24 -0
  39. package/releases/0.48.0/core/tree/scripts/harness-check.mjs +71 -0
  40. package/releases/0.48.0/core/tree/scripts/loop-gate.mjs +19 -0
  41. package/releases/0.48.0/core/vocabulary.json +13 -0
  42. package/releases/0.48.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
  43. package/releases/0.48.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
  44. package/releases/0.48.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
  45. package/releases/0.48.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
  46. package/releases/0.48.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
  47. package/releases/0.48.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
  48. package/releases/0.48.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
  49. package/releases/0.48.0/surfaces/blockchain/tree/.claude/router.md +6 -0
  50. package/releases/0.48.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
  51. package/releases/0.48.0/surfaces/d1/tree/.claude/agents/dba.md +13 -0
  52. package/releases/0.48.0/surfaces/d1/tree/.claude/agents/devops.md +2 -0
  53. package/releases/0.48.0/surfaces/d1/tree/.claude/agents/implementer.md +2 -0
  54. package/releases/0.48.0/surfaces/d1/tree/.claude/patterns.md +8 -0
  55. package/releases/0.48.0/surfaces/d1/tree/CLAUDE.md +2 -0
  56. package/releases/0.48.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
  57. package/releases/0.48.0/surfaces/db/tree/.claude/agents/dba.md +54 -0
  58. package/releases/0.48.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
  59. package/releases/0.48.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
  60. package/releases/0.48.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
  61. package/releases/0.48.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
  62. package/releases/0.48.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
  63. package/releases/0.48.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
  64. package/releases/0.48.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
  65. package/releases/0.48.0/surfaces/db/tree/.claude/graph.md +8 -0
  66. package/releases/0.48.0/surfaces/db/tree/.claude/patterns.md +22 -0
  67. package/releases/0.48.0/surfaces/db/tree/.claude/pipeline.md +13 -0
  68. package/releases/0.48.0/surfaces/db/tree/.claude/retrieval.md +5 -0
  69. package/releases/0.48.0/surfaces/db/tree/.claude/router.md +4 -0
  70. package/releases/0.48.0/surfaces/db/tree/CLAUDE.md +17 -0
  71. package/releases/0.48.0/surfaces/drizzle/tree/.claude/agents/dba.md +9 -0
  72. package/releases/0.48.0/surfaces/drizzle/tree/.claude/agents/devops.md +5 -0
  73. package/releases/0.48.0/surfaces/drizzle/tree/.claude/agents/implementer.md +2 -0
  74. package/releases/0.48.0/surfaces/drizzle/tree/.claude/patterns.md +7 -0
  75. package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
  76. package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
  77. package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/devops.md +29 -0
  78. package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
  79. package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
  80. package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
  81. package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
  82. package/releases/0.48.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
  83. package/releases/0.48.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
  84. package/releases/0.48.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
  85. package/releases/0.48.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
  86. package/releases/0.48.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
  87. package/releases/0.48.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
  88. package/releases/0.48.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
  89. package/releases/0.48.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
  90. package/releases/0.48.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
  91. package/releases/0.48.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
  92. package/releases/0.48.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
  93. package/releases/0.48.0/surfaces/frontend/tree/.claude/router.md +14 -0
  94. package/releases/0.48.0/surfaces/frontend/tree/CLAUDE.md +5 -0
  95. package/releases/0.48.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
  96. package/releases/0.48.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
  97. package/releases/0.48.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
  98. package/releases/0.48.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
  99. package/releases/0.48.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
  100. package/releases/0.48.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
  101. package/releases/0.48.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
  102. package/releases/0.48.0/surfaces/integrations/tree/.claude/graph.md +10 -0
  103. package/releases/0.48.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
  104. package/releases/0.48.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
  105. package/releases/0.48.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
  106. package/releases/0.48.0/surfaces/integrations/tree/.claude/router.md +6 -0
  107. package/releases/0.48.0/surfaces/integrations/tree/CLAUDE.md +17 -0
  108. package/releases/0.48.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
  109. package/releases/0.48.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
  110. package/releases/0.48.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
  111. package/releases/0.48.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
  112. package/releases/0.48.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
  113. package/releases/0.48.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
  114. package/releases/0.48.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
  115. package/releases/0.48.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
  116. package/releases/0.48.0/surfaces/money/tree/.claude/graph.md +2 -0
  117. package/releases/0.48.0/surfaces/money/tree/.claude/patterns.md +49 -0
  118. package/releases/0.48.0/surfaces/money/tree/.claude/pipeline.md +2 -0
  119. package/releases/0.48.0/surfaces/money/tree/.claude/retrieval.md +2 -0
  120. package/releases/0.48.0/surfaces/money/tree/CLAUDE.md +11 -0
  121. package/releases/0.48.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
  122. package/releases/0.48.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
  123. package/releases/0.48.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
  124. package/releases/0.48.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
  125. package/releases/0.48.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
  126. package/releases/0.48.0/surfaces/pii/tree/CLAUDE.md +5 -0
  127. package/releases/0.48.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
  128. package/releases/0.48.0/surfaces/prisma/tree/.claude/agents/dba.md +42 -0
  129. package/releases/0.48.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
  130. package/releases/0.48.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
  131. package/releases/0.48.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
  132. package/releases/0.48.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
  133. package/releases/0.48.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
  134. package/releases/0.48.0/surfaces/prisma/tree/CLAUDE.md +5 -0
  135. package/releases/0.49.0/core/GAPS.md +125 -0
  136. package/releases/0.49.0/core/answered.json +53 -0
  137. package/releases/0.49.0/core/defaults/tree/.claude/agents/architect.md +1 -0
  138. package/releases/0.49.0/core/defaults/tree/.claude/agents/dba.md +3 -0
  139. package/releases/0.49.0/core/defaults/tree/.claude/agents/devops.md +1 -0
  140. package/releases/0.49.0/core/defaults/tree/.claude/agents/implementer.md +1 -0
  141. package/releases/0.49.0/core/defaults/tree/.claude/agents/integration-tester.md +3 -0
  142. package/releases/0.49.0/core/defaults/tree/.claude/agents/qa.md +7 -0
  143. package/releases/0.49.0/core/defaults/tree/.claude/agents/reviewer.md +3 -0
  144. package/releases/0.49.0/core/defaults/tree/.claude/agents/secops.md +3 -0
  145. package/releases/0.49.0/core/defaults/tree/.claude/agents/solidity-auditor.md +3 -0
  146. package/releases/0.49.0/core/defaults/tree/.claude/agents/solidity-dev.md +3 -0
  147. package/releases/0.49.0/core/tree/.claude/agents/architect.md +129 -0
  148. package/releases/0.49.0/core/tree/.claude/agents/dba.md +117 -0
  149. package/releases/0.49.0/core/tree/.claude/agents/devops.md +118 -0
  150. package/releases/0.49.0/core/tree/.claude/agents/implementer.md +162 -0
  151. package/releases/0.49.0/core/tree/.claude/agents/integration-tester.md +168 -0
  152. package/releases/0.49.0/core/tree/.claude/agents/planner.md +132 -0
  153. package/releases/0.49.0/core/tree/.claude/agents/qa.md +150 -0
  154. package/releases/0.49.0/core/tree/.claude/agents/reviewer.md +184 -0
  155. package/releases/0.49.0/core/tree/.claude/agents/secops.md +120 -0
  156. package/releases/0.49.0/core/tree/.claude/agents/solidity-auditor.md +126 -0
  157. package/releases/0.49.0/core/tree/.claude/agents/solidity-dev.md +123 -0
  158. package/releases/0.49.0/core/tree/.claude/agents-overview.md +64 -0
  159. package/releases/0.49.0/core/tree/.claude/frontend.md +10 -0
  160. package/releases/0.49.0/core/tree/.claude/graph.md +75 -0
  161. package/releases/0.49.0/core/tree/.claude/patterns.md +251 -0
  162. package/releases/0.49.0/core/tree/.claude/pills/README.md +147 -0
  163. package/releases/0.49.0/core/tree/.claude/pipeline.md +101 -0
  164. package/releases/0.49.0/core/tree/.claude/retrieval.md +103 -0
  165. package/releases/0.49.0/core/tree/.claude/router.md +202 -0
  166. package/releases/0.49.0/core/tree/.claude/templates/integration.md +45 -0
  167. package/releases/0.49.0/core/tree/CLAUDE.md +164 -0
  168. package/releases/0.49.0/core/tree/scripts/cost-watch.mjs +25 -0
  169. package/releases/0.49.0/core/tree/scripts/edit-guard.mjs +24 -0
  170. package/releases/0.49.0/core/tree/scripts/harness-check.mjs +71 -0
  171. package/releases/0.49.0/core/tree/scripts/loop-gate.mjs +19 -0
  172. package/releases/0.49.0/core/vocabulary.json +13 -0
  173. package/releases/0.49.0/surfaces/blockchain/tree/.claude/agents/architect.md +5 -0
  174. package/releases/0.49.0/surfaces/blockchain/tree/.claude/agents/devops.md +5 -0
  175. package/releases/0.49.0/surfaces/blockchain/tree/.claude/agents/reviewer.md +5 -0
  176. package/releases/0.49.0/surfaces/blockchain/tree/.claude/agents/solidity-auditor.md +18 -0
  177. package/releases/0.49.0/surfaces/blockchain/tree/.claude/agents/solidity-dev.md +16 -0
  178. package/releases/0.49.0/surfaces/blockchain/tree/.claude/agents-overview.md +6 -0
  179. package/releases/0.49.0/surfaces/blockchain/tree/.claude/graph.md +11 -0
  180. package/releases/0.49.0/surfaces/blockchain/tree/.claude/router.md +6 -0
  181. package/releases/0.49.0/surfaces/blockchain/tree/CLAUDE.md +12 -0
  182. package/releases/0.49.0/surfaces/d1/tree/.claude/agents/dba.md +13 -0
  183. package/releases/0.49.0/surfaces/d1/tree/.claude/agents/devops.md +2 -0
  184. package/releases/0.49.0/surfaces/d1/tree/.claude/agents/implementer.md +2 -0
  185. package/releases/0.49.0/surfaces/d1/tree/.claude/patterns.md +8 -0
  186. package/releases/0.49.0/surfaces/d1/tree/CLAUDE.md +2 -0
  187. package/releases/0.49.0/surfaces/db/tree/.claude/agents/architect.md +5 -0
  188. package/releases/0.49.0/surfaces/db/tree/.claude/agents/dba.md +54 -0
  189. package/releases/0.49.0/surfaces/db/tree/.claude/agents/devops.md +5 -0
  190. package/releases/0.49.0/surfaces/db/tree/.claude/agents/implementer.md +2 -0
  191. package/releases/0.49.0/surfaces/db/tree/.claude/agents/integration-tester.md +10 -0
  192. package/releases/0.49.0/surfaces/db/tree/.claude/agents/planner.md +2 -0
  193. package/releases/0.49.0/surfaces/db/tree/.claude/agents/reviewer.md +22 -0
  194. package/releases/0.49.0/surfaces/db/tree/.claude/agents/secops.md +2 -0
  195. package/releases/0.49.0/surfaces/db/tree/.claude/agents-overview.md +5 -0
  196. package/releases/0.49.0/surfaces/db/tree/.claude/graph.md +8 -0
  197. package/releases/0.49.0/surfaces/db/tree/.claude/patterns.md +22 -0
  198. package/releases/0.49.0/surfaces/db/tree/.claude/pipeline.md +13 -0
  199. package/releases/0.49.0/surfaces/db/tree/.claude/retrieval.md +5 -0
  200. package/releases/0.49.0/surfaces/db/tree/.claude/router.md +4 -0
  201. package/releases/0.49.0/surfaces/db/tree/CLAUDE.md +17 -0
  202. package/releases/0.49.0/surfaces/drizzle/tree/.claude/agents/dba.md +9 -0
  203. package/releases/0.49.0/surfaces/drizzle/tree/.claude/agents/devops.md +5 -0
  204. package/releases/0.49.0/surfaces/drizzle/tree/.claude/agents/implementer.md +2 -0
  205. package/releases/0.49.0/surfaces/drizzle/tree/.claude/patterns.md +7 -0
  206. package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/architect.md +14 -0
  207. package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/dba.md +2 -0
  208. package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/devops.md +29 -0
  209. package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/implementer.md +14 -0
  210. package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/integration-tester.md +11 -0
  211. package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/qa.md +3 -0
  212. package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/reviewer.md +8 -0
  213. package/releases/0.49.0/surfaces/edge-cf/tree/.claude/agents/secops.md +8 -0
  214. package/releases/0.49.0/surfaces/edge-cf/tree/.claude/patterns.md +17 -0
  215. package/releases/0.49.0/surfaces/edge-cf/tree/.claude/retrieval.md +8 -0
  216. package/releases/0.49.0/surfaces/edge-cf/tree/CLAUDE.md +27 -0
  217. package/releases/0.49.0/surfaces/frontend/tree/.claude/agents/architect.md +20 -0
  218. package/releases/0.49.0/surfaces/frontend/tree/.claude/agents/devops.md +28 -0
  219. package/releases/0.49.0/surfaces/frontend/tree/.claude/agents/implementer.md +25 -0
  220. package/releases/0.49.0/surfaces/frontend/tree/.claude/agents/qa.md +17 -0
  221. package/releases/0.49.0/surfaces/frontend/tree/.claude/agents/reviewer.md +56 -0
  222. package/releases/0.49.0/surfaces/frontend/tree/.claude/patterns.md +8 -0
  223. package/releases/0.49.0/surfaces/frontend/tree/.claude/retrieval.md +2 -0
  224. package/releases/0.49.0/surfaces/frontend/tree/.claude/router.md +14 -0
  225. package/releases/0.49.0/surfaces/frontend/tree/CLAUDE.md +5 -0
  226. package/releases/0.49.0/surfaces/integrations/tree/.claude/agents/architect.md +16 -0
  227. package/releases/0.49.0/surfaces/integrations/tree/.claude/agents/implementer.md +12 -0
  228. package/releases/0.49.0/surfaces/integrations/tree/.claude/agents/integration-tester.md +20 -0
  229. package/releases/0.49.0/surfaces/integrations/tree/.claude/agents/planner.md +2 -0
  230. package/releases/0.49.0/surfaces/integrations/tree/.claude/agents/reviewer.md +21 -0
  231. package/releases/0.49.0/surfaces/integrations/tree/.claude/agents/secops.md +2 -0
  232. package/releases/0.49.0/surfaces/integrations/tree/.claude/agents-overview.md +8 -0
  233. package/releases/0.49.0/surfaces/integrations/tree/.claude/graph.md +10 -0
  234. package/releases/0.49.0/surfaces/integrations/tree/.claude/patterns.md +38 -0
  235. package/releases/0.49.0/surfaces/integrations/tree/.claude/pipeline.md +16 -0
  236. package/releases/0.49.0/surfaces/integrations/tree/.claude/retrieval.md +16 -0
  237. package/releases/0.49.0/surfaces/integrations/tree/.claude/router.md +6 -0
  238. package/releases/0.49.0/surfaces/integrations/tree/CLAUDE.md +17 -0
  239. package/releases/0.49.0/surfaces/money/tree/.claude/agents/architect.md +12 -0
  240. package/releases/0.49.0/surfaces/money/tree/.claude/agents/dba.md +14 -0
  241. package/releases/0.49.0/surfaces/money/tree/.claude/agents/implementer.md +11 -0
  242. package/releases/0.49.0/surfaces/money/tree/.claude/agents/integration-tester.md +3 -0
  243. package/releases/0.49.0/surfaces/money/tree/.claude/agents/reviewer.md +23 -0
  244. package/releases/0.49.0/surfaces/money/tree/.claude/agents/secops.md +5 -0
  245. package/releases/0.49.0/surfaces/money/tree/.claude/agents/solidity-auditor.md +4 -0
  246. package/releases/0.49.0/surfaces/money/tree/.claude/agents/solidity-dev.md +4 -0
  247. package/releases/0.49.0/surfaces/money/tree/.claude/graph.md +2 -0
  248. package/releases/0.49.0/surfaces/money/tree/.claude/patterns.md +49 -0
  249. package/releases/0.49.0/surfaces/money/tree/.claude/pipeline.md +2 -0
  250. package/releases/0.49.0/surfaces/money/tree/.claude/retrieval.md +2 -0
  251. package/releases/0.49.0/surfaces/money/tree/CLAUDE.md +11 -0
  252. package/releases/0.49.0/surfaces/pii/tree/.claude/agents/dba.md +2 -0
  253. package/releases/0.49.0/surfaces/pii/tree/.claude/agents/devops.md +8 -0
  254. package/releases/0.49.0/surfaces/pii/tree/.claude/agents/reviewer.md +8 -0
  255. package/releases/0.49.0/surfaces/pii/tree/.claude/agents/secops.md +2 -0
  256. package/releases/0.49.0/surfaces/pii/tree/.claude/agents/solidity-auditor.md +5 -0
  257. package/releases/0.49.0/surfaces/pii/tree/CLAUDE.md +5 -0
  258. package/releases/0.49.0/surfaces/prisma/tree/.claude/agents/architect.md +2 -0
  259. package/releases/0.49.0/surfaces/prisma/tree/.claude/agents/dba.md +42 -0
  260. package/releases/0.49.0/surfaces/prisma/tree/.claude/agents/devops.md +11 -0
  261. package/releases/0.49.0/surfaces/prisma/tree/.claude/agents/implementer.md +2 -0
  262. package/releases/0.49.0/surfaces/prisma/tree/.claude/agents/integration-tester.md +2 -0
  263. package/releases/0.49.0/surfaces/prisma/tree/.claude/agents/reviewer.md +2 -0
  264. package/releases/0.49.0/surfaces/prisma/tree/.claude/patterns.md +15 -0
  265. package/releases/0.49.0/surfaces/prisma/tree/CLAUDE.md +5 -0
  266. package/src/commands/check.mjs +10 -0
  267. package/src/commands/gate.mjs +47 -2
  268. package/src/commands/init.mjs +9 -2
  269. package/src/commands/pipeline.mjs +22 -1
  270. package/src/commands/upgrade.mjs +8 -0
  271. package/src/gate.mjs +106 -22
  272. package/src/office.html +1069 -0
  273. package/src/office.mjs +434 -0
  274. package/src/wiring.mjs +11 -1
@@ -0,0 +1,2 @@
1
+ <!-- nina:slot prisma.1 -->
2
+ | `prisma-cli` | a Prisma CLI command |
@@ -0,0 +1,2 @@
1
+ <!-- nina:slot prisma.1 -->
2
+ | `prisma-client-api` | a new or changed Prisma query |
@@ -0,0 +1,15 @@
1
+ <!-- nina:slot prisma.1 -->
2
+ - **Database table/column names** (Prisma `@map` / `@@map`): `snake_case` in SQL, `camelCase` in TS.
3
+
4
+ <!-- nina:slot prisma.2 -->
5
+ - Where Prisma reads are cached through Accelerate, `cacheStrategy` lives in `{{DB_PKG}}`: services do not add, strip, or override cache policies.
6
+
7
+ <!-- nina:slot prisma.3 -->
8
+
9
+ ### Prisma
10
+
11
+ - Schema lives in `{{DB_PKG}}/prisma/schema.prisma`. Nowhere else.
12
+ - **Never write raw SQL** unless justified in a code comment with perf data.
13
+ - N+1 is solved with `include` / `select`, chosen deliberately.
14
+ - Where reads are cached through Accelerate, every `findUnique` / `findMany` on a hot path defines its `cacheStrategy`, and no direct connection string is embedded in code.
15
+ - Tests run a real Prisma client against a real database; the client is never mocked.
@@ -0,0 +1,5 @@
1
+ <!-- nina:slot prisma.1 -->
2
+ | Run any `prisma` CLI command (the database gate, devops, and the integration gate where there is one) | `prisma-cli` | flags differ between versions; `migrate diff --shadow-database-url` against a live DB wiped dev/staging once |
3
+
4
+ <!-- nina:slot prisma.2 -->
5
+ | Write or audit a Prisma query (architect, implementer, the database gate, reviewer) | `prisma-client-api` | filters, operators, `$transaction` semantics. a caching or edge layer on top of the client is **NOT covered** — its cache strategy belongs to that layer's own integration doc |
@@ -297,6 +297,16 @@ export async function check(argv, ctx) {
297
297
  notes.push(`${label} names boundary "${entry.boundary}", which does not exist yet`);
298
298
  }
299
299
  }
300
+ // The other direction: a doc no integration declares is retired, waits for its boundary, or lost its
301
+ // declaration, and nothing told a reader which. Said as a note, and as a deferral once the profile says why.
302
+ const declaredSlugs = new Set(integrations.map((entry) => entry?.slug));
303
+ const docs = await readdir(join(target, '.claude', 'integrations')).catch(() => []);
304
+ for (const doc of docs.filter((name) => name.endsWith('.md') && !declaredSlugs.has(name.slice(0, -'.md'.length))).sort()) {
305
+ const key = `.claude/integrations/${doc}`;
306
+ const why = deferredWhy(profile, key);
307
+ if (why) deferredUsed.add(key);
308
+ notes.push(why ? `deferred: ${key} — ${why}` : `${key} documents no declared integration — declare it, or say under "deferred" why not: retired, or waiting for its boundary`);
309
+ }
300
310
 
301
311
  const slots = await projectSlots(resolved.dir, surfaces);
302
312
  const filled = await filledSlots(target);
@@ -2,6 +2,7 @@
2
2
  * `nina gate` — the loop gate's own check, and a way to run it by hand.
3
3
  *
4
4
  * nina gate --selftest is the gate wired, has it failed, would it still hold a loop past its cap?
5
+ * nina gate --status the loops a session holds open, and what would close each
5
6
  * nina gate --hook handle one hook event from stdin, the way the composed script does
6
7
  *
7
8
  * The gate lets every call through when anything goes wrong, which is right for a hook and makes a
@@ -20,7 +21,7 @@ import { tmpdir } from 'node:os';
20
21
  import { join, resolve } from 'node:path';
21
22
  import { readProfile, slugFor } from '../paths.mjs';
22
23
  import { GATE, hookCommand, missingWiring, shippedScripts } from '../wiring.mjs';
23
- import { LEDGER_TAIL, ledgerPath, loadProject, projectGateDir, readLedger, runGate } from '../gate.mjs';
24
+ import { LEDGER_TAIL, ledgerPath, loadProject, loopState, projectGateDir, readLedger, runGate, sessionLedgers } from '../gate.mjs';
24
25
  import { projectRecords } from '../store.mjs';
25
26
  import { layerRootFor } from './compose.mjs';
26
27
  import { handsToModel } from '../detectors.mjs';
@@ -271,13 +272,57 @@ function dryRun(target) {
271
272
  }
272
273
 
273
274
  /**
274
- * @param {string[]} argv - `[--selftest | --hook] [--project <dir>]`.
275
+ * What the gate holds of one session: its loops, what would close each, and the loop-backs waiting on a fix —
276
+ * the same lines a compaction hands the orchestrator, at any time. The session is the one whose ledger was
277
+ * written last, which is the running one while it dispatches, or the one `--session` names by the start of its id.
278
+ *
279
+ * @param {string} target - The project directory.
280
+ * @param {string|undefined} wanted - The start of a session id, or nothing for the latest.
281
+ * @returns {number} Process exit code.
282
+ */
283
+ function loopStatus(target, wanted) {
284
+ const project = loadProject(target);
285
+ if (!project) {
286
+ console.error(' .claude/graph.md is not composed, so the gate has no loops to hold — run `nina compose`\n');
287
+ return 2;
288
+ }
289
+ const { dir, ledgers, picked, why } = sessionLedgers(target, wanted);
290
+ if (ledgers.length === 0 && !wanted) {
291
+ console.log(` the gate has no ledger here yet: no session has dispatched a stage since it was wired (${dir})`);
292
+ console.log('gate: no open loop');
293
+ return 0;
294
+ }
295
+ if (!picked) {
296
+ console.error(` ${why} (${dir})\n`);
297
+ return 2;
298
+ }
299
+ const { session, written, path } = picked;
300
+ const state = loopState(project, readLedger(path));
301
+ const others = ledgers.length > 1 ? `; ${ledgers.length - 1} other session(s) kept here, --session <id> for one` : '';
302
+ console.log(` session ${session}, its ledger last written ${written.toISOString().slice(0, 16).replace('T', ' ')} UTC${others}`);
303
+ for (const line of state.lines) console.log(` ${line}`);
304
+ const counts = [
305
+ state.open ? `${state.open} open loop(s)` : 'no open loop',
306
+ state.closing ? `${state.closing} settled, dropped at the next dispatch` : '',
307
+ state.waiting ? `${state.waiting} loop-back(s) waiting on a fix` : '',
308
+ ];
309
+ console.log(`gate: ${counts.filter(Boolean).join(', ')}`);
310
+ return 0;
311
+ }
312
+
313
+ /**
314
+ * @param {string[]} argv - `[--selftest | --status [--session <id>] | --hook] [--project <dir>]`.
275
315
  * @param {{root: string}} ctx - CLI context; `root` is the NINA install directory.
276
316
  * @returns {Promise<number>} Process exit code.
277
317
  */
278
318
  export async function gate(argv, ctx) {
279
319
  const target = resolve(argv.includes('--project') ? argv[argv.indexOf('--project') + 1] : '.');
280
320
  if (argv.includes('--hook')) return runGate({ root: target });
321
+ if (argv.includes('--status')) return loopStatus(target, argv.includes('--session') ? argv[argv.indexOf('--session') + 1] : undefined);
322
+ if (argv.includes('--session')) {
323
+ console.error(' --session names the session --status reads, and means nothing without it\n');
324
+ return 2;
325
+ }
281
326
 
282
327
  const { profile, error } = readProfile(target);
283
328
  if (error) {
@@ -218,14 +218,21 @@ async function interview(available, matched, impacts) {
218
218
  const scope = lines.join(' ');
219
219
 
220
220
  const order = available;
221
- console.log(`\n ${strong('Surfaces')} — ${order.length} yes-or-no questions, one per surface.`);
221
+ console.log(`\n ${strong('Surfaces')} — up to ${order.length} yes-or-no questions, one per surface; a stack comes only after a yes to what it needs.`);
222
222
  console.log(' A surface is a part of the harness only some projects need: its rules, its checks, sometimes a');
223
223
  console.log(' whole role. Answer for what the project does today; one can be added later in .nina/profile.json.\n');
224
224
  const surfaces = [];
225
+ let asked = 0;
225
226
  for (const [n, s] of order.entries()) {
226
227
  // A stack is asked about only where its concern was answered yes: asked whether data it does not
227
228
  // own goes through Prisma, a project has nothing true to answer.
228
229
  if (missingNeeds(s, surfaces).length > 0) continue;
230
+ // Counted by what is asked, not by place in the list: numbered by place, a no to the database read
231
+ // 1/10 then 4/10, and the header promised ten questions to someone asked seven. The total is what is
232
+ // asked so far plus what can still be, so a no shrinks it; the last question is n/n while the order
233
+ // ends with a surface that needs nothing, as it does.
234
+ asked += 1;
235
+ const total = asked + order.slice(n + 1).filter((t) => missingNeeds(t, [...surfaces, ...order.slice(n)]).length === 0).length;
229
236
  const i = impacts[s];
230
237
  const reason = matched.find((d) => d.surface === s)?.why;
231
238
  // What a yes brings, in what a person would recognise: roles, rules, agents. The count of
@@ -235,7 +242,7 @@ async function interview(available, matched, impacts) {
235
242
  ...(i.rules ? [`${i.rules} hard rule${i.rules > 1 ? 's' : ''}`] : []),
236
243
  ...(i.agents.length ? [`changes to ${i.agents.length} agent${i.agents.length > 1 ? 's' : ''}`] : []),
237
244
  ];
238
- console.log(` ${faint(`${n + 1}/${order.length}`)} ${strong(s)}`);
245
+ console.log(` ${faint(`${asked}/${total}`)} ${strong(s)}`);
239
246
  console.log(` ${questionFor(s)}`);
240
247
  console.log(faint(` if yes: ${brings.length ? brings.join(' · ') : `guidance in ${i.fragments} places`}`));
241
248
  if (reason) console.log(found(` already here: ${reason}`));
@@ -168,7 +168,8 @@ export function historyOf(records, since) {
168
168
  /**
169
169
  * Runs the command.
170
170
  *
171
- * @param {string[]} argv - `[--project <dir>] [--for <number or words>] [--since <YYYY-MM-DD>]`.
171
+ * @param {string[]} argv - `[--project <dir>] [--for <number or words>] [--since <YYYY-MM-DD>]`, or `--view [--session <id>]
172
+ * [--replay] [--port <n>] [--open]` for the office.
172
173
  * @returns {Promise<number>} Process exit code.
173
174
  */
174
175
  export async function pipeline(argv) {
@@ -179,6 +180,26 @@ export async function pipeline(argv) {
179
180
  console.error(` no .claude/graph.md in ${dir} — this draws a composed pipeline: \`nina compose\` writes one`);
180
181
  return 1;
181
182
  }
183
+ // The office: the same graph as a room, its agents at their desks, live or replayed (src/office.mjs).
184
+ const viewOnly = ['--session', '--replay', '--port', '--open'].filter((flag) => argv.includes(flag));
185
+ if (viewOnly.length > 0 && !argv.includes('--view')) {
186
+ console.error(` ${viewOnly.join(', ')} ${viewOnly.length > 1 ? 'belong' : 'belongs'} to the office, and mean${viewOnly.length > 1 ? '' : 's'} nothing without --view`);
187
+ return 2;
188
+ }
189
+ if (argv.includes('--view')) {
190
+ const drawingOnly = ['--for', '--since'].filter((flag) => argv.includes(flag));
191
+ if (drawingOnly.length > 0) {
192
+ console.error(` ${drawingOnly.join(', ')} ${drawingOnly.length > 1 ? 'are' : 'is'} for the drawing, not the office`);
193
+ return 2;
194
+ }
195
+ const { viewOffice } = await import('../office.mjs');
196
+ const port = arg('--port');
197
+ if (port !== null && !(/^\d+$/.test(port) && Number(port) <= 65535)) {
198
+ console.error(` --port takes a port number, 0 to 65535, and was given ${port}`);
199
+ return 2;
200
+ }
201
+ return viewOffice({ target: dir, wanted: arg('--session') || undefined, replay: argv.includes('--replay'), port: port === null ? undefined : Number(port), open: argv.includes('--open') });
202
+ }
182
203
  const graphText = readFileSync(graphPath, 'utf8');
183
204
  const graph = parseGraph(graphText);
184
205
  const words = stageWords(graphText);
@@ -364,6 +364,14 @@ export async function upgrade(argv, ctx) {
364
364
  const scriptsBefore = await shippedScripts(from.dir, surfaces);
365
365
  const newScripts = new Set([...(await shippedScripts(onto.dir, kept))].filter((s) => !scriptsBefore.has(s)));
366
366
  const unwired = newScripts.size > 0 ? await missingWiring(target, newScripts) : [];
367
+ // A script already run whose hooks this NINA needs more of — a matcher that grew — holds what it held, so it does
368
+ // not stop the move; it is said, with what fixes it.
369
+ const stillRun = new Set([...(await shippedScripts(onto.dir, kept))].filter((s) => scriptsBefore.has(s)));
370
+ const widened = stillRun.size > 0 ? (await missingWiring(target, stillRun)).filter((w) => / hook running `[^`]+` does not match /.test(w)) : [];
371
+ if (widened.length > 0) {
372
+ for (const w of widened) console.log(` · ${w}`);
373
+ console.log('');
374
+ }
367
375
  if (unwired.length > 0) {
368
376
  console.log(` ✗ ${unwired.length} hook(s) or script(s) that ${to}'s new ${[...newScripts].join(', ')} need(s) are not wired:`);
369
377
  for (const w of unwired) console.log(` ${w}`);
package/src/gate.mjs CHANGED
@@ -18,6 +18,8 @@
18
18
  * PreToolUse / PostToolUse on Agent, Task, SendMessage
19
19
  * a dispatch launched, and the agent it launched
20
20
  * UserPromptSubmit (human text only) the owner spoke, and every open loop gets one round more
21
+ * PreToolUse / PostToolUse on AskUserQuestion
22
+ * the orchestrator asked the owner, and was answered: when, for the office
21
23
  * PreToolUse on Agent, Task, SendMessage
22
24
  * the decision: let it through, or send the round past the cap to
23
25
  * the owner to confirm
@@ -280,11 +282,13 @@ function append(path, entry) {
280
282
  * @param {object[]} entries - The ledger, oldest first.
281
283
  * @param {ReturnType<typeof loopEdges>} loops - The capped loop-back edges.
282
284
  * @param {Map<string, Set<string>>} [forward] - Stage → the sources that hand passing work on to it.
283
- * @returns {{effect: (to: string) => {rounds: object[]}, rounds: object[]}}
285
+ * @returns {{effect: (to: string) => {rounds: object[]}, rounds: object[], open: object[], waiting: object[]}}
284
286
  * A round is `{source, target, token, round, max, edgeRound, ceiling, agents}`, plus `issue` (the id
285
287
  * that decides it) and `issues` (every id with its count) when it was counted by issue.
286
288
  * `effect` says what a dispatch to a stage would make now, without making it; `rounds` is every round
287
- * the recorded dispatches made.
289
+ * the recorded dispatches made; `open` each loop still counted, with whether a pass already closes it
290
+ * (`closing`), the stages a dispatch to which would (`targets`) and the stages whose pass would (`onward`);
291
+ * `waiting` each loop-back no fix has acted on yet.
288
292
  */
289
293
  export function replay(entries, loops, forward = new Map()) {
290
294
  // When each dispatch went out. A foreground dispatch's `dispatch` entry is written after the agent
@@ -329,15 +333,23 @@ export function replay(entries, loops, forward = new Map()) {
329
333
  for (const map of [streak, grace]) for (const key of [...map.keys()]) if (key.startsWith(`${source}|`)) map.delete(key);
330
334
  };
331
335
 
336
+ /**
337
+ * A source's waiting verdicts, settled: the rejections no later review of a fix cancelled, and whether a pass
338
+ * closes its loops — one that began after the latest fix, with no rejection running beside it.
339
+ */
340
+ const settle = (source, batch) => {
341
+ const passes = batch.filter((v) => !isLoopBack(v.verdict));
342
+ const live = batch.filter((v) => isLoopBack(v.verdict) && !passes.some((p) => p.launch > v.at && fixBetween(source, v.at, p.launch)));
343
+ return { live, closes: passes.some((p) => p.launch > (lastFix.get(source) ?? -1) && !live.some((v) => v.launch < p.at)) };
344
+ };
345
+
332
346
  const effect = (to, at) => {
333
347
  const rounds = [];
334
348
  const outcome = [];
335
349
  for (const [source, batch] of pending) {
336
350
  const byToken = loops.get(source);
337
351
  if (!byToken || batch.length === 0 || !targetsOf(source).has(to)) continue;
338
- const passes = batch.filter((v) => !isLoopBack(v.verdict));
339
- const live = batch.filter((v) => isLoopBack(v.verdict) && !passes.some((p) => p.launch > v.at && fixBetween(source, v.at, p.launch)));
340
- const closes = passes.some((p) => p.launch > (lastFix.get(source) ?? -1) && !live.some((v) => v.launch < p.at));
352
+ const { live, closes } = settle(source, batch);
341
353
  const routed = live.filter((v) => byToken.get(v.verdict)?.has(to));
342
354
  const fresh = routed.filter((v) => {
343
355
  const last = streak.get(`${source}|${v.verdict}`);
@@ -434,13 +446,18 @@ export function replay(entries, loops, forward = new Map()) {
434
446
  });
435
447
  // What is open when the replay ends: each loop with its rounds, the rounds the owner's replies gave it and the
436
448
  // issues it counted by, and each loop-back no fix has acted on yet — what a compaction takes from the orchestrator.
449
+ // A loop whose source passed after the latest fix is closed by the next dispatch it could act on, and until then
450
+ // is still in the count; it says so, and what would close each of the others. Listed as open, three loops
451
+ // approved the evening before a compaction were handed back as capped, and the orchestrator went looking for why.
437
452
  const open = [...streak.entries()]
438
453
  .filter(([key]) => key.split('|').length === 2)
439
454
  .map(([key, s]) => {
440
455
  const [source, token] = key.split('|');
441
456
  const cap = Math.max(0, ...(loops.get(source)?.get(token)?.values() ?? []));
442
457
  const issues = [...streak.keys()].filter((k) => k.startsWith(`${key}|`)).map((k) => k.slice(key.length + 1));
443
- return { source, token, rounds: s.n, cap, given: grace.get(key) ?? 0, issues };
458
+ const closing = settle(source, pending.get(source) ?? []).closes;
459
+ const onward = [...forward].filter(([, sources]) => sources.has(source)).map(([stage]) => stage);
460
+ return { source, token, rounds: s.n, cap, given: grace.get(key) ?? 0, issues, closing, targets: [...targetsOf(source)], onward };
444
461
  });
445
462
  const waiting = [...pending.entries()].flatMap(([source, batch]) => batch.filter((v) => isLoopBack(v.verdict)).map((v) => ({ source, verdict: v.verdict, issues: v.issues ?? [] })));
446
463
  return { effect: (to) => effect(to, entries.length), rounds: made, open, waiting };
@@ -591,6 +608,10 @@ function onPost(input, project, path, at) {
591
608
  }
592
609
  // A dispatch made inside a subagent is not the pipeline's: its stages are dispatched by the orchestrator.
593
610
  if (input.agent_id) return null;
611
+ if (tool === 'AskUserQuestion') {
612
+ append(path, { k: 'answered', at });
613
+ return null;
614
+ }
594
615
  if (!DISPATCH.has(tool)) return null;
595
616
  const response = input.tool_response && typeof input.tool_response === 'object' ? input.tool_response : {};
596
617
  let role;
@@ -644,13 +665,23 @@ function confirmation(r) {
644
665
 
645
666
  /** A dispatch is about to go out: note when, and send the round past its edge's cap to the owner to confirm. */
646
667
  function onPre(input, project, path, at) {
668
+ // The orchestrator asking the owner, as it asks: when it was, and nothing it said. Nothing counts it; the office
669
+ // (`nina pipeline --view`) draws the question over its head until the answer, which `onPost` records.
670
+ if (!input.agent_id && input.tool_name === 'AskUserQuestion') {
671
+ append(path, { k: 'question', at });
672
+ return null;
673
+ }
647
674
  if (input.agent_id || !DISPATCH.has(input.tool_name)) return null;
648
675
  const entries = readLedger(path);
649
- const role = input.tool_name === 'SendMessage' ? recipient(entries, project, input.tool_input?.to).role : input.tool_input?.subagent_type;
676
+ const to = input.tool_name === 'SendMessage' ? recipient(entries, project, input.tool_input?.to) : null;
677
+ const role = to ? to.role : input.tool_input?.subagent_type;
650
678
  if (!role || !project.graph.stages.has(role)) return null;
651
679
  // When it went out, whatever the owner decides: joined to its `dispatch` entry by the tool_use id,
652
- // it is the moment the fixer was sent — which is what a later review has to begin after.
653
- append(path, { k: 'pre', at, role, id: input.tool_use_id ?? null });
680
+ // it is the moment the fixer was sent — which is what a later review has to begin after. A message says so, and
681
+ // names its agent when the ledger already knows it — never a name the model wrote that nothing launched — so the
682
+ // office can tell a message to a running agent from a resume before the `dispatch` entry is written.
683
+ const known = to?.agent && entries.some((e) => e.k === 'dispatch' && e.agent === to.agent);
684
+ append(path, { k: 'pre', at, role, id: input.tool_use_id ?? null, ...(to ? { via: 'SendMessage', ...(known ? { agent: to.agent } : {}) } : {}) });
654
685
  const over = roundsFor(entries, role, project.loops, project.forward)
655
686
  .filter((r) => excess(r) > 0)
656
687
  .sort((a, b) => excess(b) - excess(a));
@@ -672,31 +703,84 @@ function onPre(input, project, path, at) {
672
703
  }
673
704
 
674
705
  /**
675
- * What the gate hands the orchestrator after its conversation was compacted: the loops still open and the
676
- * loop-backs waiting on a fix, the latest dispatches, where the checkout stands, and the two documents to read
677
- * again. The graph and the router are read once, at a session's start, and a compaction drops them with every
678
- * report; the router told the orchestrator to write "round 2 of max 2" in a dispatch so "you after a
679
- * compaction" could see it, and after a compaction it sees only a summary.
706
+ * A project's session ledgers, the newest first, and the one asked for: the latest, or the one whose id starts
707
+ * with `wanted`. `nina gate --status` and the office (`nina pipeline --view`) pick a session through this, so
708
+ * the two never show different ones.
709
+ *
710
+ * @param {string} root - The project directory.
711
+ * @param {string} [wanted] - The start of a session id; absent, the session whose ledger was written last.
712
+ * @returns {{dir: string, ledgers: {session: string, written: Date, path: string}[], picked: {session: string, written: Date, path: string}|null, why: string|null}}
713
+ * `why` says why nothing was picked, and is null when there is simply no ledger yet.
714
+ */
715
+ export function sessionLedgers(root, wanted) {
716
+ const dir = projectGateDir(root);
717
+ const ledgers = (existsSync(dir) ? readdirSync(dir) : [])
718
+ .filter((f) => f.endsWith('.jsonl') && f !== 'errors.jsonl')
719
+ .flatMap((f) => {
720
+ // Pruned between the listing and here, by another session: gone, and not a failure.
721
+ try {
722
+ return [{ session: f.slice(0, -'.jsonl'.length), written: statSync(join(dir, f)).mtime, path: join(dir, f) }];
723
+ } catch {
724
+ return [];
725
+ }
726
+ })
727
+ .sort((a, b) => b.written - a.written);
728
+ const matching = wanted ? ledgers.filter((l) => l.session.startsWith(wanted)) : ledgers.slice(0, 1);
729
+ if (matching.length === 1) return { dir, ledgers, picked: matching[0], why: null };
730
+ const why = !wanted ? null : matching.length === 0 ? `no session starts with "${wanted}"` : `${matching.length} sessions start with "${wanted}" — give more of the id`;
731
+ return { dir, ledgers, picked: null, why };
732
+ }
733
+
734
+ /** Stages as a sentence lists them: `qa`, `implementer or architect`, `a, b or c`. */
735
+ const either = (stages) => (stages.length < 2 ? stages.join('') : `${stages.slice(0, -1).join(', ')} or ${stages.at(-1)}`);
736
+
737
+ /**
738
+ * What a session's ledger says of its loops, a line each: every loop still counted, and what would close it;
739
+ * the loop-backs no fix has acted on yet; and the latest dispatches. It is what a compaction hands the
740
+ * orchestrator and what `nina gate --status` prints, so the two cannot disagree.
680
741
  *
681
742
  * @param {object} project - `loadProject`'s.
682
- * @param {string} path - The session's ledger.
683
- * @param {string} root - The project.
684
- * @returns {object}
743
+ * @param {object[]} entries - The session's ledger, oldest first.
744
+ * @returns {{lines: string[], open: number, closing: number, waiting: number}} The lines; the loops open, the
745
+ * loops a pass already settled, whose count the next dispatch drops, and the loop-backs waiting on a fix.
685
746
  */
686
- function afterCompaction(project, path, root) {
687
- const entries = readLedger(path);
747
+ export function loopState(project, entries) {
688
748
  const { open, waiting } = replay(entries, project.loops, project.forward);
689
- const lines = ['The conversation was compacted. What the NINA loop gate kept of this session:'];
749
+ const lines = [];
690
750
  for (const l of open) {
751
+ const count =
752
+ `${l.source} ${l.token} — ${l.rounds} round(s), capped at ${l.cap}${l.given ? ` and ${l.given} more from the owner's replies` : ''}` +
753
+ (l.issues.length ? `, issues ${l.issues.join(', ')}` : '');
754
+ const fix = waiting.some((w) => w.source === l.source) ? 'the fix it waits on' : 'the latest fix';
691
755
  lines.push(
692
- `- open loop: ${l.source} ${l.token} — ${l.rounds} round(s), capped at ${l.cap}${l.given ? ` and ${l.given} more from the owner's replies` : ''}` +
693
- (l.issues.length ? `, issues ${l.issues.join(', ')}` : ''),
756
+ l.closing
757
+ ? `- passed after its latest fix: ${count}. Those rounds are settled, and the gate drops them at the next dispatch to ${either(l.targets)}`
758
+ : `- open loop: ${count}. It closes when a ${l.source} launched after ${fix} passes with no rejection beside it` +
759
+ (l.onward.length ? `, or when ${either(l.onward)} passes, sent out after the ${l.source} last reported` : ''),
694
760
  );
695
761
  }
696
762
  for (const w of waiting) lines.push(`- waiting on a fix: ${w.source} ${w.verdict}${w.issues.length ? `, issues ${w.issues.join(', ')}` : ''}`);
697
763
  if (open.length === 0 && waiting.length === 0) lines.push('- no loop is open');
698
764
  const dispatched = entries.filter((e) => e.k === 'dispatch' && e.role).slice(-4);
699
765
  if (dispatched.length) lines.push(`- the latest dispatches: ${dispatched.map((e) => e.role).join(', ')}, the newest at ${String(dispatched.at(-1).at ?? '').slice(11, 16)} UTC`);
766
+ const closing = open.filter((l) => l.closing).length;
767
+ return { lines, open: open.length - closing, closing, waiting: waiting.length };
768
+ }
769
+
770
+ /**
771
+ * What the gate hands the orchestrator after its conversation was compacted: the loops still open and the
772
+ * loop-backs waiting on a fix, the latest dispatches, where the checkout stands, and the two documents to read
773
+ * again. The graph and the router are read once, at a session's start, and a compaction drops them with every
774
+ * report; the router told the orchestrator to write "round 2 of max 2" in a dispatch so "you after a
775
+ * compaction" could see it, and after a compaction it sees only a summary.
776
+ *
777
+ * @param {object} project - `loadProject`'s.
778
+ * @param {string} path - The session's ledger.
779
+ * @param {string} root - The project.
780
+ * @returns {object}
781
+ */
782
+ function afterCompaction(project, path, root) {
783
+ const lines = ['The conversation was compacted. What the NINA loop gate kept of this session:', ...loopState(project, readLedger(path)).lines];
700
784
  const git = (...args) => spawnSync('git', ['-C', root, ...args], { encoding: 'utf8', timeout: 2000 }).stdout?.trim() ?? '';
701
785
  const branch = git('rev-parse', '--abbrev-ref', 'HEAD');
702
786
  const last = git('log', '-1', '--format=%h %s');