@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.
- package/package.json +1 -1
- package/payload/platform/lib/graph-mcp/dist/index.js +4 -3
- package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-mcp/src/index.ts +4 -3
- package/payload/platform/package.json +1 -0
- package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
- package/payload/platform/plugins/admin/PLUGIN.md +1 -0
- package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +16 -0
- package/payload/platform/plugins/admin/hooks/mcp-tool-missing.sh +94 -0
- package/payload/platform/plugins/admin/hooks/turn-completed-graph-write.sh +7 -0
- package/payload/platform/plugins/admin/skills/a4-print-documents/SKILL.md +14 -14
- package/payload/platform/plugins/admin/skills/deck-pages/SKILL.md +9 -23
- package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/publish-site/SKILL.md +3 -3
- package/payload/platform/plugins/admin/skills/skill-builder/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/skill-builder/references/pdf-generation.md +4 -4
- package/payload/platform/plugins/admin/skills/stream-log-review/references/analysis-patterns.md +11 -7
- package/payload/platform/plugins/browser/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/browser/PLUGIN.md +78 -8
- package/payload/platform/plugins/browser/mcp/dist/index.js +111 -7
- package/payload/platform/plugins/browser/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.d.ts +98 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.d.ts.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.js +455 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-actions.js.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.d.ts +9 -31
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.d.ts.map +1 -1
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.js +6 -165
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-render.js.map +1 -1
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.d.ts +153 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.d.ts.map +1 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.js +401 -0
- package/payload/platform/plugins/browser/mcp/dist/lib/cdp-session.js.map +1 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +3 -4
- package/payload/platform/plugins/docs/references/deployment.md +1 -1
- package/payload/platform/plugins/docs/references/internals.md +6 -4
- package/payload/platform/plugins/docs/references/plugins-guide.md +2 -2
- package/payload/platform/plugins/email/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/email/PLUGIN.md +6 -2
- package/payload/platform/plugins/email/mcp/dist/index.js +38 -0
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/providers.d.ts +28 -0
- package/payload/platform/plugins/email/mcp/dist/lib/providers.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/providers.js +106 -0
- package/payload/platform/plugins/email/mcp/dist/lib/providers.js.map +1 -1
- package/payload/platform/plugins/email/references/email-reference.md +19 -15
- package/payload/platform/plugins/graph/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/graph/PLUGIN.md +37 -0
- package/payload/platform/plugins/memory/PLUGIN.md +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.js +3 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-brain-capture-recent.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js +2 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-compiled-truth-history.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.js +2 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-review-queue.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js +3 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js.map +1 -1
- package/payload/platform/plugins/memory/references/graph-primitives.md +36 -27
- package/payload/platform/plugins/obsidian-import/PLUGIN.md +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js.map +1 -1
- package/payload/platform/scripts/check-canonical-tool-names.mjs +110 -0
- package/payload/platform/scripts/check-cypher-int-params.mjs +277 -0
- package/payload/platform/scripts/generate-canonical-tool-names.mjs +63 -0
- package/payload/platform/scripts/lib/canonical-tool-names.mjs +88 -0
- package/payload/platform/scripts/setup-account.sh +6 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.d.ts +7 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +204 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.js +23 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/index.js +30 -6
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/mcp-tools-probe.d.ts +6 -4
- package/payload/platform/services/claude-session-manager/dist/mcp-tools-probe.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/mcp-tools-probe.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +50 -26
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +40 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +200 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.js +50 -34
- package/payload/platform/services/claude-session-manager/dist/specialist-drift.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/tool-surface.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/tool-surface.js +23 -13
- package/payload/platform/services/claude-session-manager/dist/tool-surface.js.map +1 -1
- package/payload/platform/templates/specialists/agents/content-producer.md +4 -4
- package/payload/platform/templates/specialists/agents/librarian.md +2 -2
- package/payload/platform/templates/specialists/agents/personal-assistant.md +6 -2
- package/payload/platform/templates/specialists/agents/project-manager.md +1 -1
- package/payload/platform/templates/specialists/agents/research-assistant.md +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.d.ts.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js +33 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-retrieve-conditioning.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-retrieve-conditioning.ts +5 -2
- package/payload/server/{chunk-L2YK2VK3.js → chunk-WWD4TCJJ.js} +3 -17
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/Checkbox-ChWmQ7if.js +1 -0
- package/payload/server/public/assets/admin-BLi_fAKQ.js +216 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DZoOzmjE.js → architectureDiagram-Q4EWVU46-DSqzB21V.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-DG3yufCR.js → blockDiagram-DXYQGD6D--otTO_UB.js} +1 -1
- package/payload/server/public/assets/brand-Djtb-tLQ.js +9 -0
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-BBx5wXuZ.js → c4Diagram-AHTNJAMY-CBszqa9W.js} +1 -1
- package/payload/server/public/assets/channel-C-SJYogg.js +1 -0
- package/payload/server/public/assets/{chunk-336JU56O-DwP7g5ns.js → chunk-336JU56O-ClAJP2KY.js} +2 -2
- package/payload/server/public/assets/{chunk-426QAEUC-DXzGdc22.js → chunk-426QAEUC-CgLucS4H.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-1LDRjk0E.js → chunk-4TB4RGXK-BvOX6mSk.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-BHjWhwhn.js → chunk-5FUZZQ4R-uyN4XtrW.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-CwPHRVN8.js → chunk-5PVQY5BW-B6xHEzh2.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-7443hrYZ.js → chunk-EDXVE4YY-9objw8tW.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-BTnmmnmX.js → chunk-ENJZ2VHE-pQpbHIJL.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-CPnMJplL.js → chunk-ICPOFSXX-qS23jC3x.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-DCQ5Q4RS.js → chunk-OYMX7WX6-C9YUwGqp.js} +1 -1
- package/payload/server/public/assets/{chunk-U2HBQHQK-BmfZG8e4.js → chunk-U2HBQHQK-D_pYedRJ.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-Da2yJL2u.js → chunk-X2U36JSP-nxf6GHD-.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-BdUmMUJ6.js → chunk-YZCP3GAM-DqKT124V.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-abkGirrZ.js → chunk-ZZ45TVLE-CLwvpbvu.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-OBkxl5to.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-CkTyus-1.js +1 -0
- package/payload/server/public/assets/clone-VufdPzJ-.js +1 -0
- package/payload/server/public/assets/{dagre-BVVuAEsU.js → dagre-COCXt23n.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-VG2kNj19.js → dagre-KV5264BT-DmuVMrt4.js} +1 -1
- package/payload/server/public/assets/data-TlJbMIfa.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-DRiVy69K.js → diagram-5BDNPKRD-DOiLSgj_.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-DtpiRTYX.js → diagram-G4DWMVQ6-quY6PSZQ.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-Da3t99fY.js → diagram-MMDJMWI5-tN2yiz0C.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-BzQmLTJQ.js → diagram-TYMM5635-CjrRQNQ0.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-D3Ll-GDw.js → erDiagram-SMLLAGMA-CrNNMxkA.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-D17bje6d.js → flowDiagram-DWJPFMVM-CCUsJph7.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-BjeQ-Bun.js → ganttDiagram-T4ZO3ILL-18276bri.js} +1 -1
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-LtX4pvZS.js → gitGraphDiagram-UUTBAWPF-CsLsKMjN.js} +1 -1
- package/payload/server/public/assets/graph-D2Yvni5R.js +1 -0
- package/payload/server/public/assets/graph-labels-uksnCgSC.js +1 -0
- package/payload/server/public/assets/{graphlib-CzwsfZOw.js → graphlib-CmtUzIni.js} +1 -1
- package/payload/server/public/assets/{infoDiagram-42DDH7IO-DOgH4i52.js → infoDiagram-42DDH7IO-Dny_ZNz3.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-DYqy5BFj.js → ishikawaDiagram-UXIWVN3A-kUhIUQE-.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-CsFpIULJ.js → journeyDiagram-VCZTEJTY-DekMvAKB.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-5uZuArw7.js → kanban-definition-6JOO6SKY-st0HNZZo.js} +1 -1
- package/payload/server/public/assets/lib-Ca7uCvl9.js +33 -0
- package/payload/server/public/assets/{line-BY8PmCSW.js → line-BaNk-I7J.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-CJ_9xqwK.js → mermaid-parser.core-CRCHYQ_F.js} +1 -1
- package/payload/server/public/assets/{mermaid.core-CXznBkX7.js → mermaid.core-BaIYEciF.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-DZbHBud5.js → mindmap-definition-QFDTVHPH-D4u4KU2v.js} +1 -1
- package/payload/server/public/assets/{page-CfN7nw1J.js → page-DayPduMN.js} +2 -2
- package/payload/server/public/assets/page-QQmuB04w.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-QmOi4DRG.js → pieDiagram-DEJITSTG-BItWEilY.js} +1 -1
- package/payload/server/public/assets/public-Dze4yArS.js +7 -0
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-Cpidq6Xj.js → quadrantDiagram-34T5L4WZ-CeWDBvmH.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-CBqtfbzR.js → requirementDiagram-MS252O5E-CcosjedW.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-4BiY4dV9.js → sankeyDiagram-XADWPNL6-DNGeFpUj.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-DVlL-l2u.js → sequenceDiagram-FGHM5R23-Cqy_deB6.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-CAm9dW68.js → stateDiagram-FHFEXIEX-2yoAqEsA.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-Dc5siYvu.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CD-tZc4y.js → timeline-definition-GMOUNBTQ-Dmeghpw1.js} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-Davcm0TF.js → vennDiagram-DHZGUBPP-TX5D-Zzp.js} +1 -1
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-40LBYw_Y.js → wardleyDiagram-NUSXRM2D-B7mcpl5E.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DEPT-__v.js → xychartDiagram-5P7HB3ND-DH77CxKf.js} +1 -1
- package/payload/server/public/brand/Archive/favicon.ico +0 -0
- package/payload/server/public/brand/Archive/maxy-black.png +0 -0
- package/payload/server/public/brand/Archive/maxy-horizontal.png +0 -0
- package/payload/server/public/brand/Archive/maxy-monochrome.png +0 -0
- package/payload/server/public/brand/Archive/maxy-square.png +0 -0
- package/payload/server/public/brand/Archive/maxy.png +0 -0
- package/payload/server/public/brand/Archive/star.png +0 -0
- package/payload/server/public/brand/Archive.zip +0 -0
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +8 -8
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +312 -793
- package/payload/server/public/assets/ChatInput-CvRaT05l.js +0 -13
- package/payload/server/public/assets/Checkbox-BJKNkUYu.js +0 -1
- package/payload/server/public/assets/admin-BNy4N2d6.js +0 -217
- package/payload/server/public/assets/channel-DdtUtt5g.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-CKk2DFZe.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E--YvfDQCh.js +0 -1
- package/payload/server/public/assets/clone-Cw6UfPGM.js +0 -1
- package/payload/server/public/assets/data-uBpwaldK.js +0 -1
- package/payload/server/public/assets/graph-labels-Cn9jAE6I.js +0 -1
- package/payload/server/public/assets/graph-tvTPvBaz.js +0 -1
- package/payload/server/public/assets/lib-p4ylk2XS.js +0 -29
- package/payload/server/public/assets/page-B3VYaBcf.js +0 -1
- package/payload/server/public/assets/public-Bbew78md.js +0 -7
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B2EzXi50.js +0 -1
- /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;
|
package/payload/platform/plugins/memory/mcp/dist/tools/session-retrospective-skip-rate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-retrospective-skip-rate.js","sourceRoot":"","sources":["../../src/tools/session-retrospective-skip-rate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;
|
|
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
|
-
|
|
44
|
-
`
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
55
|
-
write goes through the schema-aware memory tools), do not
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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. `
|
|
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" | `
|
|
82
|
-
| "delete this specific node", "remove this entry by its id", "get rid of node X", "remove orphans", "prune", "clean up" | `
|
|
83
|
-
| "undo that delete", "I deleted X by mistake, bring it back", "restore the contact I just trashed" | `
|
|
84
|
-
| "empty the trash", "purge old soft-deletes", "what's been sitting in trash and ready to clear" | `
|
|
85
|
-
| "scrub this conversation's memory", "remove this claim the bot picked up", "forget what we said in that thread" | `
|
|
86
|
-
| "update a property", "fix a typo in a node", "change the role field on X", "null this poisonous string" | `
|
|
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" | `
|
|
88
|
-
| "don't surface these substrings in future memory results", "watch for this string" | `
|
|
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
|
-
`
|
|
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 `
|
|
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":"AAAA,OAAO,KAAoC,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,IAAI,MAAM,GAAkB,IAAI,CAAC;AAEjC,SAAS,YAAY;IACnB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAElE,MAAM,YAAY,GAAG,OAAO,CAC1B,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,EACxE,wBAAwB,CACzB,CAAC;IAEF,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,yCAAyC,YAAY,gCAAgC,CACtF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,UAAU;IACjB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,6DAA6D;QAC7D,oEAAoE;QACpE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QAClC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CACb,2FAA2F,CAC5F,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC;QAC/C,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,iDAAiD,GAAG,EAAE,CAAC,CAAC;QACtE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;AAC1B,CAAC;AAiBD,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAqB;IAErB,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,GAAG,EAAE,EAAE,GAAG,MAAM,CAAC;IAE1D,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,CAAC,0BAA0B,CAAC,CAAC;QAChD,MAAM,WAAW,GAA4B,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"message-history.js","sourceRoot":"","sources":["../../src/tools/message-history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,IAAI,MAAM,GAAkB,IAAI,CAAC;AAEjC,SAAS,YAAY;IACnB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAElE,MAAM,YAAY,GAAG,OAAO,CAC1B,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,EACxE,wBAAwB,CACzB,CAAC;IAEF,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,yCAAyC,YAAY,gCAAgC,CACtF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,UAAU;IACjB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,6DAA6D;QAC7D,oEAAoE;QACpE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QAClC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CACb,2FAA2F,CAC5F,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC;QAC/C,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,iDAAiD,GAAG,EAAE,CAAC,CAAC;QACtE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;AAC1B,CAAC;AAiBD,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAqB;IAErB,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,GAAG,EAAE,EAAE,GAAG,MAAM,CAAC;IAE1D,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,CAAC,0BAA0B,CAAC,CAAC;QAChD,MAAM,WAAW,GAA4B,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAEpF,IAAI,OAAO,EAAE,CAAC;YACZ,UAAU,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;YACxC,WAAW,CAAC,OAAO,GAAG,OAAO,CAAC;QAChC,CAAC;QACD,IAAI,MAAM,EAAE,CAAC;YACX,UAAU,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YACtC,WAAW,CAAC,MAAM,GAAG,MAAM,CAAC;QAC9B,CAAC;QAED,MAAM,KAAK,GAAG;;cAEJ,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;;;;KAIjC,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAErD,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;YACnC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC;YACrC,OAAO;gBACL,OAAO,EAAE,CAAC,CAAC,OAAiB;gBAC5B,MAAM,EAAE,CAAC,CAAC,MAAgB;gBAC1B,IAAI,EAAE,CAAC,CAAC,IAAc;gBACtB,OAAO,EAAE,CAAC,CAAC,OAAiB;gBAC5B,SAAS,EAAE,CAAC,CAAC,QAAkB;aAChC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
|
|
@@ -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()
|