@rubytech/create-maxy-code 0.1.255 → 0.1.257
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/dist/__tests__/plugin-install.test.js +58 -40
- package/dist/index.js +77 -26
- package/dist/lib/plugin-install.js +31 -29
- package/package.json +1 -1
- package/payload/platform/config/brand-registry.json +8 -0
- package/payload/platform/config/brand.json +3 -3
- package/payload/platform/lib/graph-search/src/__tests__/fulltext-coverage.test.ts +12 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +2 -0
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/index.ts +2 -0
- package/payload/platform/neo4j/schema.cypher +126 -0
- package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +3 -6
- package/payload/platform/plugins/admin/mcp/dist/index.js +0 -60
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/insight/SKILL.md +24 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +63 -10
- package/payload/platform/plugins/business-assistant/PLUGIN.md +1 -1
- package/payload/platform/plugins/business-assistant/references/document-management.md +1 -1
- package/payload/platform/plugins/business-assistant/references/invoicing.md +2 -2
- package/payload/platform/plugins/business-assistant/references/quoting.md +1 -1
- package/payload/platform/plugins/docs/PLUGIN.md +1 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
- package/payload/platform/plugins/docs/references/deployment.md +18 -5
- package/payload/platform/plugins/docs/references/memory-guide.md +4 -0
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
- package/payload/platform/plugins/docs/references/slides.md +31 -0
- package/payload/platform/plugins/docs/references/voice-mirror-guide.md +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.d.ts +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-typed-edge-pass.js +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +10 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/references/schema-construction.md +72 -0
- package/payload/platform/plugins/slides/.claude-plugin/plugin.json +8 -0
- package/payload/platform/plugins/slides/LICENSE +21 -0
- package/payload/platform/plugins/slides/PLUGIN.md +18 -0
- package/payload/platform/plugins/slides/PROVENANCE.md +40 -0
- package/payload/platform/plugins/slides/commands/add-slide.md +29 -0
- package/payload/platform/plugins/slides/commands/slides-claus.md +39 -0
- package/payload/platform/plugins/slides/commands/slides-new-component.md +39 -0
- package/payload/platform/plugins/slides/commands/slides-outline.md +43 -0
- package/payload/platform/plugins/slides/commands/slides-review.md +52 -0
- package/payload/platform/plugins/slides/commands/slides-theme.md +64 -0
- package/payload/platform/plugins/slides/commands/slides.md +59 -0
- package/payload/platform/plugins/slides/skills/deck-system/REFERENCE.md +581 -0
- package/payload/platform/plugins/slides/skills/deck-system/SKILL.md +607 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-board.md +426 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-claus.md +185 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-mbb.md +450 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-product-launch.md +579 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-sales.md +464 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING-sequoia.md +489 -0
- package/payload/platform/plugins/slides/skills/deck-system/STORYTELLING.md +273 -0
- package/payload/platform/plugins/slides/skills/deck-system/deck-craft.html +1371 -0
- package/payload/platform/plugins/slides/skills/deck-system/deck-solid.html +1667 -0
- package/payload/platform/plugins/slides/skills/deck-system/deck.html +1359 -0
- package/payload/platform/plugins/url-get/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/url-get/PLUGIN.md +26 -21
- package/payload/platform/plugins/url-get/mcp/dist/index.js +3 -3
- package/payload/platform/plugins/url-get/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.d.ts +1 -2
- package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.d.ts.map +1 -1
- package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.js +20 -40
- package/payload/platform/plugins/url-get/mcp/dist/tools/url-get.js.map +1 -1
- package/payload/platform/scripts/setup-account.sh +1 -10
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +0 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +32 -2
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +2 -2
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/templates/specialists/agents/database-operator.md +1 -1
- package/payload/platform/templates/specialists/agents/typed-edge-classifier.md +1 -1
- package/payload/premium-plugins/writer-craft/PLUGIN.md +2 -2
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js +5 -5
- package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.d.ts +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/lib/voice-corpus.js +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.d.ts +4 -8
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.d.ts.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.js +30 -98
- package/payload/premium-plugins/writer-craft/mcp/dist/tools/voice-ingest-session-text.js.map +1 -1
- package/payload/premium-plugins/writer-craft/mcp/scripts/smoke.mjs +37 -114
- package/payload/premium-plugins/writer-craft/mcp/src/index.ts +5 -5
- package/payload/premium-plugins/writer-craft/mcp/src/lib/voice-corpus.ts +1 -1
- package/payload/premium-plugins/writer-craft/mcp/src/tools/voice-ingest-session-text.ts +29 -106
- package/payload/premium-plugins/writer-craft/skills/voice-mirror/SKILL.md +15 -17
- package/payload/server/public/assets/AdminShell-T-YknnBn.js +1 -0
- package/payload/server/public/assets/Checkbox-DmDxpqVv.js +1 -0
- package/payload/server/public/assets/admin-COUV-jgt.js +1 -0
- package/payload/server/public/assets/{arc-aUiRP9AS.js → arc-B2CweJq3.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL-Dnn6Hc65.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-c09loTER.js → architectureDiagram-Q4EWVU46-DP2o-MFV.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-Cjdeyoq1.js → blockDiagram-DXYQGD6D-DO4mcYDJ.js} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-NY6Wlzo2.js → c4Diagram-AHTNJAMY-Sy1giHbj.js} +1 -1
- package/payload/server/public/assets/channel-CEpR_0rE.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-BK3470lx.js → chunk-2KRD3SAO-CKsCYCsN.js} +1 -1
- package/payload/server/public/assets/chunk-336JU56O-C0-P-aUF.js +2 -0
- package/payload/server/public/assets/chunk-426QAEUC-DFjEt3Zb.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-BOvVdJLf.js → chunk-4BX2VUAB-B8bqAmBa.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-BXpto3yW.js → chunk-4TB4RGXK-D1k0VSlW.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-BwZyF7vR.js → chunk-55IACEB6-B-p_QNqz.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-C403gCUk.js → chunk-5FUZZQ4R-D6U6tV_j.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-CjVzXQEp.js → chunk-5PVQY5BW-CYK76xfs.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-D5bhMrtY.js → chunk-67CJDMHE-BC9js-lf.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-Si7Lgrwc.js → chunk-7N4EOEYR-4j2OqKkv.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-DMuHtDqO.js → chunk-AA7GKIK3-Coen-fXN.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-L79XKVcb.js → chunk-BSJP7CBP-CAiOBvec.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-C0O7Upmg.js → chunk-CIAEETIT-AJzzpZVb.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-DJcJAsAg.js → chunk-EDXVE4YY-BL4BKozX.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-CFDNvYu1.js → chunk-ENJZ2VHE-mhAFG8UD.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-C_E43NFJ.js → chunk-FMBD7UC4-H231gZA_.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-D9lWWHAu.js → chunk-FOC6F5B3-Cl3ZZjYG.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-ecLOxGhL.js → chunk-ICPOFSXX-DOEzvzJa.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-DuhsNH4c.js → chunk-K5T4RW27-C_ipbUDD.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-B4-A1Abi.js → chunk-KGLVRYIC-CTsDNSCU.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-BxqgHRgT.js → chunk-LIHQZDEY-DvSXhkGf.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-DEYQ5WaJ.js → chunk-ORNJ4GCN-p574NOI7.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-B7MW66KB.js → chunk-OYMX7WX6-BlEgFM6U.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-DpF06ZZQ.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-BMawmsyk.js → chunk-U2HBQHQK-B2bDK0jv.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-CT6g7pno.js → chunk-X2U36JSP-D69BxKFw.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-CBfZXZDB.js → chunk-XPW4576I-Dm-PcyUi.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-xeAluiAH.js → chunk-YZCP3GAM-Be8RnXgx.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-BRN9qUC5.js → chunk-ZZ45TVLE-Ck8PCTa4.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-CYbXvKLI.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-DEyHzRhq.js +1 -0
- package/payload/server/public/assets/clone-y8gexbBy.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-Br2gjtEO.js → cose-bilkent-S5V4N54A-CmkW2Eaj.js} +1 -1
- package/payload/server/public/assets/{dagre-DTjePoco.js → dagre-Dqp-ns8F.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-DHBkRke4.js → dagre-KV5264BT-ZgWWXPLc.js} +1 -1
- package/payload/server/public/assets/data-gy6QH9c1.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-BIq1-idL.js → diagram-5BDNPKRD-CTX5-ScM.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-BsIUDzV6.js → diagram-G4DWMVQ6-BovIsO6H.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-CgHSri2i.js → diagram-MMDJMWI5-DcETsQy-.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-Ce2Wh9ZX.js → diagram-TYMM5635-yyq6peoZ.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-BU0Kh6OQ.js → erDiagram-SMLLAGMA-CiNToftB.js} +1 -1
- package/payload/server/public/assets/{flatten-Bo6YRmWl.js → flatten-BtFI066E.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-B0N06MF7.js → flowDiagram-DWJPFMVM-Xnl3SpIM.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-BVbx4ARZ.js → ganttDiagram-T4ZO3ILL-C1iyWe0f.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-CNs-LD5i.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-C-xRJ94t.js → gitGraphDiagram-UUTBAWPF-D97pbMQb.js} +1 -1
- package/payload/server/public/assets/graph-labels-cZu4pK16.js +1 -0
- package/payload/server/public/assets/{graph-g48ZcA5M.js → graph-qz5tFKqU.js} +3 -3
- package/payload/server/public/assets/{graphlib-YmNcoMjY.js → graphlib-Lq8ijgON.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-DsTNigSS.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-C_OarRTA.js +2 -0
- package/payload/server/public/assets/{isEmpty-D6Kr-M1M.js → isEmpty-D6QovjYR.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-DTrq54yC.js → ishikawaDiagram-UXIWVN3A-B8XBdjJn.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-OZZZMrFX.js → journeyDiagram-VCZTEJTY-CZYbiOaQ.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY--w-IP9pN.js → kanban-definition-6JOO6SKY-B1PybFoh.js} +1 -1
- package/payload/server/public/assets/{line-Ckeulv5T.js → line-D-tw3hHp.js} +1 -1
- package/payload/server/public/assets/{linear-DOh_6k2k.js → linear-BHhXD3cd.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-CVRAxYRD.js → mermaid-parser.core-C9RAnysF.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-B-mE18I1.js → mermaid.core-B532LT1r.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-Bm8mDicL.js → mindmap-definition-QFDTVHPH-DGlgeeTV.js} +1 -1
- package/payload/server/public/assets/{ordinal-BDi6f4xk.js → ordinal-Bl-aM5b9.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-DGES22b-.js +1 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-ChKeDbzt.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-BCmRLgGO.js → pieDiagram-DEJITSTG-DV9FIWko.js} +1 -1
- package/payload/server/public/assets/{public-DknO-g9S.js → public-Bu2_Xi0a.js} +5 -5
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CniTIUTm.js → quadrantDiagram-34T5L4WZ-Betwya4l.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-FGG5Fs7N.js +1 -0
- package/payload/server/public/assets/{reduce-CGi9ik8i.js → reduce-BD4xUd2c.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-CoxBSj9M.js → requirementDiagram-MS252O5E-Cq3vODdg.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-BjS-4jzq.js → sankeyDiagram-XADWPNL6-x8krXWcS.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-B9jVOnPR.js → sequenceDiagram-FGHM5R23-i-_uH-Yl.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-BvOQPzP8.js → stateDiagram-FHFEXIEX-il4KqSgI.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B6zNJ6Tv.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CdfgWLo1.js → timeline-definition-GMOUNBTQ-DATdZkA5.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-VAQQdbtf.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-DKchO3zI.js +1 -0
- package/payload/server/public/assets/useSelectionMode-A5KItZ2T.js +13 -0
- package/payload/server/public/assets/{brand-D0gNihp7.css → useSelectionMode-C-Ojh7W9.css} +1 -1
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-JCgpIbj-.js → vennDiagram-DHZGUBPP-BJh9tJTt.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-CBGtx0bS.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-Dei3VqHo.js → wardleyDiagram-NUSXRM2D-EMN1Hdfg.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-DUtIyoIb.js → xychartDiagram-5P7HB3ND-DbUWXa7T.js} +1 -1
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +5 -6
- package/payload/server/public/public.html +4 -5
- package/payload/server/server.js +65 -23
- package/payload/platform/plugins/admin/hooks/__tests__/insight.test.sh +0 -395
- package/payload/platform/plugins/admin/hooks/insight.sh +0 -219
- package/payload/platform/plugins/admin/hooks/lib/admin-graph-pass-common.sh +0 -239
- package/payload/premium-plugins/writer-craft/hooks/__tests__/voice-session-end-text.test.sh +0 -284
- package/payload/premium-plugins/writer-craft/hooks/voice-session-end-text.sh +0 -75
- package/payload/premium-plugins/writer-craft/mcp/src/cli/ingest-session-text.ts +0 -65
- package/payload/server/public/assets/AdminShell-892Jy_rs.js +0 -1
- package/payload/server/public/assets/Checkbox-Bc2QzX9b.js +0 -1
- package/payload/server/public/assets/admin-D3K13ndi.js +0 -1
- package/payload/server/public/assets/architecture-YZFGNWBL--v-pJPNp.js +0 -1
- package/payload/server/public/assets/brand-CcN3dELF.js +0 -9
- package/payload/server/public/assets/channel-B1IT7to2.js +0 -1
- package/payload/server/public/assets/chunk-336JU56O-CdKRCIeE.js +0 -2
- package/payload/server/public/assets/chunk-426QAEUC-BybuQ3Ve.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-Bd-GrQM6.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-rjCize6i.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BORWOUt0.js +0 -1
- package/payload/server/public/assets/clone-Csqv5U6T.js +0 -1
- package/payload/server/public/assets/data-Br-pdljK.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-CI0s_tqn.js +0 -1
- package/payload/server/public/assets/graph-labels-BYH-IPCb.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-g3gYW7Qm.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-Di6oPQ_-.js +0 -2
- package/payload/server/public/assets/packet-4T2RLAQJ-CT0TB9HI.js +0 -1
- package/payload/server/public/assets/pie-ZZUOXDRM-CXLe7TFF.js +0 -1
- package/payload/server/public/assets/radar-PYXPWWZC-DnPLBl-D.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-v4ND10uR.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-C3cb7Xwe.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-Dc7G3Bgm.js +0 -1
- package/payload/server/public/assets/useSelectionMode-DwsyptOw.js +0 -5
- package/payload/server/public/assets/wardley-RL74JXVD-DtgibWAt.js +0 -1
- /package/payload/server/public/assets/{_baseFor-Cam2PbSt.js → _baseFor-Cs8Y-rGh.js} +0 -0
- /package/payload/server/public/assets/{array-DYRGGQae.js → array-iHZP4KWJ.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-nWsJMTNI.js → cytoscape.esm-BR2GOQ8_.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-Du1XY3Dp.js → defaultLocale-B9aLeOTg.js} +0 -0
- /package/payload/server/public/assets/{dist-BzAsli7o.js → dist-DB-VPj_8.js} +0 -0
- /package/payload/server/public/assets/{init-B5BXBRcm.js → init-BNFRgqHM.js} +0 -0
- /package/payload/server/public/assets/{katex-HOUACuRw.js → katex-B-EfS3nw.js} +0 -0
- /package/payload/server/public/assets/{path-CNO468J-.js → path-DmWWdwp7.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-DRO6hWPh.js → rough.esm-Ci7Kjt46.js} +0 -0
- /package/payload/server/public/assets/{src-CWiyyVfn.js → src-C1jfwBq0.js} +0 -0
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Shared plumbing for the admin-side hook that orchestrates admin-walked
|
|
3
|
-
# graph writes: `insight.sh` (UserPromptSubmit on the operator's `/insight`
|
|
4
|
-
# token). Originally introduced (Task 439) as shared plumbing across two hooks; the
|
|
5
|
-
# per-turn sibling was removed in Task 447 because session-end Pass 3 already
|
|
6
|
-
# performs the graph-completeness sweep. The library is retained — its
|
|
7
|
-
# stdin parse, role / specialist / transcript guards, log-ingest envelope,
|
|
8
|
-
# and post-user tool-name walker are still the single body for the
|
|
9
|
-
# remaining hook.
|
|
10
|
-
#
|
|
11
|
-
# Caller contract:
|
|
12
|
-
# 1. Set `AGP_LOG_TAG` to the hook's identifying tag before sourcing.
|
|
13
|
-
# `insight` is the current value; the helper itself
|
|
14
|
-
# does not log under its own tag.
|
|
15
|
-
# 2. Source this file.
|
|
16
|
-
# 3. Call `agp_setup_logging`, `agp_read_stdin`, `agp_parse_stdin`,
|
|
17
|
-
# `agp_guard_role_specialist`, `agp_guard_transcript` in that order.
|
|
18
|
-
# Each guard exits 0 from inside the helper when its condition fires
|
|
19
|
-
# (sourced `exit` exits the caller).
|
|
20
|
-
# 4. After the guards return, the hook proceeds with its event-specific
|
|
21
|
-
# body (sentinel-grep + intent token for session-end; post-turn-context
|
|
22
|
-
# fetch + directive envelope for pre-turn).
|
|
23
|
-
#
|
|
24
|
-
# Globals set by the helper (read by callers):
|
|
25
|
-
# INPUT — raw stdin (empty when none piped)
|
|
26
|
-
# SESSION_ID — parsed `session_id` field, empty on parse failure
|
|
27
|
-
# TRANSCRIPT_PATH — parsed `transcript_path` field
|
|
28
|
-
# HOOK_EVENT_NAME — parsed `hook_event_name` field
|
|
29
|
-
# SESSION_ID_REPORTED — `$SESSION_ID` or the literal "unknown"
|
|
30
|
-
# UI_BASE — `http://127.0.0.1:${MAXY_UI_INTERNAL_PORT}`
|
|
31
|
-
# LOG_INGEST_URL — `${UI_BASE}/api/admin/log-ingest`
|
|
32
|
-
#
|
|
33
|
-
# Functions:
|
|
34
|
-
# agp_setup_logging — resolves UI port + URLs, or exits 0 with
|
|
35
|
-
# a stderr loud-fail when the port is unset
|
|
36
|
-
# (fail-open so a missing port never bricks
|
|
37
|
-
# the operator).
|
|
38
|
-
# agp_emit_log <line> — POSTs `{tag, level:"info", line}` to
|
|
39
|
-
# log-ingest; silent on network failure.
|
|
40
|
-
# agp_read_stdin — slurps stdin into `INPUT`, leaves it empty
|
|
41
|
-
# when no input is piped.
|
|
42
|
-
# agp_parse_stdin — populates `SESSION_ID`, `TRANSCRIPT_PATH`,
|
|
43
|
-
# `HOOK_EVENT_NAME`, `SESSION_ID_REPORTED`.
|
|
44
|
-
# agp_guard_role_specialist — exits 0 with `trigger-skipped` log on
|
|
45
|
-
# `role-not-admin` or `is-specialist`.
|
|
46
|
-
# agp_guard_transcript — exits 0 with `trigger-skipped` log on
|
|
47
|
-
# `empty-stdin` (when $1="require-stdin") or
|
|
48
|
-
# `missing-transcript`.
|
|
49
|
-
|
|
50
|
-
if [ -z "${AGP_LOG_TAG:-}" ]; then
|
|
51
|
-
echo "[admin-graph-pass-common] AGP_LOG_TAG not set by caller" >&2
|
|
52
|
-
exit 1
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
agp_setup_logging() {
|
|
56
|
-
local ui_port="${MAXY_UI_INTERNAL_PORT:-}"
|
|
57
|
-
if [ -z "$ui_port" ]; then
|
|
58
|
-
echo "[${AGP_LOG_TAG}] trigger-skipped sessionId=unknown reason=missing-env env=MAXY_UI_INTERNAL_PORT" >&2
|
|
59
|
-
exit 0
|
|
60
|
-
fi
|
|
61
|
-
UI_BASE="http://127.0.0.1:${ui_port}"
|
|
62
|
-
LOG_INGEST_URL="${UI_BASE}/api/admin/log-ingest"
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
agp_emit_log() {
|
|
66
|
-
local line="$1"
|
|
67
|
-
curl -sS -o /dev/null -X POST \
|
|
68
|
-
-H 'Content-Type: application/json' \
|
|
69
|
-
--max-time 2 \
|
|
70
|
-
--data "$(AGP_TAG="$AGP_LOG_TAG" AGP_LINE="$line" python3 -c '
|
|
71
|
-
import os, json
|
|
72
|
-
print(json.dumps({"tag": os.environ["AGP_TAG"], "level": "info", "line": os.environ["AGP_LINE"]}))
|
|
73
|
-
')" \
|
|
74
|
-
"$LOG_INGEST_URL" 2>/dev/null || true
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
agp_read_stdin() {
|
|
78
|
-
INPUT=""
|
|
79
|
-
if [ ! -t 0 ]; then
|
|
80
|
-
INPUT=$(cat)
|
|
81
|
-
fi
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
agp_parse_stdin() {
|
|
85
|
-
local parsed
|
|
86
|
-
parsed=$(printf '%s' "$INPUT" | python3 -c '
|
|
87
|
-
import sys, json
|
|
88
|
-
try:
|
|
89
|
-
d = json.load(sys.stdin)
|
|
90
|
-
sid = d.get("session_id", "") or ""
|
|
91
|
-
tpath = d.get("transcript_path", "") or ""
|
|
92
|
-
event = d.get("hook_event_name", "") or ""
|
|
93
|
-
print(f"{sid}\t{tpath}\t{event}")
|
|
94
|
-
except Exception:
|
|
95
|
-
print("\t\t")
|
|
96
|
-
' 2>/dev/null)
|
|
97
|
-
SESSION_ID="${parsed%% *}"
|
|
98
|
-
local rest="${parsed#* }"
|
|
99
|
-
TRANSCRIPT_PATH="${rest%% *}"
|
|
100
|
-
HOOK_EVENT_NAME="${rest#* }"
|
|
101
|
-
SESSION_ID_REPORTED="${SESSION_ID:-unknown}"
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
agp_guard_role_specialist() {
|
|
105
|
-
if [ "${MAXY_SESSION_ROLE:-}" != "admin" ]; then
|
|
106
|
-
agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=role-not-admin"
|
|
107
|
-
exit 0
|
|
108
|
-
fi
|
|
109
|
-
# Recursion guard. A specialist subagent dispatched via the Task tool
|
|
110
|
-
# (e.g. database-operator running the operator's graph writes) carries
|
|
111
|
-
# `MAXY_SPECIALIST=<name>` on the PTY env. Skip — the hooks only fire
|
|
112
|
-
# for the operator's own admin session.
|
|
113
|
-
#
|
|
114
|
-
# Invariant the PTY spawner is expected to honour: `MAXY_SPECIALIST` is
|
|
115
|
-
# either set to a non-empty specialist name OR absent. An explicit empty
|
|
116
|
-
# string from a wrapper would slip the recursion guard here (`-n ""` is
|
|
117
|
-
# false) — that would be a defect at the spawn site, not here.
|
|
118
|
-
if [ -n "${MAXY_SPECIALIST:-}" ]; then
|
|
119
|
-
agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=is-specialist specialist=${MAXY_SPECIALIST}"
|
|
120
|
-
exit 0
|
|
121
|
-
fi
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
# Usage: agp_guard_transcript [require-stdin]
|
|
125
|
-
# Pass `require-stdin` (insight.sh) to emit `empty-stdin` on a
|
|
126
|
-
# blank `INPUT`. Without the flag, parse failure on a non-empty INPUT
|
|
127
|
-
# collapses to `missing-transcript` cleanly.
|
|
128
|
-
agp_guard_transcript() {
|
|
129
|
-
if [ "${1:-}" = "require-stdin" ] && [ -z "$INPUT" ]; then
|
|
130
|
-
agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=empty-stdin"
|
|
131
|
-
exit 0
|
|
132
|
-
fi
|
|
133
|
-
if [ -z "$SESSION_ID" ] || [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; then
|
|
134
|
-
agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=missing-transcript"
|
|
135
|
-
exit 0
|
|
136
|
-
fi
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
# Walks $TRANSCRIPT_PATH and reports, in one line on stdout:
|
|
140
|
-
# <real-user-present:yes|no>\t<matched-tool-name-or-empty>
|
|
141
|
-
# A "real user" record matches `insight.sh`'s `is_real_user`
|
|
142
|
-
# — `type=user, message.role=user`, content is a string OR a list with at
|
|
143
|
-
# least one non-`tool_result` block. `tool_result`-only user records are tool
|
|
144
|
-
# replies, never turn starts.
|
|
145
|
-
#
|
|
146
|
-
# Every assistant `tool_use` block that appears AFTER the latest real-user
|
|
147
|
-
# record is matched against the alternation passed as `$1` (an extended-regex
|
|
148
|
-
# alternation of literal `tool_use.name` values). The synthetic name
|
|
149
|
-
# `Task:<subagent_type>` is also matched, so callers can release on a
|
|
150
|
-
# `Task` tool_use with `input.subagent_type=<name>` by including
|
|
151
|
-
# `Task:<name>` in the alternation. Returns the first match encountered after
|
|
152
|
-
# the latest real-user record (oldest-first) so the release log carries the
|
|
153
|
-
# matched tool's actual name. Empty match means none of the release patterns
|
|
154
|
-
# appeared since the operator's last turn.
|
|
155
|
-
agp_scan_post_user_toolnames() {
|
|
156
|
-
local pattern="$1"
|
|
157
|
-
TRANSCRIPT_PATH="$TRANSCRIPT_PATH" AGP_PATTERN="$pattern" python3 - <<'PY'
|
|
158
|
-
import os, json, re
|
|
159
|
-
|
|
160
|
-
pattern = re.compile(os.environ["AGP_PATTERN"])
|
|
161
|
-
path = os.environ["TRANSCRIPT_PATH"]
|
|
162
|
-
|
|
163
|
-
def is_real_user(rec):
|
|
164
|
-
if not isinstance(rec, dict) or rec.get("type") != "user":
|
|
165
|
-
return False
|
|
166
|
-
msg = rec.get("message")
|
|
167
|
-
if not isinstance(msg, dict) or msg.get("role") != "user":
|
|
168
|
-
return False
|
|
169
|
-
content = msg.get("content")
|
|
170
|
-
if isinstance(content, str):
|
|
171
|
-
return True
|
|
172
|
-
if isinstance(content, list):
|
|
173
|
-
for b in content:
|
|
174
|
-
if isinstance(b, dict) and b.get("type") != "tool_result":
|
|
175
|
-
return True
|
|
176
|
-
return False
|
|
177
|
-
return True
|
|
178
|
-
|
|
179
|
-
def assistant_tool_use_names(rec):
|
|
180
|
-
if not isinstance(rec, dict) or rec.get("type") != "assistant":
|
|
181
|
-
return []
|
|
182
|
-
msg = rec.get("message", {}) or {}
|
|
183
|
-
if msg.get("role") != "assistant":
|
|
184
|
-
return []
|
|
185
|
-
content = msg.get("content")
|
|
186
|
-
if not isinstance(content, list):
|
|
187
|
-
return []
|
|
188
|
-
names = []
|
|
189
|
-
for b in content:
|
|
190
|
-
if isinstance(b, dict) and b.get("type") == "tool_use":
|
|
191
|
-
n = b.get("name")
|
|
192
|
-
if not isinstance(n, str):
|
|
193
|
-
continue
|
|
194
|
-
names.append(n)
|
|
195
|
-
if n == "Task":
|
|
196
|
-
inp = b.get("input")
|
|
197
|
-
if isinstance(inp, dict):
|
|
198
|
-
st = inp.get("subagent_type")
|
|
199
|
-
if isinstance(st, str) and st:
|
|
200
|
-
names.append(f"Task:{st}")
|
|
201
|
-
return names
|
|
202
|
-
|
|
203
|
-
records = []
|
|
204
|
-
try:
|
|
205
|
-
with open(path, "r", encoding="utf-8") as f:
|
|
206
|
-
for raw in f:
|
|
207
|
-
raw = raw.strip()
|
|
208
|
-
if not raw:
|
|
209
|
-
continue
|
|
210
|
-
try:
|
|
211
|
-
records.append(json.loads(raw))
|
|
212
|
-
except Exception:
|
|
213
|
-
continue
|
|
214
|
-
except Exception:
|
|
215
|
-
print("no\t")
|
|
216
|
-
raise SystemExit(0)
|
|
217
|
-
|
|
218
|
-
latest_user_idx = -1
|
|
219
|
-
for i in range(len(records) - 1, -1, -1):
|
|
220
|
-
if is_real_user(records[i]):
|
|
221
|
-
latest_user_idx = i
|
|
222
|
-
break
|
|
223
|
-
|
|
224
|
-
if latest_user_idx == -1:
|
|
225
|
-
print("no\t")
|
|
226
|
-
raise SystemExit(0)
|
|
227
|
-
|
|
228
|
-
matched = ""
|
|
229
|
-
for r in records[latest_user_idx + 1:]:
|
|
230
|
-
for n in assistant_tool_use_names(r):
|
|
231
|
-
if pattern.fullmatch(n):
|
|
232
|
-
matched = n
|
|
233
|
-
break
|
|
234
|
-
if matched:
|
|
235
|
-
break
|
|
236
|
-
|
|
237
|
-
print(f"yes\t{matched}")
|
|
238
|
-
PY
|
|
239
|
-
}
|
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# voice-session-end-text.sh hook test suite.
|
|
3
|
-
#
|
|
4
|
-
# Pattern: a local Python HTTP server stands in for /api/admin/log-ingest
|
|
5
|
-
# and records every POST so the assertions can inspect every log line the
|
|
6
|
-
# hook emitted. The CLI is stubbed with a mock node script so tests run
|
|
7
|
-
# without a live Neo4j connection.
|
|
8
|
-
#
|
|
9
|
-
# Contract under test:
|
|
10
|
-
# - Hook skips with reason=role-not-admin when MAXY_SESSION_ROLE != admin.
|
|
11
|
-
# - Hook skips with reason=is-specialist when MAXY_SPECIALIST is set.
|
|
12
|
-
# - Hook skips with reason=empty-stdin when no stdin is piped.
|
|
13
|
-
# - Hook skips with reason=missing-transcript when the JSONL path in the
|
|
14
|
-
# stdin envelope does not exist.
|
|
15
|
-
# - Hook skips with reason=cli-not-found when the compiled CLI is absent.
|
|
16
|
-
# - Happy path: hook emits trigger + result=ok when all guards pass and the
|
|
17
|
-
# stub CLI exits 0.
|
|
18
|
-
# - Dedupe path: stub CLI returns turns=0 skipped=3 → result=ok with those
|
|
19
|
-
# counts (verifying the hook forwards the CLI stdout to the log-ingest).
|
|
20
|
-
|
|
21
|
-
set -u
|
|
22
|
-
|
|
23
|
-
HOOK="$(cd "$(dirname "$0")/.." && pwd)/voice-session-end-text.sh"
|
|
24
|
-
if [[ ! -f "$HOOK" ]]; then
|
|
25
|
-
echo "FAIL: hook not found at $HOOK" >&2
|
|
26
|
-
exit 1
|
|
27
|
-
fi
|
|
28
|
-
|
|
29
|
-
PASS=0
|
|
30
|
-
FAIL=0
|
|
31
|
-
TMPFILES=()
|
|
32
|
-
LISTENER_PIDS=()
|
|
33
|
-
|
|
34
|
-
cleanup() {
|
|
35
|
-
for pid in "${LISTENER_PIDS[@]:-}"; do
|
|
36
|
-
[[ -n "$pid" ]] && { kill "$pid" 2>/dev/null || true; wait "$pid" 2>/dev/null || true; }
|
|
37
|
-
done
|
|
38
|
-
for f in "${TMPFILES[@]:-}"; do
|
|
39
|
-
[[ -n "$f" ]] && rm -f "$f" 2>/dev/null || true
|
|
40
|
-
done
|
|
41
|
-
}
|
|
42
|
-
trap cleanup EXIT
|
|
43
|
-
|
|
44
|
-
# ---- helpers ---------------------------------------------------------------
|
|
45
|
-
|
|
46
|
-
make_tmp_file() {
|
|
47
|
-
local tmp
|
|
48
|
-
tmp=$(mktemp)
|
|
49
|
-
TMPFILES+=("$tmp")
|
|
50
|
-
echo "$tmp"
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
# Start a listener on a random port; capture every POST body.
|
|
54
|
-
# Sets LISTENER_PORT and LISTENER_LOG globals.
|
|
55
|
-
start_listener() {
|
|
56
|
-
LISTENER_LOG=$(make_tmp_file)
|
|
57
|
-
LISTENER_PORT=$(python3 -c 'import socket; s=socket.socket(); s.bind(("",0)); print(s.getsockname()[1]); s.close()')
|
|
58
|
-
python3 - "$LISTENER_PORT" "$LISTENER_LOG" <<'PY' &
|
|
59
|
-
import sys, http.server, json
|
|
60
|
-
|
|
61
|
-
port = int(sys.argv[1])
|
|
62
|
-
log = sys.argv[2]
|
|
63
|
-
|
|
64
|
-
class H(http.server.BaseHTTPRequestHandler):
|
|
65
|
-
def do_POST(self):
|
|
66
|
-
n = int(self.headers.get("Content-Length", 0))
|
|
67
|
-
body = self.rfile.read(n).decode()
|
|
68
|
-
with open(log, "a") as f:
|
|
69
|
-
f.write(body + "\n")
|
|
70
|
-
self.send_response(200)
|
|
71
|
-
self.end_headers()
|
|
72
|
-
def log_message(self, *a): pass
|
|
73
|
-
|
|
74
|
-
http.server.HTTPServer(("127.0.0.1", port), H).serve_forever()
|
|
75
|
-
PY
|
|
76
|
-
local pid=$!
|
|
77
|
-
LISTENER_PIDS+=("$pid")
|
|
78
|
-
sleep 0.3 # let the server start
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
# Run the hook with given env overrides and optional stdin.
|
|
82
|
-
# Returns: HOOK_EXIT, LISTENER_LINES (array of log-ingest line bodies).
|
|
83
|
-
run_hook() {
|
|
84
|
-
local env_args=("$@")
|
|
85
|
-
local stdin_data="${HOOK_STDIN:-}"
|
|
86
|
-
local hook_exit=0
|
|
87
|
-
if [[ -n "$stdin_data" ]]; then
|
|
88
|
-
printf '%s' "$stdin_data" | env \
|
|
89
|
-
MAXY_UI_INTERNAL_PORT="$LISTENER_PORT" \
|
|
90
|
-
MAXY_SESSION_ROLE="${MAXY_SESSION_ROLE:-admin}" \
|
|
91
|
-
MAXY_SPECIALIST="${MAXY_SPECIALIST:-}" \
|
|
92
|
-
PLATFORM_ROOT="${FAKE_PLATFORM_ROOT:-}" \
|
|
93
|
-
ACCOUNT_ID="${ACCOUNT_ID:-acct-test}" \
|
|
94
|
-
USER_ID="${USER_ID:-user-test}" \
|
|
95
|
-
SESSION_ID="" \
|
|
96
|
-
NEO4J_URI="bolt://localhost:7688" \
|
|
97
|
-
NEO4J_USER="neo4j" \
|
|
98
|
-
NEO4J_PASSWORD="test" \
|
|
99
|
-
${env_args[@]+"${env_args[@]}"} \
|
|
100
|
-
bash "$HOOK" >/dev/null 2>/dev/null || hook_exit=$?
|
|
101
|
-
else
|
|
102
|
-
env \
|
|
103
|
-
MAXY_UI_INTERNAL_PORT="$LISTENER_PORT" \
|
|
104
|
-
MAXY_SESSION_ROLE="${MAXY_SESSION_ROLE:-admin}" \
|
|
105
|
-
MAXY_SPECIALIST="${MAXY_SPECIALIST:-}" \
|
|
106
|
-
PLATFORM_ROOT="${FAKE_PLATFORM_ROOT:-}" \
|
|
107
|
-
ACCOUNT_ID="${ACCOUNT_ID:-acct-test}" \
|
|
108
|
-
USER_ID="${USER_ID:-user-test}" \
|
|
109
|
-
SESSION_ID="" \
|
|
110
|
-
NEO4J_URI="bolt://localhost:7688" \
|
|
111
|
-
NEO4J_USER="neo4j" \
|
|
112
|
-
NEO4J_PASSWORD="test" \
|
|
113
|
-
${env_args[@]+"${env_args[@]}"} \
|
|
114
|
-
bash "$HOOK" </dev/null >/dev/null 2>/dev/null || hook_exit=$?
|
|
115
|
-
fi
|
|
116
|
-
HOOK_EXIT=$hook_exit
|
|
117
|
-
# Read all log-ingest POSTs.
|
|
118
|
-
LISTENER_LINES=()
|
|
119
|
-
while IFS= read -r raw; do
|
|
120
|
-
[[ -z "$raw" ]] && continue
|
|
121
|
-
LISTENER_LINES+=("$raw")
|
|
122
|
-
done < "$LISTENER_LOG"
|
|
123
|
-
# Reset log for next run.
|
|
124
|
-
> "$LISTENER_LOG"
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
assert_contains_line() {
|
|
128
|
-
local test_name="$1"
|
|
129
|
-
local fragment="$2"
|
|
130
|
-
for line in "${LISTENER_LINES[@]:-}"; do
|
|
131
|
-
if [[ "$line" == *"$fragment"* ]]; then
|
|
132
|
-
echo " PASS: $test_name"
|
|
133
|
-
PASS=$((PASS + 1))
|
|
134
|
-
return 0
|
|
135
|
-
fi
|
|
136
|
-
done
|
|
137
|
-
echo " FAIL: $test_name (expected fragment: '$fragment' not found in log lines)"
|
|
138
|
-
printf ' lines:\n'
|
|
139
|
-
for line in "${LISTENER_LINES[@]:-}"; do printf ' %s\n' "$line"; done
|
|
140
|
-
FAIL=$((FAIL + 1))
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
assert_exit() {
|
|
144
|
-
local test_name="$1"
|
|
145
|
-
local expected="$2"
|
|
146
|
-
if [[ "$HOOK_EXIT" -eq "$expected" ]]; then
|
|
147
|
-
echo " PASS: $test_name (exit $expected)"
|
|
148
|
-
PASS=$((PASS + 1))
|
|
149
|
-
else
|
|
150
|
-
echo " FAIL: $test_name (expected exit $expected, got $HOOK_EXIT)"
|
|
151
|
-
FAIL=$((FAIL + 1))
|
|
152
|
-
fi
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
# ---- setup -----------------------------------------------------------------
|
|
156
|
-
|
|
157
|
-
start_listener
|
|
158
|
-
|
|
159
|
-
# Create a fake platform root structure so the lib source works.
|
|
160
|
-
FAKE_PLATFORM_ROOT=$(mktemp -d)
|
|
161
|
-
TMPFILES+=("$FAKE_PLATFORM_ROOT")
|
|
162
|
-
mkdir -p "$FAKE_PLATFORM_ROOT/plugins/admin/hooks/lib"
|
|
163
|
-
# Copy the real lib so guards work correctly.
|
|
164
|
-
REAL_LIB="$(cd "$(dirname "$0")/../../../../platform/plugins/admin/hooks/lib" && pwd)/admin-graph-pass-common.sh"
|
|
165
|
-
if [[ ! -f "$REAL_LIB" ]]; then
|
|
166
|
-
echo "FAIL: admin-graph-pass-common.sh not found at $REAL_LIB" >&2
|
|
167
|
-
exit 1
|
|
168
|
-
fi
|
|
169
|
-
cp "$REAL_LIB" "$FAKE_PLATFORM_ROOT/plugins/admin/hooks/lib/admin-graph-pass-common.sh"
|
|
170
|
-
|
|
171
|
-
# Create a minimal fake JSONL transcript.
|
|
172
|
-
FAKE_TRANSCRIPT=$(make_tmp_file)
|
|
173
|
-
printf '{"type":"user","message":{"role":"user","content":"Hello world"}}\n' > "$FAKE_TRANSCRIPT"
|
|
174
|
-
|
|
175
|
-
# Create a stub CLI that exits 0 with a canned result.
|
|
176
|
-
FAKE_CLI_DIR=$(mktemp -d)
|
|
177
|
-
TMPFILES+=("$FAKE_CLI_DIR")
|
|
178
|
-
STUB_CLI="$FAKE_CLI_DIR/ingest-session-text.js"
|
|
179
|
-
cat > "$STUB_CLI" <<'STUB'
|
|
180
|
-
process.stdout.write("turns=3 skipped=0 sessionId=test-session\n");
|
|
181
|
-
process.exit(0);
|
|
182
|
-
STUB
|
|
183
|
-
|
|
184
|
-
# Stub CLI for dedupe scenario (turns=0).
|
|
185
|
-
STUB_CLI_DEDUP="$FAKE_CLI_DIR/ingest-dedup.js"
|
|
186
|
-
cat > "$STUB_CLI_DEDUP" <<'STUB'
|
|
187
|
-
process.stdout.write("turns=0 skipped=3 sessionId=test-session\n");
|
|
188
|
-
process.exit(0);
|
|
189
|
-
STUB
|
|
190
|
-
|
|
191
|
-
# Stub CLI that exits 1 (error).
|
|
192
|
-
STUB_CLI_ERR="$FAKE_CLI_DIR/ingest-error.js"
|
|
193
|
-
cat > "$STUB_CLI_ERR" <<'STUB'
|
|
194
|
-
process.stderr.write("simulated error\n");
|
|
195
|
-
process.exit(1);
|
|
196
|
-
STUB
|
|
197
|
-
|
|
198
|
-
# Valid stdin envelope for the SessionEnd hook.
|
|
199
|
-
valid_envelope() {
|
|
200
|
-
local transcript="${1:-$FAKE_TRANSCRIPT}"
|
|
201
|
-
printf '{"session_id":"test-session","transcript_path":"%s","hook_event_name":"SessionEnd"}' "$transcript"
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
# Override CLI path by symlinking ingest-session-text.js inside a fake platform layout.
|
|
205
|
-
make_platform_with_cli() {
|
|
206
|
-
local stub="$1"
|
|
207
|
-
local proot
|
|
208
|
-
proot=$(mktemp -d)
|
|
209
|
-
TMPFILES+=("$proot")
|
|
210
|
-
cp "$FAKE_PLATFORM_ROOT/plugins/admin/hooks/lib/admin-graph-pass-common.sh" \
|
|
211
|
-
"$proot/plugins/admin/hooks/lib/" 2>/dev/null || \
|
|
212
|
-
{ mkdir -p "$proot/plugins/admin/hooks/lib"; cp "$REAL_LIB" "$proot/plugins/admin/hooks/lib/admin-graph-pass-common.sh"; }
|
|
213
|
-
mkdir -p "$proot/../premium-plugins/writer-craft/mcp/dist/cli" 2>/dev/null || \
|
|
214
|
-
mkdir -p "$(dirname "$proot")/../premium-plugins/writer-craft/mcp/dist/cli" 2>/dev/null || true
|
|
215
|
-
# Simpler: build the expected path from $PLATFORM_ROOT/../premium-plugins/...
|
|
216
|
-
# The hook computes: ${PLATFORM_ROOT}/../premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.js
|
|
217
|
-
# So parent of $proot + /premium-plugins/writer-craft/mcp/dist/cli/
|
|
218
|
-
local parent
|
|
219
|
-
parent=$(dirname "$proot")
|
|
220
|
-
mkdir -p "$parent/premium-plugins/writer-craft/mcp/dist/cli"
|
|
221
|
-
cp "$stub" "$parent/premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.js"
|
|
222
|
-
echo "$proot"
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
# ---- tests -----------------------------------------------------------------
|
|
226
|
-
echo "[voice-session-end-text.test] starting"
|
|
227
|
-
|
|
228
|
-
# T1: non-admin role → skip
|
|
229
|
-
MAXY_SESSION_ROLE="public" HOOK_STDIN=$(valid_envelope) run_hook
|
|
230
|
-
assert_exit "T1: non-admin exits 0" 0
|
|
231
|
-
assert_contains_line "T1: non-admin emits trigger-skipped" "trigger-skipped"
|
|
232
|
-
|
|
233
|
-
# T2: specialist → skip
|
|
234
|
-
unset MAXY_SESSION_ROLE 2>/dev/null || true
|
|
235
|
-
MAXY_SESSION_ROLE="admin" MAXY_SPECIALIST="database-operator" HOOK_STDIN=$(valid_envelope) run_hook
|
|
236
|
-
assert_exit "T2: specialist exits 0" 0
|
|
237
|
-
assert_contains_line "T2: specialist emits trigger-skipped" "trigger-skipped"
|
|
238
|
-
|
|
239
|
-
# T3: empty stdin → skip
|
|
240
|
-
MAXY_SPECIALIST="" MAXY_SESSION_ROLE="admin"
|
|
241
|
-
HOOK_STDIN="" run_hook # no stdin piped
|
|
242
|
-
assert_exit "T3: empty-stdin exits 0" 0
|
|
243
|
-
assert_contains_line "T3: empty-stdin emits trigger-skipped" "trigger-skipped"
|
|
244
|
-
|
|
245
|
-
# T4: missing transcript → skip
|
|
246
|
-
FAKE_MISSING=$(make_tmp_file)
|
|
247
|
-
rm -f "$FAKE_MISSING"
|
|
248
|
-
HOOK_STDIN=$(valid_envelope "$FAKE_MISSING") run_hook
|
|
249
|
-
assert_exit "T4: missing-transcript exits 0" 0
|
|
250
|
-
assert_contains_line "T4: missing-transcript emits trigger-skipped" "trigger-skipped"
|
|
251
|
-
|
|
252
|
-
# T5: CLI not found → skip
|
|
253
|
-
FAKE_PLATFORM_ROOT="$FAKE_PLATFORM_ROOT" HOOK_STDIN=$(valid_envelope) run_hook
|
|
254
|
-
assert_exit "T5: cli-not-found exits 0" 0
|
|
255
|
-
assert_contains_line "T5: cli-not-found emits trigger-skipped" "cli-not-found"
|
|
256
|
-
|
|
257
|
-
# T6: happy path — stub CLI exits 0
|
|
258
|
-
HAPPY_PROOT=$(make_platform_with_cli "$STUB_CLI")
|
|
259
|
-
FAKE_PLATFORM_ROOT="$HAPPY_PROOT" HOOK_STDIN=$(valid_envelope) run_hook
|
|
260
|
-
assert_exit "T6: happy path exits 0" 0
|
|
261
|
-
assert_contains_line "T6: happy path emits trigger" "trigger"
|
|
262
|
-
assert_contains_line "T6: happy path emits result=ok" "result=ok"
|
|
263
|
-
|
|
264
|
-
# T7: dedupe — stub returns turns=0 skipped=3
|
|
265
|
-
DEDUP_PROOT=$(make_platform_with_cli "$STUB_CLI_DEDUP")
|
|
266
|
-
FAKE_PLATFORM_ROOT="$DEDUP_PROOT" HOOK_STDIN=$(valid_envelope) run_hook
|
|
267
|
-
assert_exit "T7: dedupe exits 0" 0
|
|
268
|
-
assert_contains_line "T7: dedupe emits result=ok" "result=ok"
|
|
269
|
-
|
|
270
|
-
# T8: CLI error — stub exits 1
|
|
271
|
-
ERR_PROOT=$(make_platform_with_cli "$STUB_CLI_ERR")
|
|
272
|
-
FAKE_PLATFORM_ROOT="$ERR_PROOT" HOOK_STDIN=$(valid_envelope) run_hook
|
|
273
|
-
assert_exit "T8: cli-error exits 0" 0
|
|
274
|
-
assert_contains_line "T8: cli-error emits result=err" "result=err"
|
|
275
|
-
|
|
276
|
-
# ---- summary ---------------------------------------------------------------
|
|
277
|
-
echo ""
|
|
278
|
-
if [[ "$FAIL" -eq 0 ]]; then
|
|
279
|
-
echo "[voice-session-end-text.test] all $PASS tests passed"
|
|
280
|
-
exit 0
|
|
281
|
-
else
|
|
282
|
-
echo "[voice-session-end-text.test] FAILED — $FAIL/$((PASS + FAIL)) tests failed"
|
|
283
|
-
exit 1
|
|
284
|
-
fi
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# SessionEnd hook — auto-ingests operator PTY turns as :Message {format:'text'}
|
|
3
|
-
# corpus nodes in the writer-craft voice corpus.
|
|
4
|
-
#
|
|
5
|
-
# Fired by Claude Code after each PTY session terminates. Receives the full
|
|
6
|
-
# session envelope from stdin:
|
|
7
|
-
# { "session_id": "...", "transcript_path": "...", "hook_event_name": "SessionEnd", ... }
|
|
8
|
-
#
|
|
9
|
-
# Guards (shared logic via admin-graph-pass-common.sh):
|
|
10
|
-
# - role != admin → exit 0 (trigger-skipped reason=role-not-admin)
|
|
11
|
-
# - MAXY_SPECIALIST set → exit 0 (trigger-skipped reason=is-specialist)
|
|
12
|
-
# - empty stdin → exit 0 (trigger-skipped reason=empty-stdin)
|
|
13
|
-
# - missing transcript → exit 0 (trigger-skipped reason=missing-transcript)
|
|
14
|
-
#
|
|
15
|
-
# Additional guard:
|
|
16
|
-
# - CLI not found → exit 0 (trigger-skipped reason=cli-not-found)
|
|
17
|
-
# Graceful: accounts without writer-craft installed skip silently.
|
|
18
|
-
#
|
|
19
|
-
# Environment (inherited from PTY spawn — set by session manager systemd unit):
|
|
20
|
-
# PLATFORM_ROOT — used to locate the admin hooks lib and the CLI
|
|
21
|
-
# ACCOUNT_ID — operator's account identifier
|
|
22
|
-
# USER_ID — operator's userId
|
|
23
|
-
# SESSION_ID — parsed from stdin envelope by agp_parse_stdin
|
|
24
|
-
# NEO4J_URI — bolt URI (propagated from systemd unit env)
|
|
25
|
-
# NEO4J_USER — neo4j username
|
|
26
|
-
# NEO4J_PASSWORD — neo4j password
|
|
27
|
-
#
|
|
28
|
-
# Observability:
|
|
29
|
-
# [voice-session-end-hook] trigger-skipped sessionId=<id> reason=<reason>
|
|
30
|
-
# [voice-session-end-hook] trigger sessionId=<id>
|
|
31
|
-
# [voice-session-end-hook] result=ok sessionId=<id> turns=<n> skipped=<m>
|
|
32
|
-
# [voice-session-end-hook] result=err sessionId=<id> reason=<detail>
|
|
33
|
-
|
|
34
|
-
set -uo pipefail
|
|
35
|
-
|
|
36
|
-
AGP_LOG_TAG="voice-session-end-hook"
|
|
37
|
-
|
|
38
|
-
# Locate the shared plumbing library via PLATFORM_ROOT (set at PTY spawn).
|
|
39
|
-
# This is more robust than a relative path from the hook's own directory.
|
|
40
|
-
AGP_LIB="${PLATFORM_ROOT:-}/plugins/admin/hooks/lib/admin-graph-pass-common.sh"
|
|
41
|
-
if [ ! -f "$AGP_LIB" ]; then
|
|
42
|
-
echo "[${AGP_LOG_TAG}] trigger-skipped sessionId=unknown reason=missing-helper path=${AGP_LIB}" >&2
|
|
43
|
-
exit 0
|
|
44
|
-
fi
|
|
45
|
-
# shellcheck source=../../../platform/plugins/admin/hooks/lib/admin-graph-pass-common.sh
|
|
46
|
-
source "$AGP_LIB"
|
|
47
|
-
|
|
48
|
-
agp_setup_logging
|
|
49
|
-
agp_read_stdin
|
|
50
|
-
agp_parse_stdin
|
|
51
|
-
agp_guard_role_specialist
|
|
52
|
-
agp_guard_transcript require-stdin
|
|
53
|
-
|
|
54
|
-
# Locate the ingest CLI. Built alongside the MCP server — lives in dist/cli/.
|
|
55
|
-
CLI_PATH="${PLATFORM_ROOT:-}/../premium-plugins/writer-craft/mcp/dist/cli/ingest-session-text.js"
|
|
56
|
-
if [ ! -f "$CLI_PATH" ]; then
|
|
57
|
-
agp_emit_log "trigger-skipped sessionId=${SESSION_ID_REPORTED} reason=cli-not-found path=${CLI_PATH}"
|
|
58
|
-
exit 0
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
agp_emit_log "trigger sessionId=${SESSION_ID_REPORTED}"
|
|
62
|
-
|
|
63
|
-
# Invoke the CLI. All required context is in the inherited env:
|
|
64
|
-
# ACCOUNT_ID, USER_ID, NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD
|
|
65
|
-
# SESSION_ID is passed explicitly since agp_parse_stdin sets it from stdin.
|
|
66
|
-
result=$(SESSION_ID="${SESSION_ID}" node "$CLI_PATH" "$TRANSCRIPT_PATH" 2>&1)
|
|
67
|
-
exit_code=$?
|
|
68
|
-
|
|
69
|
-
if [ "$exit_code" -eq 0 ]; then
|
|
70
|
-
agp_emit_log "result=ok sessionId=${SESSION_ID_REPORTED} ${result}"
|
|
71
|
-
else
|
|
72
|
-
agp_emit_log "result=err sessionId=${SESSION_ID_REPORTED} reason=${result}"
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
exit 0
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* CLI wrapper for voice-ingest-session-text.
|
|
4
|
-
*
|
|
5
|
-
* Invoked by the SessionEnd hook after each admin PTY session:
|
|
6
|
-
* node dist/cli/ingest-session-text.js <transcript_path>
|
|
7
|
-
*
|
|
8
|
-
* All other parameters are read from environment variables inherited from
|
|
9
|
-
* the PTY session (which inherits from the session manager's systemd unit):
|
|
10
|
-
*
|
|
11
|
-
* ACCOUNT_ID — operator's account identifier
|
|
12
|
-
* USER_ID — operator's userId (= adminUserId)
|
|
13
|
-
* SESSION_ID — session identifier (parsed by hook from stdin)
|
|
14
|
-
* NEO4J_URI — neo4j bolt URI
|
|
15
|
-
* NEO4J_USER — neo4j username
|
|
16
|
-
* NEO4J_PASSWORD — neo4j password
|
|
17
|
-
*
|
|
18
|
-
* Exit codes:
|
|
19
|
-
* 0 — success (includes no-new-turns case)
|
|
20
|
-
* 1 — error (missing required env, JSONL not found, DB error)
|
|
21
|
-
*
|
|
22
|
-
* stdout: one-line result summary ("turns=N skipped=M") for the hook to
|
|
23
|
-
* capture and forward to the log-ingest endpoint.
|
|
24
|
-
* stderr: detailed diagnostics (tool log lines).
|
|
25
|
-
*/
|
|
26
|
-
import { voiceIngestSessionText } from "../tools/voice-ingest-session-text.js";
|
|
27
|
-
import { closeDriver } from "../lib/neo4j.js";
|
|
28
|
-
|
|
29
|
-
const transcriptPath = process.argv[2];
|
|
30
|
-
if (!transcriptPath) {
|
|
31
|
-
process.stderr.write("[ingest-session-text] error: transcript path required as argv[2]\n");
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const accountId = process.env.ACCOUNT_ID ?? "";
|
|
36
|
-
const userId = process.env.USER_ID ?? "";
|
|
37
|
-
const sessionId = process.env.SESSION_ID ?? undefined;
|
|
38
|
-
|
|
39
|
-
if (!accountId || !userId) {
|
|
40
|
-
process.stderr.write(
|
|
41
|
-
`[ingest-session-text] error: ACCOUNT_ID and USER_ID must be set in env (accountId=${accountId || "empty"} userId=${userId || "empty"})\n`,
|
|
42
|
-
);
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
const result = await voiceIngestSessionText({
|
|
48
|
-
sessionJsonlPath: transcriptPath,
|
|
49
|
-
accountId,
|
|
50
|
-
userId,
|
|
51
|
-
sessionId,
|
|
52
|
-
});
|
|
53
|
-
// Emit one-line summary to stdout for the hook to capture.
|
|
54
|
-
process.stdout.write(
|
|
55
|
-
`turns=${result.ingestedCount} skipped=${result.skippedCount} sessionId=${result.sessionId ?? "unknown"}\n`,
|
|
56
|
-
);
|
|
57
|
-
process.exit(0);
|
|
58
|
-
} catch (err) {
|
|
59
|
-
process.stderr.write(
|
|
60
|
-
`[ingest-session-text] error: ${err instanceof Error ? err.message : String(err)}\n`,
|
|
61
|
-
);
|
|
62
|
-
process.exit(1);
|
|
63
|
-
} finally {
|
|
64
|
-
await closeDriver();
|
|
65
|
-
}
|