@rubytech/create-maxy-code 0.1.186 → 0.1.188

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 (199) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-mcp/dist/index.js +4 -3
  3. package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
  4. package/payload/platform/lib/graph-mcp/src/index.ts +4 -3
  5. package/payload/platform/package.json +1 -0
  6. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  7. package/payload/platform/plugins/admin/PLUGIN.md +1 -0
  8. package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +16 -0
  9. package/payload/platform/plugins/admin/hooks/mcp-tool-missing.sh +94 -0
  10. package/payload/platform/plugins/admin/hooks/turn-completed-graph-write.sh +7 -0
  11. package/payload/platform/plugins/admin/skills/a4-print-documents/SKILL.md +14 -14
  12. package/payload/platform/plugins/admin/skills/deck-pages/SKILL.md +9 -23
  13. package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +1 -1
  14. package/payload/platform/plugins/admin/skills/publish-site/SKILL.md +3 -3
  15. package/payload/platform/plugins/admin/skills/skill-builder/SKILL.md +1 -1
  16. package/payload/platform/plugins/admin/skills/skill-builder/references/pdf-generation.md +4 -4
  17. package/payload/platform/plugins/admin/skills/stream-log-review/references/analysis-patterns.md +11 -7
  18. package/payload/platform/plugins/browser/.claude-plugin/plugin.json +1 -1
  19. package/payload/platform/plugins/browser/PLUGIN.md +78 -8
  20. package/payload/platform/plugins/browser/mcp/dist/index.js +111 -7
  21. package/payload/platform/plugins/browser/mcp/dist/index.js.map +1 -1
  22. package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.d.ts +98 -0
  23. package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.d.ts.map +1 -0
  24. package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.js +455 -0
  25. package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.js.map +1 -0
  26. package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.d.ts +9 -31
  27. package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.d.ts.map +1 -1
  28. package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.js +6 -165
  29. package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.js.map +1 -1
  30. package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.d.ts +153 -0
  31. package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.d.ts.map +1 -0
  32. package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.js +401 -0
  33. package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.js.map +1 -0
  34. package/payload/platform/plugins/docs/references/admin-ui.md +3 -4
  35. package/payload/platform/plugins/docs/references/deployment.md +1 -1
  36. package/payload/platform/plugins/docs/references/internals.md +6 -4
  37. package/payload/platform/plugins/docs/references/plugins-guide.md +2 -2
  38. package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
  39. package/payload/platform/plugins/email/PLUGIN.md +6 -2
  40. package/payload/platform/plugins/email/mcp/dist/index.js +38 -0
  41. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  42. package/payload/platform/plugins/email/mcp/dist/lib/providers.d.ts +28 -0
  43. package/payload/platform/plugins/email/mcp/dist/lib/providers.d.ts.map +1 -1
  44. package/payload/platform/plugins/email/mcp/dist/lib/providers.js +106 -0
  45. package/payload/platform/plugins/email/mcp/dist/lib/providers.js.map +1 -1
  46. package/payload/platform/plugins/email/references/email-reference.md +19 -15
  47. package/payload/platform/plugins/graph/.claude-plugin/plugin.json +8 -0
  48. package/payload/platform/plugins/graph/PLUGIN.md +37 -0
  49. package/payload/platform/plugins/memory/PLUGIN.md +2 -0
  50. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts +1 -1
  51. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +1 -1
  52. package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.d.ts.map +1 -1
  53. package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.js +3 -2
  54. package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.js.map +1 -1
  55. package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts.map +1 -1
  56. package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js +2 -1
  57. package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js.map +1 -1
  58. package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.d.ts.map +1 -1
  59. package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.js +2 -1
  60. package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.js.map +1 -1
  61. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts +1 -1
  62. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts.map +1 -1
  63. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js +3 -2
  64. package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js.map +1 -1
  65. package/payload/platform/plugins/memory/references/graph-primitives.md +36 -27
  66. package/payload/platform/plugins/obsidian-import/PLUGIN.md +1 -1
  67. package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js +1 -1
  68. package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js.map +1 -1
  69. package/payload/platform/scripts/check-canonical-tool-names.mjs +110 -0
  70. package/payload/platform/scripts/check-cypher-int-params.mjs +277 -0
  71. package/payload/platform/scripts/generate-canonical-tool-names.mjs +63 -0
  72. package/payload/platform/scripts/lib/canonical-tool-names.mjs +88 -0
  73. package/payload/platform/scripts/setup-account.sh +6 -0
  74. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.d.ts +7 -0
  75. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.d.ts.map +1 -0
  76. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts +5 -0
  77. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -0
  78. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +204 -0
  79. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -0
  80. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.js +23 -0
  81. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.js.map +1 -0
  82. package/payload/platform/services/claude-session-manager/dist/index.js +30 -6
  83. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  84. package/payload/platform/services/claude-session-manager/dist/mcp-tools-probe.d.ts +6 -4
  85. package/payload/platform/services/claude-session-manager/dist/mcp-tools-probe.d.ts.map +1 -1
  86. package/payload/platform/services/claude-session-manager/dist/mcp-tools-probe.js.map +1 -1
  87. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +1 -0
  88. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  89. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +50 -26
  90. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  91. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +40 -0
  92. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -0
  93. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +200 -0
  94. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -0
  95. package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts.map +1 -1
  96. package/payload/platform/services/claude-session-manager/dist/specialist-drift.js +50 -34
  97. package/payload/platform/services/claude-session-manager/dist/specialist-drift.js.map +1 -1
  98. package/payload/platform/services/claude-session-manager/dist/tool-surface.d.ts.map +1 -1
  99. package/payload/platform/services/claude-session-manager/dist/tool-surface.js +23 -13
  100. package/payload/platform/services/claude-session-manager/dist/tool-surface.js.map +1 -1
  101. package/payload/platform/templates/specialists/agents/content-producer.md +4 -4
  102. package/payload/platform/templates/specialists/agents/librarian.md +2 -2
  103. package/payload/platform/templates/specialists/agents/personal-assistant.md +6 -2
  104. package/payload/platform/templates/specialists/agents/project-manager.md +1 -1
  105. package/payload/platform/templates/specialists/agents/research-assistant.md +1 -1
  106. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts.map +1 -1
  107. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js +33 -1
  108. package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js.map +1 -1
  109. package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-retrieve-conditioning.ts +5 -2
  110. package/payload/server/{chunk-L2YK2VK3.js → chunk-WWD4TCJJ.js} +3 -17
  111. package/payload/server/maxy-edge.js +1 -1
  112. package/payload/server/public/assets/Checkbox-ChWmQ7if.js +1 -0
  113. package/payload/server/public/assets/admin-BLi_fAKQ.js +216 -0
  114. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DZoOzmjE.js → architectureDiagram-Q4EWVU46-DSqzB21V.js} +1 -1
  115. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DG3yufCR.js → blockDiagram-DXYQGD6D--otTO_UB.js} +1 -1
  116. package/payload/server/public/assets/brand-Djtb-tLQ.js +9 -0
  117. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-BBx5wXuZ.js → c4Diagram-AHTNJAMY-CBszqa9W.js} +1 -1
  118. package/payload/server/public/assets/channel-C-SJYogg.js +1 -0
  119. package/payload/server/public/assets/{chunk-336JU56O-DwP7g5ns.js → chunk-336JU56O-ClAJP2KY.js} +2 -2
  120. package/payload/server/public/assets/{chunk-426QAEUC-DXzGdc22.js → chunk-426QAEUC-CgLucS4H.js} +1 -1
  121. package/payload/server/public/assets/{chunk-4TB4RGXK-1LDRjk0E.js → chunk-4TB4RGXK-BvOX6mSk.js} +1 -1
  122. package/payload/server/public/assets/{chunk-5FUZZQ4R-BHjWhwhn.js → chunk-5FUZZQ4R-uyN4XtrW.js} +1 -1
  123. package/payload/server/public/assets/{chunk-5PVQY5BW-CwPHRVN8.js → chunk-5PVQY5BW-B6xHEzh2.js} +1 -1
  124. package/payload/server/public/assets/{chunk-EDXVE4YY-7443hrYZ.js → chunk-EDXVE4YY-9objw8tW.js} +1 -1
  125. package/payload/server/public/assets/{chunk-ENJZ2VHE-BTnmmnmX.js → chunk-ENJZ2VHE-pQpbHIJL.js} +1 -1
  126. package/payload/server/public/assets/{chunk-ICPOFSXX-CPnMJplL.js → chunk-ICPOFSXX-qS23jC3x.js} +1 -1
  127. package/payload/server/public/assets/{chunk-OYMX7WX6-DCQ5Q4RS.js → chunk-OYMX7WX6-C9YUwGqp.js} +1 -1
  128. package/payload/server/public/assets/{chunk-U2HBQHQK-BmfZG8e4.js → chunk-U2HBQHQK-D_pYedRJ.js} +1 -1
  129. package/payload/server/public/assets/{chunk-X2U36JSP-Da2yJL2u.js → chunk-X2U36JSP-nxf6GHD-.js} +1 -1
  130. package/payload/server/public/assets/{chunk-YZCP3GAM-BdUmMUJ6.js → chunk-YZCP3GAM-DqKT124V.js} +1 -1
  131. package/payload/server/public/assets/{chunk-ZZ45TVLE-abkGirrZ.js → chunk-ZZ45TVLE-CLwvpbvu.js} +1 -1
  132. package/payload/server/public/assets/classDiagram-6PBFFD2Q-OBkxl5to.js +1 -0
  133. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-CkTyus-1.js +1 -0
  134. package/payload/server/public/assets/clone-VufdPzJ-.js +1 -0
  135. package/payload/server/public/assets/{dagre-BVVuAEsU.js → dagre-COCXt23n.js} +1 -1
  136. package/payload/server/public/assets/{dagre-KV5264BT-VG2kNj19.js → dagre-KV5264BT-DmuVMrt4.js} +1 -1
  137. package/payload/server/public/assets/data-TlJbMIfa.js +1 -0
  138. package/payload/server/public/assets/{diagram-5BDNPKRD-DRiVy69K.js → diagram-5BDNPKRD-DOiLSgj_.js} +1 -1
  139. package/payload/server/public/assets/{diagram-G4DWMVQ6-DtpiRTYX.js → diagram-G4DWMVQ6-quY6PSZQ.js} +1 -1
  140. package/payload/server/public/assets/{diagram-MMDJMWI5-Da3t99fY.js → diagram-MMDJMWI5-tN2yiz0C.js} +1 -1
  141. package/payload/server/public/assets/{diagram-TYMM5635-BzQmLTJQ.js → diagram-TYMM5635-CjrRQNQ0.js} +1 -1
  142. package/payload/server/public/assets/{erDiagram-SMLLAGMA-D3Ll-GDw.js → erDiagram-SMLLAGMA-CrNNMxkA.js} +1 -1
  143. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-D17bje6d.js → flowDiagram-DWJPFMVM-CCUsJph7.js} +1 -1
  144. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-BjeQ-Bun.js → ganttDiagram-T4ZO3ILL-18276bri.js} +1 -1
  145. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-LtX4pvZS.js → gitGraphDiagram-UUTBAWPF-CsLsKMjN.js} +1 -1
  146. package/payload/server/public/assets/graph-D2Yvni5R.js +1 -0
  147. package/payload/server/public/assets/graph-labels-uksnCgSC.js +1 -0
  148. package/payload/server/public/assets/{graphlib-CzwsfZOw.js → graphlib-CmtUzIni.js} +1 -1
  149. package/payload/server/public/assets/{infoDiagram-42DDH7IO-DOgH4i52.js → infoDiagram-42DDH7IO-Dny_ZNz3.js} +1 -1
  150. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-DYqy5BFj.js → ishikawaDiagram-UXIWVN3A-kUhIUQE-.js} +1 -1
  151. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CsFpIULJ.js → journeyDiagram-VCZTEJTY-DekMvAKB.js} +1 -1
  152. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-5uZuArw7.js → kanban-definition-6JOO6SKY-st0HNZZo.js} +1 -1
  153. package/payload/server/public/assets/lib-Ca7uCvl9.js +33 -0
  154. package/payload/server/public/assets/{line-BY8PmCSW.js → line-BaNk-I7J.js} +1 -1
  155. package/payload/server/public/assets/{mermaid-parser.core-CJ_9xqwK.js → mermaid-parser.core-CRCHYQ_F.js} +1 -1
  156. package/payload/server/public/assets/{mermaid.core-CXznBkX7.js → mermaid.core-BaIYEciF.js} +3 -3
  157. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-DZbHBud5.js → mindmap-definition-QFDTVHPH-D4u4KU2v.js} +1 -1
  158. package/payload/server/public/assets/{page-CfN7nw1J.js → page-DayPduMN.js} +2 -2
  159. package/payload/server/public/assets/page-QQmuB04w.js +1 -0
  160. package/payload/server/public/assets/{pieDiagram-DEJITSTG-QmOi4DRG.js → pieDiagram-DEJITSTG-BItWEilY.js} +1 -1
  161. package/payload/server/public/assets/public-Dze4yArS.js +7 -0
  162. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-Cpidq6Xj.js → quadrantDiagram-34T5L4WZ-CeWDBvmH.js} +1 -1
  163. package/payload/server/public/assets/{requirementDiagram-MS252O5E-CBqtfbzR.js → requirementDiagram-MS252O5E-CcosjedW.js} +1 -1
  164. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-4BiY4dV9.js → sankeyDiagram-XADWPNL6-DNGeFpUj.js} +1 -1
  165. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-DVlL-l2u.js → sequenceDiagram-FGHM5R23-Cqy_deB6.js} +1 -1
  166. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-CAm9dW68.js → stateDiagram-FHFEXIEX-2yoAqEsA.js} +1 -1
  167. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Dc5siYvu.js +1 -0
  168. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CD-tZc4y.js → timeline-definition-GMOUNBTQ-Dmeghpw1.js} +1 -1
  169. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-Davcm0TF.js → vennDiagram-DHZGUBPP-TX5D-Zzp.js} +1 -1
  170. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-40LBYw_Y.js → wardleyDiagram-NUSXRM2D-B7mcpl5E.js} +1 -1
  171. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DEPT-__v.js → xychartDiagram-5P7HB3ND-DH77CxKf.js} +1 -1
  172. package/payload/server/public/brand/Archive/favicon.ico +0 -0
  173. package/payload/server/public/brand/Archive/maxy-black.png +0 -0
  174. package/payload/server/public/brand/Archive/maxy-horizontal.png +0 -0
  175. package/payload/server/public/brand/Archive/maxy-monochrome.png +0 -0
  176. package/payload/server/public/brand/Archive/maxy-square.png +0 -0
  177. package/payload/server/public/brand/Archive/maxy.png +0 -0
  178. package/payload/server/public/brand/Archive/star.png +0 -0
  179. package/payload/server/public/brand/Archive.zip +0 -0
  180. package/payload/server/public/data.html +5 -5
  181. package/payload/server/public/graph.html +6 -6
  182. package/payload/server/public/index.html +8 -8
  183. package/payload/server/public/public.html +5 -5
  184. package/payload/server/server.js +312 -793
  185. package/payload/server/public/assets/ChatInput-CvRaT05l.js +0 -13
  186. package/payload/server/public/assets/Checkbox-BJKNkUYu.js +0 -1
  187. package/payload/server/public/assets/admin-BNy4N2d6.js +0 -217
  188. package/payload/server/public/assets/channel-DdtUtt5g.js +0 -1
  189. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CKk2DFZe.js +0 -1
  190. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E--YvfDQCh.js +0 -1
  191. package/payload/server/public/assets/clone-Cw6UfPGM.js +0 -1
  192. package/payload/server/public/assets/data-uBpwaldK.js +0 -1
  193. package/payload/server/public/assets/graph-labels-Cn9jAE6I.js +0 -1
  194. package/payload/server/public/assets/graph-tvTPvBaz.js +0 -1
  195. package/payload/server/public/assets/lib-p4ylk2XS.js +0 -29
  196. package/payload/server/public/assets/page-B3VYaBcf.js +0 -1
  197. package/payload/server/public/assets/public-Bbew78md.js +0 -7
  198. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B2EzXi50.js +0 -1
  199. /package/payload/server/public/assets/{ChatInput-DzacFNMk.css → brand-DzacFNMk.css} +0 -0
@@ -15,6 +15,7 @@
15
15
  * most-recent completed retrospective) carry the deferred typed-edge work
16
16
  * that will land at the next /end.
17
17
  */
18
+ import { int } from "neo4j-driver";
18
19
  export async function sessionRetrospectiveSkipRate(args, session, accountId) {
19
20
  const windowSize = Math.max(1, Math.min(args.windowSize ?? 10, 100));
20
21
  // Conversations carry `retrospectiveCompletedAt` when the sentinel tool
@@ -88,7 +89,7 @@ export async function sessionRetrospectiveSkipRate(args, session, accountId) {
88
89
  try {
89
90
  const result = await session.run(cypher, {
90
91
  accountId,
91
- windowSize,
92
+ windowSize: int(windowSize),
92
93
  proseLabels,
93
94
  });
94
95
  records = result.records;
@@ -96,7 +97,7 @@ export async function sessionRetrospectiveSkipRate(args, session, accountId) {
96
97
  catch {
97
98
  const result = await session.run(fallbackCypher, {
98
99
  accountId,
99
- windowSize,
100
+ windowSize: int(windowSize),
100
101
  proseLabels,
101
102
  });
102
103
  records = result.records;
@@ -1 +1 @@
1
- {"version":3,"file":"session-retrospective-skip-rate.js","sourceRoot":"","sources":["../../src/tools/session-retrospective-skip-rate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAgBH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,IAAyB,EACzB,OAAgB,EAChB,SAAiB;IAEjB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IAErE,wEAAwE;IACxE,kEAAkE;IAClE,sEAAsE;IACtE,6BAA6B;IAC7B,wEAAwE;IACxE,wEAAwE;IACxE,mEAAmE;IACnE,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;GAuBd,CAAC;IAEF,4EAA4E;IAC5E,oDAAoD;IACpD,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;GAuBtB,CAAC;IAEF,MAAM,WAAW,GAAG;QAClB,SAAS;QACT,MAAM;QACN,SAAS;QACT,OAAO;QACP,MAAM;QACN,MAAM;QACN,MAAM;QACN,QAAQ;KACT,CAAC;IAEF,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE;YACvC,SAAS;YACT,UAAU;YACV,WAAW;SACZ,CAAC,CAAC;QACH,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE;YAC/C,SAAS;YACT,UAAU;YACV,WAAW;SACZ,CAAC,CAAC;QACH,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC3B,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,cAAc,EAAE,CAAC;YACjB,uBAAuB,EAAE,CAAC;YAC1B,QAAQ,EAAE,CAAC;YACX,qBAAqB,EAAE,IAAI;SAC5B,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACrB,MAAM,cAAc,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5D,MAAM,uBAAuB,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9E,MAAM,aAAa,GAAG,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,cAAc,GAAG,CAAC;QACjC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,GAAG,uBAAuB,CAAC,GAAG,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC,CAAC;IACN,MAAM,YAAY,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3C,MAAM,gBAAgB,GAAG,aAAa,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAE9E,OAAO,CAAC,GAAG,CACT,+CAA+C,YAAY,YAAY,cAAc,cAAc,uBAAuB,cAAc,QAAQ,mBAAmB,gBAAgB,IAAI,MAAM,EAAE,CAChM,CAAC;IAEF,OAAO;QACL,cAAc;QACd,uBAAuB;QACvB,QAAQ;QACR,qBAAqB,EAAE,gBAAgB;KACxC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"session-retrospective-skip-rate.js","sourceRoot":"","sources":["../../src/tools/session-retrospective-skip-rate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,GAAG,EAAgB,MAAM,cAAc,CAAC;AAcjD,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,IAAyB,EACzB,OAAgB,EAChB,SAAiB;IAEjB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;IAErE,wEAAwE;IACxE,kEAAkE;IAClE,sEAAsE;IACtE,6BAA6B;IAC7B,wEAAwE;IACxE,wEAAwE;IACxE,mEAAmE;IACnE,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;GAuBd,CAAC;IAEF,4EAA4E;IAC5E,oDAAoD;IACpD,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;GAuBtB,CAAC;IAEF,MAAM,WAAW,GAAG;QAClB,SAAS;QACT,MAAM;QACN,SAAS;QACT,OAAO;QACP,MAAM;QACN,MAAM;QACN,MAAM;QACN,QAAQ;KACT,CAAC;IAEF,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE;YACvC,SAAS;YACT,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC;YAC3B,WAAW;SACZ,CAAC,CAAC;QACH,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE;YAC/C,SAAS;YACT,UAAU,EAAE,GAAG,CAAC,UAAU,CAAC;YAC3B,WAAW;SACZ,CAAC,CAAC;QACH,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC3B,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,cAAc,EAAE,CAAC;YACjB,uBAAuB,EAAE,CAAC;YAC1B,QAAQ,EAAE,CAAC;YACX,qBAAqB,EAAE,IAAI;SAC5B,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACrB,MAAM,cAAc,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5D,MAAM,uBAAuB,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9E,MAAM,aAAa,GAAG,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,cAAc,GAAG,CAAC;QACjC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,GAAG,uBAAuB,CAAC,GAAG,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAClF,CAAC,CAAC,CAAC,CAAC;IACN,MAAM,YAAY,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3C,MAAM,gBAAgB,GAAG,aAAa,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAE9E,OAAO,CAAC,GAAG,CACT,+CAA+C,YAAY,YAAY,cAAc,cAAc,uBAAuB,cAAc,QAAQ,mBAAmB,gBAAgB,IAAI,MAAM,EAAE,CAChM,CAAC;IAEF,OAAO;QACL,cAAc;QACd,uBAAuB;QACvB,QAAQ;QACR,qBAAqB,EAAE,gBAAgB;KACxC,CAAC;AACJ,CAAC"}
@@ -40,31 +40,40 @@ token-limit discipline the upstream server enforces.
40
40
 
41
41
  ## When the memory tools are absent
42
42
 
43
- If `mcp__memory__memory-write`, `mcp__memory__memory-search`,
44
- `mcp__memory__memory-reindex`,
45
- `mcp__memory__session-compact`, `mcp__memory__session-compact-status`, or
46
- `mcp__memory__conversation-search` is missing from your tool list, the
47
- memory MCP server failed to start on this device. Reply once with
48
- exactly:
49
-
50
- > The memory MCP server failed to start on this device. Run the admin
43
+ The maxy MCP tool names are bound to the live registry (Task 502): the
44
+ canonical long name `mcp__plugin_memory_memory__<tool>` is generated from the
45
+ memory plugin's manifest, a build gate fails when any instruction names a tool
46
+ the registry does not serve, and a PostToolUse hook turns a missing-tool call
47
+ into a fixed envelope. So a missing memory tool is one of two distinct states,
48
+ not the single "failed to start" the old contract assumed:
49
+
50
+ - **Server dead.** None of the `mcp__plugin_memory_memory__*` tools appear in
51
+ your tool list at all. The per-account memory server did not start; the
52
+ platform's `[mcp-init-error] server=memory` line in the per-conversation
53
+ stream log names it.
54
+ - **Name not registered.** The server is up and other
55
+ `mcp__plugin_memory_memory__*` tools are present, but a specific call returns
56
+ `No such tool available`. The `mcp-tool-missing` hook has already logged
57
+ `[mcp-tool-missing] server=memory tool=<tool>` and returned the deterministic
58
+ envelope you are reading.
59
+
60
+ In either state, reply once with exactly:
61
+
62
+ > The memory MCP server is unavailable on this device. Run the admin
51
63
  > system-status check to diagnose — do not retry by other routes.
52
64
 
53
- Then stop. Do not search for a similarly-named tool via `ToolSearch`, do
54
- not improvise via `maxy-graph-read_neo4j_cypher` (it is read-only — every
55
- write goes through the schema-aware memory tools), do not paraphrase.
56
- A missing memory tool is a deterministic failure of the per-account
57
- memory server: the platform's `[mcp-init-error] server=memory` line in
58
- the per-conversation stream log already names it; the user-facing reply
59
- exists so the operator knows the session cannot proceed without an
60
- operator-side fix (the loud-fail contract applies equally
61
- to graph and memory tool initialisation).
65
+ Then stop. Do not search for a similarly-named tool via `ToolSearch`, do not
66
+ improvise via `mcp__plugin_graph_graph__maxy-graph-read_neo4j_cypher` (it is
67
+ read-only — every write goes through the schema-aware memory tools), do not
68
+ paraphrase, do not describe the server as "warming up". The reply exists so the
69
+ operator knows the session cannot proceed without an operator-side fix (the
70
+ loud-fail contract applies equally to graph and memory tool initialisation).
62
71
 
63
72
  ## Mutation intents
64
73
 
65
74
  The graph tools above are read-only. Every write intent has a schema-aware
66
75
  tool that already exists — match the user's prose to a tool in this table
67
- rather than improvising. `mcp__admin__admin-remove` is in your initial
76
+ rather than improvising. `mcp__plugin_admin_admin__admin-remove` is in your initial
68
77
  tool list; the other tools below are deferred (they do not appear until
69
78
  you invoke `ToolSearch` by the exact name you pick from the table).
70
79
  Do not use `ToolSearch` to *discover* the tool by keyword — identify it
@@ -78,14 +87,14 @@ graph as a single action.
78
87
 
79
88
  | User intent | Tool | When to pick this, not the next row |
80
89
  |---|---|---|
81
- | "remove admin X", "take away their admin access", "revoke admin" | `mcp__admin__admin-remove` | Device-level admin offboarding — detaches the `(AdminUser)-[:ADMIN_OF]->(LocalBusiness)` edge and updates `platform/config/users.json`. Not for scrubbing personal data — use the GDPR contact-erase tools for that. |
82
- | "delete this specific node", "remove this entry by its id", "get rid of node X", "remove orphans", "prune", "clean up" | `mcp__memory__memory-delete` | Soft-deletes a single node by `elementId` (adds `:Trashed`, preserves relationships, snapshots unique keys, invisible to `memory-search` and any `notTrashed`-filtered read). For KnowledgeDocument, cascades to Sections and Chunks. Enumerate candidates first with a Cypher read, confirm with the user, then delete each by `elementId`. There is no bulk "delete everything without relationships" tool. Per the write doctrine (`.docs/neo4j.md` § Write doctrine), **new** orphans should be impossible — every writer now rejects zero-edge calls at the MCP tool-surface. Existing orphans from legacy data are legitimate backfill candidates but are not produced by any live writer; if you see a freshly-created orphan, it is a bug in a writer that bypassed `writeNodeWithEdges`, not expected state. |
83
- | "undo that delete", "I deleted X by mistake, bring it back", "restore the contact I just trashed" | `mcp__memory__memory-restore` | Removes `:Trashed`, restores snapshotted unique-key properties. Fails loudly when an active node already holds the unique slot (e.g. a fresh `LocalBusiness` was created while the old one was in trash) — the error names the conflicting `elementId` so you can ask the user how to resolve. |
84
- | "empty the trash", "purge old soft-deletes", "what's been sitting in trash and ready to clear" | `mcp__memory__memory-empty-trash` | Hard-deletes trashed nodes whose `trashedAt` is older than `graceDays` (default 30); `dryRun=true` lists candidates without mutating. Use `dryRun` first, show the list to the user, then run for real. For KnowledgeDocument candidates, the on-disk attachment directory is removed too. |
85
- | "scrub this conversation's memory", "remove this claim the bot picked up", "forget what we said in that thread" | `mcp__memory__conversation-memory-expunge` | Removes deny-listable content from `:Memory` nodes scoped to one conversation. Requires an explicit pattern and sessionId. Not for deleting individual nodes — use `memory-delete`. |
86
- | "update a property", "fix a typo in a node", "change the role field on X", "null this poisonous string" | `mcp__memory__memory-update` | Targeted property edit on an existing node, schema-validated. Use to null individual string properties matching a deny-list entry once you've identified the offender via a Cypher read. Not for creating new nodes — use `memory-write`. |
87
- | "update X by name, search didn't return it", "wire this Org but search came back empty", "I know it exists but I don't have the elementId" | `mcp__memory__memory-update-by-name` | Repair surface for the case where `memory-search` cannot return a row you know exists by name. Resolves the unique `(label, name, accountId)` tuple, then delegates to `memory-update`. Rejects loudly on zero or ambiguous matches (the rejection lists candidate elementIds so you can pick one). Trashed nodes excluded. Use this only after `memory-search` failed — for normal updates, get the elementId from search and call `memory-update` directly. |
88
- | "don't surface these substrings in future memory results", "watch for this string" | `mcp__memory__graph-prune-denylist-add` | Records a substring as operator-curated signal. Nothing auto-applies it — when a subsequent memory-search or cypher read surfaces a match, use `memory-update` or `memory-delete` with discretion to scrub it. |
90
+ | "remove admin X", "take away their admin access", "revoke admin" | `mcp__plugin_admin_admin__admin-remove` | Device-level admin offboarding — detaches the `(AdminUser)-[:ADMIN_OF]->(LocalBusiness)` edge and updates `platform/config/users.json`. Not for scrubbing personal data — use the GDPR contact-erase tools for that. |
91
+ | "delete this specific node", "remove this entry by its id", "get rid of node X", "remove orphans", "prune", "clean up" | `mcp__plugin_memory_memory__memory-delete` | Soft-deletes a single node by `elementId` (adds `:Trashed`, preserves relationships, snapshots unique keys, invisible to `memory-search` and any `notTrashed`-filtered read). For KnowledgeDocument, cascades to Sections and Chunks. Enumerate candidates first with a Cypher read, confirm with the user, then delete each by `elementId`. There is no bulk "delete everything without relationships" tool. Per the write doctrine (`.docs/neo4j.md` § Write doctrine), **new** orphans should be impossible — every writer now rejects zero-edge calls at the MCP tool-surface. Existing orphans from legacy data are legitimate backfill candidates but are not produced by any live writer; if you see a freshly-created orphan, it is a bug in a writer that bypassed `writeNodeWithEdges`, not expected state. |
92
+ | "undo that delete", "I deleted X by mistake, bring it back", "restore the contact I just trashed" | `mcp__plugin_memory_memory__memory-restore` | Removes `:Trashed`, restores snapshotted unique-key properties. Fails loudly when an active node already holds the unique slot (e.g. a fresh `LocalBusiness` was created while the old one was in trash) — the error names the conflicting `elementId` so you can ask the user how to resolve. |
93
+ | "empty the trash", "purge old soft-deletes", "what's been sitting in trash and ready to clear" | `mcp__plugin_memory_memory__memory-empty-trash` | Hard-deletes trashed nodes whose `trashedAt` is older than `graceDays` (default 30); `dryRun=true` lists candidates without mutating. Use `dryRun` first, show the list to the user, then run for real. For KnowledgeDocument candidates, the on-disk attachment directory is removed too. |
94
+ | "scrub this conversation's memory", "remove this claim the bot picked up", "forget what we said in that thread" | `mcp__plugin_memory_memory__conversation-memory-expunge` | Removes deny-listable content from `:Memory` nodes scoped to one conversation. Requires an explicit pattern and sessionId. Not for deleting individual nodes — use `memory-delete`. |
95
+ | "update a property", "fix a typo in a node", "change the role field on X", "null this poisonous string" | `mcp__plugin_memory_memory__memory-update` | Targeted property edit on an existing node, schema-validated. Use to null individual string properties matching a deny-list entry once you've identified the offender via a Cypher read. Not for creating new nodes — use `memory-write`. |
96
+ | "update X by name, search didn't return it", "wire this Org but search came back empty", "I know it exists but I don't have the elementId" | `mcp__plugin_memory_memory__memory-update-by-name` | Repair surface for the case where `memory-search` cannot return a row you know exists by name. Resolves the unique `(label, name, accountId)` tuple, then delegates to `memory-update`. Rejects loudly on zero or ambiguous matches (the rejection lists candidate elementIds so you can pick one). Trashed nodes excluded. Use this only after `memory-search` failed — for normal updates, get the elementId from search and call `memory-update` directly. |
97
+ | "don't surface these substrings in future memory results", "watch for this string" | `mcp__plugin_memory_memory__graph-prune-denylist-add` | Records a substring as operator-curated signal. Nothing auto-applies it — when a subsequent memory-search or cypher read surfaces a match, use `memory-update` or `memory-delete` with discretion to scrub it. |
89
98
 
90
99
  **Never run `cypher-shell` via `Bash` to work around the read-only tool.**
91
100
  The admin session's `Bash` surface has no path to the per-brand Neo4j
@@ -107,7 +116,7 @@ MATCH (a:AdminUser) WHERE NOT (a)--() RETURN elementId(a), a.userId, a.createdAt
107
116
  ```
108
117
 
109
118
  Show the list to the user. For each confirmed entry, call
110
- `mcp__memory__memory-delete` with the `elementId`. Do not batch-delete
119
+ `mcp__plugin_memory_memory__memory-delete` with the `elementId`. Do not batch-delete
111
120
  by pattern — on 2026-04-20 an autonomous orphan-delete rule wiped 19
112
121
  nodes in one call, with no property recovery possible. Structural mass-
113
122
  deletion by shape is banned; discretion per node is the only sanctioned
@@ -18,7 +18,7 @@ The admin agent delegates to `specialists:database-operator` when the operator d
18
18
 
19
19
  ## How parsing happens
20
20
 
21
- The skill does not parse markdown. Vault traversal, frontmatter YAML, wikilink regex, tag scanning, daily-notes detection, and attachment resolution all happen server-side inside `mcp__memory__obsidian-vault-import` (memory plugin). The skill is the operator-facing conversation surface only: confirm owner → select filters → invoke the tool with `mode='dry-run'` → present ambiguities → invoke with `mode='commit'` and the operator's resolutions.
21
+ The skill does not parse markdown. Vault traversal, frontmatter YAML, wikilink regex, tag scanning, daily-notes detection, and attachment resolution all happen server-side inside `mcp__plugin_memory_memory__obsidian-vault-import` (memory plugin). The skill is the operator-facing conversation surface only: confirm owner → select filters → invoke the tool with `mode='dry-run'` → present ambiguities → invoke with `mode='commit'` and the operator's resolutions.
22
22
 
23
23
  ## Intra-plugin growth
24
24
 
@@ -33,7 +33,7 @@ export async function messageHistory(params) {
33
33
  const session = getSession();
34
34
  try {
35
35
  const conditions = ["m.accountId = $accountId"];
36
- const queryParams = { accountId, limit };
36
+ const queryParams = { accountId, limit: neo4j.int(limit) };
37
37
  if (channel) {
38
38
  conditions.push("m.channel = $channel");
39
39
  queryParams.channel = channel;
@@ -1 +1 @@
1
- {"version":3,"file":"message-history.js","sourceRoot":"","sources":["../../src/tools/message-history.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"message-history.js","sourceRoot":"","sources":["../../src/tools/message-history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ // Strong build gate for canonical maxy MCP tool names (Task 502).
3
+ //
4
+ // Two checks, both must pass:
5
+ // 1. Regenerate-and-diff: the committed generated artifact
6
+ // (canonical-tool-names.generated.ts) must byte-match a fresh generation
7
+ // from platform/plugins/*/PLUGIN.md. A stale artifact fails the build —
8
+ // this is what stops a PLUGIN.md tool add/remove from drifting away from
9
+ // the names instructions are bound to.
10
+ // 2. Instruction-name validation: every `mcp__...` tool name in an
11
+ // instruction file (specialist templates, agent prompts, plugin
12
+ // references/skills, PLUGIN.md prose) that targets a MAXY plugin must be
13
+ // the canonical long prefix AND must exist in the live set. A short-prefix
14
+ // maxy name (mcp__<plugin>__<tool>) fails — it must be long. An unknown
15
+ // long-prefix maxy name fails — typo or stale. Non-maxy bridge namespaces
16
+ // (playwright, etc.) are ignored: their tool names are upstream-owned.
17
+
18
+ import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs'
19
+ import { join } from 'node:path'
20
+ import { computeCanonicalToolNames, MAXY_CODE_ROOT } from './lib/canonical-tool-names.mjs'
21
+ import { renderArtifact, renderPluginsList } from './generate-canonical-tool-names.mjs'
22
+
23
+ const GENERATED = join(
24
+ MAXY_CODE_ROOT, 'platform', 'services', 'claude-session-manager', 'src',
25
+ 'canonical-tool-names.generated.ts',
26
+ )
27
+ const PLUGINS_TXT = join(
28
+ MAXY_CODE_ROOT, 'platform', 'plugins', 'admin', 'hooks', 'lib',
29
+ 'maxy-mcp-plugins.txt',
30
+ )
31
+
32
+ const SCAN_DIRS = [
33
+ join(MAXY_CODE_ROOT, 'platform', 'templates', 'specialists', 'agents'),
34
+ join(MAXY_CODE_ROOT, 'platform', 'plugins'),
35
+ ]
36
+ const SCAN_EXT = new Set(['.md'])
37
+ const SKIP_DIRS = new Set(['node_modules', 'dist', 'build', 'payload', '__tests__'])
38
+ // mcp__<seg>__<tool> — seg may itself contain `_` (long prefix plugin_a_b).
39
+ const TOOL_RE = /mcp__([A-Za-z0-9-]+(?:_[A-Za-z0-9-]+)*)__([A-Za-z0-9_-]+)/g
40
+
41
+ function* walk(dir) {
42
+ let entries
43
+ try { entries = readdirSync(dir) } catch { return }
44
+ for (const e of entries.sort()) {
45
+ if (e.startsWith('.') || SKIP_DIRS.has(e)) continue
46
+ const p = join(dir, e)
47
+ let st
48
+ try { st = statSync(p) } catch { continue }
49
+ if (st.isDirectory()) { yield* walk(p); continue }
50
+ const ext = e.includes('.') ? '.' + e.split('.').pop() : ''
51
+ if (SCAN_EXT.has(ext)) yield p
52
+ }
53
+ }
54
+
55
+ function main() {
56
+ const { plugins, names } = computeCanonicalToolNames()
57
+ const maxyPlugins = new Set(plugins)
58
+ const canonical = new Set(names)
59
+
60
+ // Check 1 — regenerate-and-diff both generated outputs.
61
+ let driftError = null
62
+ const staleOutputs = []
63
+ if ((existsSync(GENERATED) ? readFileSync(GENERATED, 'utf8') : '') !== renderArtifact()) staleOutputs.push(GENERATED)
64
+ if ((existsSync(PLUGINS_TXT) ? readFileSync(PLUGINS_TXT, 'utf8') : '') !== renderPluginsList()) staleOutputs.push(PLUGINS_TXT)
65
+ if (staleOutputs.length > 0) {
66
+ driftError =
67
+ `[gate] canonical-tool-names: GENERATED ARTIFACT STALE.\n` +
68
+ staleOutputs.map((p) => ` ${p}`).join('\n') + '\n' +
69
+ ` Regenerate: cd platform && npm run gen:canonical-tools`
70
+ }
71
+
72
+ // Check 2 — instruction-name validation.
73
+ const violations = []
74
+ for (const root of SCAN_DIRS) {
75
+ for (const file of walk(root)) {
76
+ const text = readFileSync(file, 'utf8')
77
+ for (const m of text.matchAll(TOOL_RE)) {
78
+ const seg = m[1]
79
+ const full = m[0]
80
+ // Skip glob patterns in prose (`mcp__plugin_x_x__*`): the `*` is not a
81
+ // tool char so the match captured a partial name, not a real reference.
82
+ if (text[m.index + full.length] === '*') continue
83
+ if (seg.startsWith('plugin_')) {
84
+ // Long prefix mcp__plugin_<plugin>_<server>__<tool>.
85
+ const inner = seg.slice('plugin_'.length)
86
+ // plugin === server for maxy; the long prefix is `<plugin>_<plugin>`.
87
+ const plugin = [...maxyPlugins].find((p) => inner === `${p}_${p}`)
88
+ if (!plugin) continue // non-maxy bridge (playwright) — upstream-owned.
89
+ if (!canonical.has(full)) {
90
+ violations.push({ file, name: full, why: 'unknown maxy tool (typo or stale)' })
91
+ }
92
+ } else if (maxyPlugins.has(seg)) {
93
+ // Short prefix mcp__<maxyplugin>__<tool> — must be long now.
94
+ violations.push({ file, name: full, why: `short prefix for maxy plugin '${seg}' — use canonical long name` })
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ const failed = driftError || violations.length > 0
101
+ if (driftError) console.error(driftError)
102
+ if (violations.length > 0) {
103
+ console.error('[gate] canonical-tool-names: instruction files name tools outside the live set:')
104
+ for (const v of violations) console.error(` ${v.file.replace(MAXY_CODE_ROOT + '/', '')}: ${v.name} — ${v.why}`)
105
+ }
106
+ if (failed) process.exit(1)
107
+ console.log(`[gate] check-canonical-tool-names: PASS (${names.length} canonical names, scanned instruction files)`)
108
+ }
109
+
110
+ main()
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env node
2
+ // Build-time gate: rejects raw JS numbers bound into Cypher integer positions.
3
+ //
4
+ // The neo4j-driver serializes a plain JS `number` as a Cypher FLOAT (15 -> 15.0).
5
+ // Cypher `LIMIT`/`SKIP` reject non-integers: "Invalid input. '15.0' is not a
6
+ // valid value. Must be a non-negative integer." A `LIMIT $p` / `SKIP $p` bound a
7
+ // raw number therefore THROWS on every call — and the throw is usually swallowed
8
+ // by a tool's catch and returned as empty data, so it presents as "no results"
9
+ // rather than an error. The fix at every call site is `neo4j.int(p)` (or the
10
+ // package's `int(...)` alias). Live repro: writer-craft voice-retrieve returned an
11
+ // empty voice profile in production because `LIMIT $k` was bound k=15.
12
+ //
13
+ // Posture: TARGETED, FAIL-OPEN. The gate fails the build only on a DEFINITE
14
+ // unwrapped binding — a `LIMIT $p`/`SKIP $p` param bound as object shorthand
15
+ // (`{ p }`) or as `p: <expr>` where <expr> contains no `int(`. When the params
16
+ // object cannot be statically resolved (built across multiple statements, spread
17
+ // from `...rest`, or passed through a helper), the site is skipped rather than
18
+ // flagged — zero false positives is the priority for a gate that must stay
19
+ // trusted. A novel unparseable offender would still surface at runtime via the
20
+ // existing per-tool error logging.
21
+ //
22
+ // Detection model:
23
+ // 1. Tokenize once into two newline-preserving masked views: comments blanked
24
+ // in both; string/template CONTENTS kept in `keepStr` (so `LIMIT $p` inside a
25
+ // query template is visible) and blanked in `noStr` (so braces/parens/commas
26
+ // inside queries and string values never confuse structural scanning).
27
+ // 2. Collect integer-position param names from `keepStr`: `LIMIT $p` / `SKIP $p`.
28
+ // 3. For each `.run(` call, resolve its params argument from `noStr` — an inline
29
+ // `{...}` object, or a bare identifier traced back to its `const/let/var X = {`
30
+ // object literal in the same file.
31
+ // 4. Within that object, flag each integer-position param bound shorthand or
32
+ // without an `int(` wrap.
33
+
34
+ import { readFileSync, readdirSync, statSync } from 'node:fs'
35
+ import { dirname, join, relative, basename } from 'node:path'
36
+ import { fileURLToPath } from 'node:url'
37
+
38
+ const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url))
39
+ const REPO_ROOT = join(SCRIPT_DIR, '..', '..')
40
+
41
+ const SCAN_ROOTS = [
42
+ join(REPO_ROOT, 'premium-plugins'),
43
+ join(REPO_ROOT, 'platform', 'plugins'),
44
+ join(REPO_ROOT, 'platform', 'ui', 'server'),
45
+ join(REPO_ROOT, 'platform', 'ui', 'app'),
46
+ ]
47
+
48
+ const ALLOWED_EXTS = new Set(['.ts', '.tsx'])
49
+ const SKIP_DIR_NAMES = new Set([
50
+ 'node_modules', 'dist', 'build', '.next', 'payload', '__tests__',
51
+ ])
52
+
53
+ function shouldScanFile(filePath) {
54
+ const name = basename(filePath)
55
+ if (name.endsWith('.test.ts') || name.endsWith('.test.tsx')) return false
56
+ const ext = name.includes('.') ? '.' + name.split('.').pop() : ''
57
+ return ALLOWED_EXTS.has(ext)
58
+ }
59
+
60
+ function* walk(root) {
61
+ let entries
62
+ try { entries = readdirSync(root) } catch { return }
63
+ for (const name of entries) {
64
+ if (SKIP_DIR_NAMES.has(name)) continue
65
+ const full = join(root, name)
66
+ let st
67
+ try { st = statSync(full) } catch { continue }
68
+ if (st.isDirectory()) yield* walk(full)
69
+ else if (st.isFile()) yield full
70
+ }
71
+ }
72
+
73
+ // Single tokenizer pass. Returns two strings of identical length to `src`, with
74
+ // newlines preserved so character offsets map back to line numbers:
75
+ // keepStr — comment characters replaced by spaces; string/template contents kept.
76
+ // noStr — comment characters AND string/template contents replaced by spaces.
77
+ // Delimiters (quotes, backticks, comment markers) become spaces in both.
78
+ function mask(src) {
79
+ const keep = new Array(src.length)
80
+ const no = new Array(src.length)
81
+ // states: code, line (//), block (/* */), sq ('), dq ("), tpl (`)
82
+ let state = 'code'
83
+ for (let i = 0; i < src.length; i++) {
84
+ const c = src[i]
85
+ const n = src[i + 1]
86
+ const blank = c === '\n' ? '\n' : ' '
87
+ if (state === 'code') {
88
+ if (c === '/' && n === '/') { keep[i] = no[i] = blank; state = 'line'; continue }
89
+ if (c === '/' && n === '*') { keep[i] = no[i] = blank; state = 'block'; continue }
90
+ if (c === "'") { keep[i] = no[i] = blank; state = 'sq'; continue }
91
+ if (c === '"') { keep[i] = no[i] = blank; state = 'dq'; continue }
92
+ if (c === '`') { keep[i] = no[i] = blank; state = 'tpl'; continue }
93
+ keep[i] = no[i] = c
94
+ continue
95
+ }
96
+ if (state === 'line') {
97
+ keep[i] = no[i] = blank
98
+ if (c === '\n') state = 'code'
99
+ continue
100
+ }
101
+ if (state === 'block') {
102
+ keep[i] = no[i] = blank
103
+ if (c === '*' && n === '/') {
104
+ // blank the closing slash too on the next iteration
105
+ no[i + 1] = keep[i + 1] = ' '
106
+ i++
107
+ state = 'code'
108
+ }
109
+ continue
110
+ }
111
+ // inside a string/template: keep content in `keep`, blank it in `no`.
112
+ if (state === 'sq' || state === 'dq' || state === 'tpl') {
113
+ const closer = state === 'sq' ? "'" : state === 'dq' ? '"' : '`'
114
+ if (c === '\\') {
115
+ // escape: consume this and the next char as string content
116
+ keep[i] = c === '\n' ? '\n' : (c)
117
+ no[i] = blank
118
+ if (i + 1 < src.length) {
119
+ keep[i + 1] = src[i + 1] === '\n' ? '\n' : src[i + 1]
120
+ no[i + 1] = src[i + 1] === '\n' ? '\n' : ' '
121
+ i++
122
+ }
123
+ continue
124
+ }
125
+ if (c === closer) { keep[i] = no[i] = blank; state = 'code'; continue }
126
+ keep[i] = c
127
+ no[i] = blank
128
+ continue
129
+ }
130
+ }
131
+ return { keep: keep.join(''), no: no.join('') }
132
+ }
133
+
134
+ function lineAt(text, index) {
135
+ let line = 1
136
+ for (let i = 0; i < index && i < text.length; i++) if (text[i] === '\n') line++
137
+ return line
138
+ }
139
+
140
+ const IDENT_RE = /^[A-Za-z_$][\w$]*$/
141
+
142
+ // Capture a balanced {...} starting at `noStr[start]` (which must be '{').
143
+ // Returns [objTextInNoStr, endIndexExclusive] or null.
144
+ function captureBraces(noStr, start) {
145
+ if (noStr[start] !== '{') return null
146
+ let depth = 0
147
+ for (let i = start; i < noStr.length; i++) {
148
+ if (noStr[i] === '{') depth++
149
+ else if (noStr[i] === '}') { depth--; if (depth === 0) return [noStr.slice(start, i + 1), i + 1] }
150
+ }
151
+ return null
152
+ }
153
+
154
+ // Capture a balanced (...) starting at `noStr[start]` (which must be '(').
155
+ // Returns [argsTextInNoStr, endIndexExclusive] or null.
156
+ function captureParens(noStr, start) {
157
+ if (noStr[start] !== '(') return null
158
+ let depth = 0
159
+ for (let i = start; i < noStr.length; i++) {
160
+ if (noStr[i] === '(') depth++
161
+ else if (noStr[i] === ')') { depth--; if (depth === 0) return [noStr.slice(start, i + 1), i + 1] }
162
+ }
163
+ return null
164
+ }
165
+
166
+ // Split args text (without the outer parens) on TOP-LEVEL commas only.
167
+ function splitTopLevelArgs(argsInner) {
168
+ const parts = []
169
+ let depth = 0
170
+ let cur = ''
171
+ for (const ch of argsInner) {
172
+ if (ch === '(' || ch === '{' || ch === '[') depth++
173
+ else if (ch === ')' || ch === '}' || ch === ']') depth--
174
+ if (ch === ',' && depth === 0) { parts.push(cur); cur = ''; continue }
175
+ cur += ch
176
+ }
177
+ if (cur.trim() !== '') parts.push(cur)
178
+ return parts
179
+ }
180
+
181
+ // Given a params object's text (from noStr) and the set of integer-position
182
+ // param names, return the names that are bound shorthand or without an int() wrap.
183
+ function findUnwrapped(objText, intParams) {
184
+ const offenders = []
185
+ for (const name of intParams) {
186
+ const esc = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
187
+ // Explicit binding `name : value`
188
+ const explicit = new RegExp(`\\b${esc}\\s*:\\s*([^,}]+)`)
189
+ const m = objText.match(explicit)
190
+ if (m) {
191
+ if (!/\bint\s*\(/.test(m[1])) offenders.push(name)
192
+ continue
193
+ }
194
+ // Shorthand `{ name }` / `, name ,` / `, name }` — bare identifier, never wrapped.
195
+ const shorthand = new RegExp(`[{,]\\s*${esc}\\s*[,}]`)
196
+ if (shorthand.test(objText)) offenders.push(name)
197
+ }
198
+ return offenders
199
+ }
200
+
201
+ const violations = []
202
+
203
+ for (const root of SCAN_ROOTS) {
204
+ for (const file of walk(root)) {
205
+ if (!shouldScanFile(file)) continue
206
+ let src
207
+ try { src = readFileSync(file, 'utf-8') } catch { continue }
208
+ if (!/\.run\s*\(/.test(src)) continue
209
+
210
+ const { keep, no } = mask(src)
211
+
212
+ // 1. Integer-position param names from the query strings (kept in `keep`).
213
+ const intParams = new Set()
214
+ for (const mm of keep.matchAll(/\b(?:LIMIT|SKIP)\s+\$(\w+)/gi)) intParams.add(mm[1])
215
+ if (intParams.size === 0) continue
216
+
217
+ // 2. Walk each `.run(` call.
218
+ for (const rm of no.matchAll(/\.run\s*\(/g)) {
219
+ const parenStart = rm.index + rm[0].length - 1
220
+ const cap = captureParens(no, parenStart)
221
+ if (!cap) continue
222
+ const inner = cap[0].slice(1, -1)
223
+ const args = splitTopLevelArgs(inner)
224
+ if (args.length < 2) continue
225
+ const paramArg = args[1].trim()
226
+
227
+ let objText = null
228
+ if (paramArg.startsWith('{')) {
229
+ // Inline object literal: re-capture against `no` for balanced braces.
230
+ const objStart = no.indexOf('{', parenStart)
231
+ const objCap = objStart >= 0 ? captureBraces(no, objStart) : null
232
+ if (objCap) objText = objCap[0]
233
+ } else if (IDENT_RE.test(paramArg)) {
234
+ // Single-variable params object: trace back to its declaration.
235
+ const esc = paramArg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
236
+ const declRe = new RegExp(`(?:const|let|var)\\s+${esc}\\b[^=]*=\\s*\\{`)
237
+ const dm = no.match(declRe)
238
+ if (dm) {
239
+ const braceIdx = no.indexOf('{', dm.index)
240
+ const objCap = braceIdx >= 0 ? captureBraces(no, braceIdx) : null
241
+ if (objCap) objText = objCap[0]
242
+ }
243
+ }
244
+ // Unresolved params shape -> fail open (skip).
245
+ if (!objText) continue
246
+
247
+ for (const name of findUnwrapped(objText, intParams)) {
248
+ violations.push({
249
+ file: relative(REPO_ROOT, file),
250
+ line: lineAt(no, parenStart),
251
+ name,
252
+ })
253
+ }
254
+ }
255
+ }
256
+ }
257
+
258
+ if (violations.length > 0) {
259
+ // De-duplicate identical (file, line, name) tuples.
260
+ const seen = new Set()
261
+ const unique = violations.filter((v) => {
262
+ const k = `${v.file}:${v.line}:${v.name}`
263
+ if (seen.has(k)) return false
264
+ seen.add(k)
265
+ return true
266
+ })
267
+ console.error(`check-cypher-int-params: ${unique.length} raw-number Cypher integer binding(s):`)
268
+ for (const v of unique) {
269
+ console.error(` ${v.file}:${v.line}: LIMIT/SKIP $${v.name} is bound a raw JS number — wrap with int(${v.name})`)
270
+ }
271
+ console.error('')
272
+ console.error('neo4j-driver serializes a JS number as a Cypher float (15 -> 15.0); LIMIT/SKIP')
273
+ console.error('reject non-integers. Bind neo4j.int(p) (or the package int() alias) instead.')
274
+ process.exit(1)
275
+ }
276
+
277
+ console.log('check-cypher-int-params: ok (0 raw-number integer-position bindings)')
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ // Generator: writes the committed canonical maxy MCP tool-name artifact from
3
+ // every platform/plugins/<dir>/PLUGIN.md. Run via `npm run gen:canonical-tools`
4
+ // (platform). The strong build gate (check-canonical-tool-names.mjs) regenerates
5
+ // in-memory and fails the build if the committed artifact has drifted.
6
+ import { writeFileSync } from 'node:fs'
7
+ import { join } from 'node:path'
8
+ import { computeCanonicalToolNames, MAXY_CODE_ROOT } from './lib/canonical-tool-names.mjs'
9
+
10
+ const OUT = join(
11
+ MAXY_CODE_ROOT, 'platform', 'services', 'claude-session-manager', 'src',
12
+ 'canonical-tool-names.generated.ts',
13
+ )
14
+
15
+ // Plain newline-delimited maxy plugin list the deterministic missing-tool hook
16
+ // (bash) reads to tell a maxy server (mcp__plugin_<p>_<p>__) from a third-party
17
+ // bridge that shares the same long-prefix shape (Playwright etc.).
18
+ const PLUGINS_TXT = join(
19
+ MAXY_CODE_ROOT, 'platform', 'plugins', 'admin', 'hooks', 'lib',
20
+ 'maxy-mcp-plugins.txt',
21
+ )
22
+
23
+ export function renderPluginsList() {
24
+ const { plugins } = computeCanonicalToolNames()
25
+ return plugins.join('\n') + '\n'
26
+ }
27
+
28
+ export function renderArtifact() {
29
+ const { plugins, names } = computeCanonicalToolNames()
30
+ const body = [
31
+ '// GENERATED — do not edit by hand.',
32
+ '// Source: platform/plugins/*/PLUGIN.md (tools: + hidden: blocks).',
33
+ '// Regenerate: cd platform && npm run gen:canonical-tools',
34
+ '// Gate: platform/scripts/check-canonical-tool-names.mjs fails the build on drift.',
35
+ '//',
36
+ '// Canonical long-prefix names every maxy MCP tool is served under by the',
37
+ '// installed-plugin channel: mcp__plugin_<dir>_<dir>__<tool>.',
38
+ '',
39
+ '/** Directory basenames of maxy platform plugins that expose a tool surface. */',
40
+ 'export const MAXY_MCP_PLUGINS: readonly string[] = [',
41
+ ...plugins.map((p) => ` ${JSON.stringify(p)},`),
42
+ ']',
43
+ '',
44
+ '/** Every canonical maxy MCP tool name (long prefix), sorted. */',
45
+ 'export const CANONICAL_MAXY_TOOL_NAMES: readonly string[] = [',
46
+ ...names.map((n) => ` ${JSON.stringify(n)},`),
47
+ ']',
48
+ '',
49
+ ].join('\n')
50
+ return body
51
+ }
52
+
53
+ function main() {
54
+ writeFileSync(OUT, renderArtifact(), 'utf8')
55
+ writeFileSync(PLUGINS_TXT, renderPluginsList(), 'utf8')
56
+ const { plugins, names } = computeCanonicalToolNames()
57
+ console.log(`[gen] canonical-tool-names: ${plugins.length} plugins, ${names.length} tools -> ${OUT}`)
58
+ console.log(`[gen] maxy-mcp-plugins.txt -> ${PLUGINS_TXT}`)
59
+ }
60
+
61
+ export { OUT, PLUGINS_TXT }
62
+
63
+ if (import.meta.url === `file://${process.argv[1]}`) main()