@rubytech/create-maxy-code 0.1.191 → 0.1.193

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/dist/__tests__/brew-resolve.test.js +1 -1
  2. package/dist/__tests__/macos-darwin-branch.test.js +1 -1
  3. package/dist/index.js +2 -2
  4. package/package.json +1 -1
  5. package/payload/platform/lib/mcp-eager/dist/index.d.ts +55 -0
  6. package/payload/platform/lib/mcp-eager/dist/index.d.ts.map +1 -1
  7. package/payload/platform/lib/mcp-eager/dist/index.js +76 -0
  8. package/payload/platform/lib/mcp-eager/dist/index.js.map +1 -1
  9. package/payload/platform/lib/mcp-eager/package.json +7 -0
  10. package/payload/platform/lib/mcp-eager/src/index.ts +101 -0
  11. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-admin-tool-gate.test.sh +102 -0
  12. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-base64-guard.test.sh +4 -4
  13. package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +56 -0
  14. package/payload/platform/plugins/browser/mcp/dist/index.js +19 -19
  15. package/payload/platform/plugins/browser/mcp/dist/index.js.map +1 -1
  16. package/payload/platform/plugins/docs/references/projects-guide.md +21 -0
  17. package/payload/platform/plugins/email/mcp/dist/index.js +12 -12
  18. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  19. package/payload/platform/plugins/memory/mcp/dist/index.js +8 -8
  20. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  21. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  22. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +35 -0
  23. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  24. package/payload/platform/plugins/prompt-optimiser/skills/prompt-optimiser/SKILL.md +1 -0
  25. package/payload/platform/plugins/url-get/mcp/dist/index.js +2 -2
  26. package/payload/platform/plugins/url-get/mcp/dist/index.js.map +1 -1
  27. package/payload/platform/plugins/work/PLUGIN.md +2 -0
  28. package/payload/platform/plugins/work/skills/execute-task/SKILL.md +101 -0
  29. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  30. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +46 -2
  31. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  32. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.d.ts +116 -0
  33. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.d.ts.map +1 -0
  34. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.js +125 -0
  35. package/payload/premium-plugins/writer-craft/lib/mcp-eager/dist/index.js.map +1 -0
  36. package/payload/premium-plugins/writer-craft/lib/mcp-eager/package.json +7 -0
  37. package/payload/premium-plugins/writer-craft/mcp/dist/index.d.ts.map +1 -1
  38. package/payload/premium-plugins/writer-craft/mcp/dist/index.js +5 -4
  39. package/payload/premium-plugins/writer-craft/mcp/dist/index.js.map +1 -1
  40. package/payload/premium-plugins/writer-craft/mcp/src/index.ts +5 -4
  41. package/payload/server/public/assets/AdminShell-BoJbnn6d.js +1 -0
  42. package/payload/server/public/assets/{Checkbox-g2WZRfCt.js → Checkbox-G-kyM1P1.js} +1 -1
  43. package/payload/server/public/assets/admin-B-GDNkoN.js +1 -0
  44. package/payload/server/public/assets/{arc-DMDAZHAN.js → arc-aUiRP9AS.js} +1 -1
  45. package/payload/server/public/assets/architecture-YZFGNWBL--v-pJPNp.js +1 -0
  46. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-BVKxPUIS.js → architectureDiagram-Q4EWVU46-BGgdXdD2.js} +1 -1
  47. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-BpD-1G1V.js → blockDiagram-DXYQGD6D-DKWoL4Rp.js} +1 -1
  48. package/payload/server/public/assets/{brand-BGvv6AYI.js → brand-CYcGHhlS.js} +1 -1
  49. package/payload/server/public/assets/{brand-BQW2DYy_.css → brand-MbpD2Bhr.css} +1 -1
  50. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-CFl9Dr6U.js → c4Diagram-AHTNJAMY-Ca6rs6F4.js} +1 -1
  51. package/payload/server/public/assets/channel-BwsV7WTV.js +1 -0
  52. package/payload/server/public/assets/{chunk-2KRD3SAO-Di4bO8ir.js → chunk-2KRD3SAO-BK3470lx.js} +1 -1
  53. package/payload/server/public/assets/{chunk-336JU56O-B2Uc6pEC.js → chunk-336JU56O-DcdMZImV.js} +2 -2
  54. package/payload/server/public/assets/chunk-426QAEUC-9FSP-c1z.js +1 -0
  55. package/payload/server/public/assets/{chunk-4BX2VUAB-DyEhFk-Z.js → chunk-4BX2VUAB-BOvVdJLf.js} +1 -1
  56. package/payload/server/public/assets/{chunk-4TB4RGXK-Dvf5uiFR.js → chunk-4TB4RGXK-CC1QpEvA.js} +1 -1
  57. package/payload/server/public/assets/{chunk-55IACEB6-BRJOZLpU.js → chunk-55IACEB6-BwZyF7vR.js} +1 -1
  58. package/payload/server/public/assets/{chunk-5FUZZQ4R-DUQR1rF9.js → chunk-5FUZZQ4R-o1b70dEE.js} +1 -1
  59. package/payload/server/public/assets/{chunk-5PVQY5BW-OTvNug7K.js → chunk-5PVQY5BW-C7VCYRz9.js} +1 -1
  60. package/payload/server/public/assets/{chunk-67CJDMHE-BG6-9r6c.js → chunk-67CJDMHE-D5bhMrtY.js} +1 -1
  61. package/payload/server/public/assets/{chunk-7N4EOEYR-BvMbitX7.js → chunk-7N4EOEYR-Si7Lgrwc.js} +1 -1
  62. package/payload/server/public/assets/{chunk-AA7GKIK3-CE8mGBD5.js → chunk-AA7GKIK3-DMuHtDqO.js} +1 -1
  63. package/payload/server/public/assets/{chunk-BSJP7CBP-DP7LTBll.js → chunk-BSJP7CBP-L79XKVcb.js} +1 -1
  64. package/payload/server/public/assets/{chunk-CIAEETIT-VfnIdN-h.js → chunk-CIAEETIT-C0O7Upmg.js} +1 -1
  65. package/payload/server/public/assets/{chunk-EDXVE4YY-CRAsAWbD.js → chunk-EDXVE4YY-B8d5xj_4.js} +1 -1
  66. package/payload/server/public/assets/{chunk-ENJZ2VHE-BNllMoi_.js → chunk-ENJZ2VHE-SSbr4n2X.js} +1 -1
  67. package/payload/server/public/assets/{chunk-FMBD7UC4-w-yBZsN2.js → chunk-FMBD7UC4-C_E43NFJ.js} +1 -1
  68. package/payload/server/public/assets/{chunk-FOC6F5B3-f9cFywhd.js → chunk-FOC6F5B3-D9lWWHAu.js} +1 -1
  69. package/payload/server/public/assets/{chunk-ICPOFSXX-vTeVS4qd.js → chunk-ICPOFSXX-Cgj9YSpX.js} +2 -2
  70. package/payload/server/public/assets/{chunk-K5T4RW27-B_ZUrFUq.js → chunk-K5T4RW27-DuhsNH4c.js} +1 -1
  71. package/payload/server/public/assets/{chunk-KGLVRYIC-CcWTvRlI.js → chunk-KGLVRYIC-B4-A1Abi.js} +1 -1
  72. package/payload/server/public/assets/{chunk-LIHQZDEY-D-5-peQw.js → chunk-LIHQZDEY-BxqgHRgT.js} +1 -1
  73. package/payload/server/public/assets/{chunk-ORNJ4GCN-B4Z5L25I.js → chunk-ORNJ4GCN-DEYQ5WaJ.js} +1 -1
  74. package/payload/server/public/assets/{chunk-OYMX7WX6-BdSFcvNa.js → chunk-OYMX7WX6-DNEEcCqI.js} +1 -1
  75. package/payload/server/public/assets/chunk-QZHKN3VN-Bd-GrQM6.js +1 -0
  76. package/payload/server/public/assets/{chunk-U2HBQHQK-BpFXup4D.js → chunk-U2HBQHQK-DuaoK83q.js} +1 -1
  77. package/payload/server/public/assets/{chunk-X2U36JSP-DF-6QHx4.js → chunk-X2U36JSP-CzMp0dpq.js} +1 -1
  78. package/payload/server/public/assets/{chunk-XPW4576I-m1Y_r88I.js → chunk-XPW4576I-CBfZXZDB.js} +1 -1
  79. package/payload/server/public/assets/{chunk-YZCP3GAM-CKgDoLRT.js → chunk-YZCP3GAM-B2wBDwSa.js} +1 -1
  80. package/payload/server/public/assets/{chunk-ZZ45TVLE-B0ACvZCZ.js → chunk-ZZ45TVLE-DkyF7XKW.js} +1 -1
  81. package/payload/server/public/assets/classDiagram-6PBFFD2Q-CEWNfHVo.js +1 -0
  82. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BBjzRxux.js +1 -0
  83. package/payload/server/public/assets/clone-CXtSonnn.js +1 -0
  84. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-BhtgY3T7.js → cose-bilkent-S5V4N54A-Br2gjtEO.js} +1 -1
  85. package/payload/server/public/assets/{dagre-3yl9rfxR.js → dagre-AGj1P0p4.js} +1 -1
  86. package/payload/server/public/assets/{dagre-KV5264BT-D-Ux4uwL.js → dagre-KV5264BT-Cnrxcu6A.js} +1 -1
  87. package/payload/server/public/assets/data-Bg0Frxt3.js +1 -0
  88. package/payload/server/public/assets/{diagram-5BDNPKRD-D-MOMikl.js → diagram-5BDNPKRD-gyFYSx73.js} +1 -1
  89. package/payload/server/public/assets/{diagram-G4DWMVQ6-DKdFcnhf.js → diagram-G4DWMVQ6-DFmZEv5C.js} +1 -1
  90. package/payload/server/public/assets/{diagram-MMDJMWI5-ajamR766.js → diagram-MMDJMWI5-DA4qbuEW.js} +1 -1
  91. package/payload/server/public/assets/{diagram-TYMM5635-D2Pbk2Kk.js → diagram-TYMM5635-D4n-8gig.js} +1 -1
  92. package/payload/server/public/assets/{erDiagram-SMLLAGMA-v3o948pk.js → erDiagram-SMLLAGMA-CigRMjF1.js} +1 -1
  93. package/payload/server/public/assets/{flatten-BsWEYbBB.js → flatten-Bo6YRmWl.js} +1 -1
  94. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-byC1a6GE.js → flowDiagram-DWJPFMVM-ClS27acA.js} +1 -1
  95. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-Dybzs-Sw.js → ganttDiagram-T4ZO3ILL-Dr1IulXe.js} +1 -1
  96. package/payload/server/public/assets/gitGraph-7Q5UKJZL-CI0s_tqn.js +1 -0
  97. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DvzXYLuW.js → gitGraphDiagram-UUTBAWPF-lwLibdPs.js} +1 -1
  98. package/payload/server/public/assets/graph-CpozZ74I.js +51 -0
  99. package/payload/server/public/assets/graph-labels-C2y666R0.js +1 -0
  100. package/payload/server/public/assets/{graphlib-DvH_spaA.js → graphlib-DBvoJMrc.js} +1 -1
  101. package/payload/server/public/assets/info-OMHHGYJF-g3gYW7Qm.js +1 -0
  102. package/payload/server/public/assets/infoDiagram-42DDH7IO-D4kReEIV.js +2 -0
  103. package/payload/server/public/assets/{isEmpty-BWl67LAZ.js → isEmpty-D6Kr-M1M.js} +1 -1
  104. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CpY5W64P.js → ishikawaDiagram-UXIWVN3A-BnteQRQ0.js} +1 -1
  105. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-ymMAQVdm.js → journeyDiagram-VCZTEJTY-CXu6hLyJ.js} +1 -1
  106. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-_NX9hNK3.js → kanban-definition-6JOO6SKY-CGDW2jle.js} +1 -1
  107. package/payload/server/public/assets/{line-Dz7zKWX-.js → line-pdg5hxC3.js} +1 -1
  108. package/payload/server/public/assets/{linear-0O14Y6uf.js → linear-DOh_6k2k.js} +1 -1
  109. package/payload/server/public/assets/{mermaid-parser.core-BCWd1en6.js → mermaid-parser.core-o3lIIK3Z.js} +2 -2
  110. package/payload/server/public/assets/{mermaid.core-gSoFkVoN.js → mermaid.core-DLR6Q3rV.js} +3 -3
  111. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-QuIyK2bd.js → mindmap-definition-QFDTVHPH-CH4cB0Dn.js} +1 -1
  112. package/payload/server/public/assets/{ordinal-krseTxxN.js → ordinal-BDi6f4xk.js} +1 -1
  113. package/payload/server/public/assets/packet-4T2RLAQJ-CT0TB9HI.js +1 -0
  114. package/payload/server/public/assets/pie-ZZUOXDRM-CXLe7TFF.js +1 -0
  115. package/payload/server/public/assets/{pieDiagram-DEJITSTG-DHK0yy7U.js → pieDiagram-DEJITSTG-Ba4voL_Y.js} +1 -1
  116. package/payload/server/public/assets/public-C8mil9h0.js +35 -0
  117. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-wK1jwWo5.js → quadrantDiagram-34T5L4WZ-Cqn2xehe.js} +1 -1
  118. package/payload/server/public/assets/radar-PYXPWWZC-DnPLBl-D.js +1 -0
  119. package/payload/server/public/assets/{reduce-tk-xY6Fv.js → reduce-CGi9ik8i.js} +1 -1
  120. package/payload/server/public/assets/{requirementDiagram-MS252O5E-W_mH85_d.js → requirementDiagram-MS252O5E-DLfNx99l.js} +1 -1
  121. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-nGAM-YLQ.js → sankeyDiagram-XADWPNL6-uck_V-cb.js} +1 -1
  122. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-SMfUCwBH.js → sequenceDiagram-FGHM5R23-DmQalqud.js} +1 -1
  123. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-CYKXFirH.js → stateDiagram-FHFEXIEX-DUdWeVRa.js} +1 -1
  124. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DNTpFwdy.js +1 -0
  125. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-DethbNFa.js → timeline-definition-GMOUNBTQ-BgL1wM5n.js} +1 -1
  126. package/payload/server/public/assets/treeView-SZITEDCU-C3cb7Xwe.js +1 -0
  127. package/payload/server/public/assets/treemap-W4RFUUIX-Dc7G3Bgm.js +1 -0
  128. package/payload/server/public/assets/useSelectionMode-BLpVlOgm.js +5 -0
  129. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BUlJTe9Z.js → vennDiagram-DHZGUBPP-7FHjIqPi.js} +1 -1
  130. package/payload/server/public/assets/wardley-RL74JXVD-DtgibWAt.js +1 -0
  131. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-CL1TvZ7j.js → wardleyDiagram-NUSXRM2D-BHzo4V1J.js} +1 -1
  132. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-Cav10l-Z.js → xychartDiagram-5P7HB3ND-DtuWhqwh.js} +1 -1
  133. package/payload/server/public/data.html +5 -5
  134. package/payload/server/public/graph.html +6 -6
  135. package/payload/server/public/index.html +6 -8
  136. package/payload/server/public/public.html +5 -5
  137. package/payload/server/server.js +478 -456
  138. package/payload/server/public/assets/admin-DFxXLXfg.js +0 -216
  139. package/payload/server/public/assets/architecture-YZFGNWBL-COhEvUpo.js +0 -1
  140. package/payload/server/public/assets/channel-BTEFjudQ.js +0 -1
  141. package/payload/server/public/assets/chunk-426QAEUC-CcIGcm29.js +0 -1
  142. package/payload/server/public/assets/chunk-QZHKN3VN-CWh_0JsP.js +0 -1
  143. package/payload/server/public/assets/classDiagram-6PBFFD2Q-ClkdniKF.js +0 -1
  144. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-kjJetfh2.js +0 -1
  145. package/payload/server/public/assets/clone-BllVm2iY.js +0 -1
  146. package/payload/server/public/assets/data-DtYcqL1G.js +0 -1
  147. package/payload/server/public/assets/gitGraph-7Q5UKJZL-DeTNsAO0.js +0 -1
  148. package/payload/server/public/assets/graph-C-XPyGsO.js +0 -1
  149. package/payload/server/public/assets/graph-labels-CZin3myX.js +0 -1
  150. package/payload/server/public/assets/info-OMHHGYJF-DJJ9GlS6.js +0 -1
  151. package/payload/server/public/assets/infoDiagram-42DDH7IO-C2FeU8nE.js +0 -2
  152. package/payload/server/public/assets/lib-BEvO-mE0.js +0 -33
  153. package/payload/server/public/assets/packet-4T2RLAQJ-CGbvGkvF.js +0 -1
  154. package/payload/server/public/assets/page-CKlvbjKn.js +0 -1
  155. package/payload/server/public/assets/page-DSj21hpG.js +0 -51
  156. package/payload/server/public/assets/pie-ZZUOXDRM-BZy8rjFn.js +0 -1
  157. package/payload/server/public/assets/public-BnS9zREj.js +0 -7
  158. package/payload/server/public/assets/radar-PYXPWWZC-DcfWIVXr.js +0 -1
  159. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B9ZwM8kx.js +0 -1
  160. package/payload/server/public/assets/treeView-SZITEDCU-3WugwVdj.js +0 -1
  161. package/payload/server/public/assets/treemap-W4RFUUIX-Cf5mDLlu.js +0 -1
  162. package/payload/server/public/assets/wardley-RL74JXVD-Bv4md4b3.js +0 -1
  163. /package/payload/server/public/assets/{_baseFor-BHtDrjIo.js → _baseFor-Cam2PbSt.js} +0 -0
  164. /package/payload/server/public/assets/{array-DetWRiSa.js → array-DYRGGQae.js} +0 -0
  165. /package/payload/server/public/assets/{cytoscape.esm-C9yNhe1u.js → cytoscape.esm-nWsJMTNI.js} +0 -0
  166. /package/payload/server/public/assets/{defaultLocale-_WRwicXn.js → defaultLocale-Du1XY3Dp.js} +0 -0
  167. /package/payload/server/public/assets/{dist-Bd4S37oi.js → dist-BzAsli7o.js} +0 -0
  168. /package/payload/server/public/assets/{init-sTEcj9YX.js → init-B5BXBRcm.js} +0 -0
  169. /package/payload/server/public/assets/{katex-s61Rgv6l.js → katex-HOUACuRw.js} +0 -0
  170. /package/payload/server/public/assets/{path-B0Ik7Tu9.js → path-CNO468J-.js} +0 -0
  171. /package/payload/server/public/assets/{rough.esm-DKRO8IF-.js → rough.esm-DRO6hWPh.js} +0 -0
  172. /package/payload/server/public/assets/{src-B6XdH6xq.js → src-CWiyyVfn.js} +0 -0
@@ -110,7 +110,7 @@ test("DARWIN_ALIASES contains nodejs → node@22 and is the only entry", () => {
110
110
  // ---------------------------------------------------------------------------
111
111
  // Mirrors `DARWIN_BASE_DEPS` in packages/create-maxy-code/src/index.ts. Every
112
112
  // entry must be either a real brew formula (pass-through) or an alias.
113
- const DARWIN_BASE_DEPS_FIXTURE = ["curl", "git", "unzip", "jq", "poppler", "ffmpeg"];
113
+ const DARWIN_BASE_DEPS_FIXTURE = ["curl", "git", "unzip", "jq", "poppler", "ffmpeg", "tesseract", "ocrmypdf"];
114
114
  // `neo4j` and `cloudflared` are passed to installAllBrewPackages from
115
115
  // installNeo4j (index.ts:1265) and installCloudflared (index.ts:1728). Both are
116
116
  // concrete brew formula names — no alias hop expected.
@@ -34,7 +34,7 @@ test("installSystemDeps darwin branch installs DARWIN_BASE_DEPS via brew", () =>
34
34
  // The shared dep set the darwin path feeds to installAllBrewPackages. If
35
35
  // this list shrinks or a name changes, brew-resolve.test.ts (DARWIN_BASE_DEPS_FIXTURE)
36
36
  // must move in lockstep — the comment in brew-resolve.test.ts says so.
37
- assert.match(SRC, /DARWIN_BASE_DEPS\s*=\s*\[\s*"curl"\s*,\s*"git"\s*,\s*"unzip"\s*,\s*"jq"\s*,\s*"poppler"\s*,\s*"ffmpeg"\s*\]/);
37
+ assert.match(SRC, /DARWIN_BASE_DEPS\s*=\s*\[\s*"curl"\s*,\s*"git"\s*,\s*"unzip"\s*,\s*"jq"\s*,\s*"poppler"\s*,\s*"ffmpeg"\s*,\s*"tesseract"\s*,\s*"ocrmypdf"\s*\]/);
38
38
  assert.match(SRC, /installAllBrewPackages\(DARWIN_BASE_DEPS,\s*logFile\)/);
39
39
  });
40
40
  test("installCloudflared darwin branch logs the documented skip and brew-installs cloudflared", () => {
package/dist/index.js CHANGED
@@ -649,11 +649,11 @@ function installSystemDeps() {
649
649
  // out-of-scope note). mDNS is provided by the OS, so avahi-* drop out.
650
650
  // Translate the remaining apt names through decideBrewResolution and
651
651
  // let installAllBrewPackages handle the install + verify pattern.
652
- const DARWIN_BASE_DEPS = ["curl", "git", "unzip", "jq", "poppler", "ffmpeg"];
652
+ const DARWIN_BASE_DEPS = ["curl", "git", "unzip", "jq", "poppler", "ffmpeg", "tesseract", "ocrmypdf"];
653
653
  installAllBrewPackages(DARWIN_BASE_DEPS, logFile);
654
654
  return;
655
655
  }
656
- const BASE_DEPS = ["curl", "git", "unzip", "jq", "avahi-daemon", "avahi-utils", "poppler-utils", "ffmpeg", "bind9-dnsutils"];
656
+ const BASE_DEPS = ["curl", "git", "unzip", "jq", "avahi-daemon", "avahi-utils", "poppler-utils", "ffmpeg", "bind9-dnsutils", "tesseract-ocr", "ocrmypdf"];
657
657
  // xterm is the *preferred* terminal-emulator binary for the VNC-rendered
658
658
  // Terminal surface
659
659
  // delegates window creation to the session's gnome-terminal-server,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.191",
3
+ "version": "0.1.193",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -20,6 +20,41 @@
20
20
  */
21
21
  /** Wire-format metadata key the Claude Code SDK reads. */
22
22
  export declare const ALWAYS_LOAD_META_KEY: "anthropic/alwaysLoad";
23
+ /**
24
+ * Admin-seat detection. The rc-daemon exports `MAXY_SESSION_ROLE=admin` and
25
+ * `MAXY_SPECIALIST=""` for the orchestrator process and its MCP plugin
26
+ * children; specialist spawns set `MAXY_SPECIALIST=<name>`. A plugin process
27
+ * inheriting the env can therefore decide at registration AND invocation
28
+ * time whether it is loaded inside the admin seat.
29
+ *
30
+ * Mirrors `isAdminSeat` in platform/plugins/admin/mcp/src/skill-resolution.ts
31
+ * — this is a duplicate at the library layer so plugins that cannot import
32
+ * from the admin plugin (a sibling, not a dependency) share the same rule.
33
+ */
34
+ export declare function isAdminSeat(env?: NodeJS.ProcessEnv): boolean;
35
+ /**
36
+ * MCP refusal envelope returned when the admin seat invokes a specialist-
37
+ * owned tool. The text names the dispatch route so the model's next action
38
+ * is the `Agent` dispatch rather than a dead end.
39
+ */
40
+ export declare function adminBlockedResponse(toolName: string, owner: string): {
41
+ content: {
42
+ type: "text";
43
+ text: string;
44
+ }[];
45
+ isError: true;
46
+ };
47
+ /**
48
+ * Wrap a tool handler so any admin-seat invocation short-circuits with the
49
+ * refusal envelope above and emits one `[admin-tool-gate]` line to stderr.
50
+ *
51
+ * Specialist seats pass through unchanged: this is the primitive Task 529
52
+ * defines for closing the in-message authoring path that Task 516 could
53
+ * not reach. The handler signature is preserved so existing call sites
54
+ * substitute `eagerTool(... handler)` for `eagerTool(... withAdminGate(name,
55
+ * owner, handler))` mechanically.
56
+ */
57
+ export declare function withAdminGate<H extends (...args: any[]) => any>(toolName: string, owner: string, handler: H): H;
23
58
  /**
24
59
  * Loose structural type for any object exposing a `registerTool` method. The
25
60
  * MCP SDK ships dual ESM/CJS packages with conditional exports, so importing
@@ -57,5 +92,25 @@ type LocalZodRawShape = Record<string, unknown>;
57
92
  * the consumer's own zod version) infers the handler args.
58
93
  */
59
94
  export declare function eagerTool<Args extends LocalZodRawShape>(server: EagerCapableServer, name: string, description: string, inputSchema: Args, handler: (...args: any[]) => any): void;
95
+ /**
96
+ * Loose structural type for the legacy four-arg `server.tool(name, desc,
97
+ * schema, handler)` call shape — used when the admin seat must register a
98
+ * specialist-owned tool as deferred (not eager) so it does not appear in
99
+ * the admin's eager tool list.
100
+ */
101
+ type DeferredCapableServer = {
102
+ tool: (name: string, description: string, inputSchema: any, handler: (...args: any[]) => any) => any;
103
+ };
104
+ /**
105
+ * Register a tool that is eager for specialists but deferred + admin-gated
106
+ * for the admin seat. The owning specialist name is named here so the
107
+ * refusal envelope (and the `[admin-tool-gate]` log line) carries it.
108
+ *
109
+ * Use for every tool whose owner is a specialist (database-operator, writer-
110
+ * craft, research-assistant, personal-assistant, content-producer). The
111
+ * admin seat sees the tool only via ToolSearch — and any invocation from the
112
+ * admin seat refuses with the dispatch hint.
113
+ */
114
+ export declare function eagerToolGated<Args extends LocalZodRawShape>(server: EagerCapableServer & DeferredCapableServer, name: string, owner: string, description: string, inputSchema: Args, handler: (...args: any[]) => any): void;
60
115
  export {};
61
116
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,EAAG,sBAA+B,CAAC;AAEpE;;;;;;;GAOG;AAEH,KAAK,kBAAkB,GAAG;IAAE,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAA;CAAE,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,IAAI,SAAS,gBAAgB,EACrD,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,IAAI,EAEjB,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAC/B,IAAI,CAUN"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,EAAG,sBAA+B,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEzE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;IACrE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC1C,OAAO,EAAE,IAAI,CAAC;CACf,CAYA;AAED;;;;;;;;;GASG;AAEH,wBAAgB,aAAa,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAC7D,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,GACT,CAAC,CAYH;AAED;;;;;;;GAOG;AAEH,KAAK,kBAAkB,GAAG;IAAE,YAAY,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAA;CAAE,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,IAAI,SAAS,gBAAgB,EACrD,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,IAAI,EAEjB,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAC/B,IAAI,CAUN;AAED;;;;;GAKG;AAEH,KAAK,qBAAqB,GAAG;IAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,KAAK,GAAG,CAAA;CAAE,CAAC;AAEtI;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,SAAS,gBAAgB,EAC1D,MAAM,EAAE,kBAAkB,GAAG,qBAAqB,EAClD,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,IAAI,EAEjB,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAC/B,IAAI,CAMN"}
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ALWAYS_LOAD_META_KEY = void 0;
4
+ exports.isAdminSeat = isAdminSeat;
5
+ exports.adminBlockedResponse = adminBlockedResponse;
6
+ exports.withAdminGate = withAdminGate;
4
7
  exports.eagerTool = eagerTool;
8
+ exports.eagerToolGated = eagerToolGated;
5
9
  /**
6
10
  * MCP eager-load helper.
7
11
  *
@@ -24,6 +28,59 @@ exports.eagerTool = eagerTool;
24
28
  */
25
29
  /** Wire-format metadata key the Claude Code SDK reads. */
26
30
  exports.ALWAYS_LOAD_META_KEY = "anthropic/alwaysLoad";
31
+ /**
32
+ * Admin-seat detection. The rc-daemon exports `MAXY_SESSION_ROLE=admin` and
33
+ * `MAXY_SPECIALIST=""` for the orchestrator process and its MCP plugin
34
+ * children; specialist spawns set `MAXY_SPECIALIST=<name>`. A plugin process
35
+ * inheriting the env can therefore decide at registration AND invocation
36
+ * time whether it is loaded inside the admin seat.
37
+ *
38
+ * Mirrors `isAdminSeat` in platform/plugins/admin/mcp/src/skill-resolution.ts
39
+ * — this is a duplicate at the library layer so plugins that cannot import
40
+ * from the admin plugin (a sibling, not a dependency) share the same rule.
41
+ */
42
+ function isAdminSeat(env = process.env) {
43
+ return env.MAXY_SESSION_ROLE === "admin" && !(env.MAXY_SPECIALIST && env.MAXY_SPECIALIST.length > 0);
44
+ }
45
+ /**
46
+ * MCP refusal envelope returned when the admin seat invokes a specialist-
47
+ * owned tool. The text names the dispatch route so the model's next action
48
+ * is the `Agent` dispatch rather than a dead end.
49
+ */
50
+ function adminBlockedResponse(toolName, owner) {
51
+ return {
52
+ content: [
53
+ {
54
+ type: "text",
55
+ text: `Tool \`${toolName}\` is owned by the \`${owner}\` specialist and is not callable from the admin seat. ` +
56
+ `Dispatch \`${owner}\` with the Agent tool and pass the work as the brief.`,
57
+ },
58
+ ],
59
+ isError: true,
60
+ };
61
+ }
62
+ /**
63
+ * Wrap a tool handler so any admin-seat invocation short-circuits with the
64
+ * refusal envelope above and emits one `[admin-tool-gate]` line to stderr.
65
+ *
66
+ * Specialist seats pass through unchanged: this is the primitive Task 529
67
+ * defines for closing the in-message authoring path that Task 516 could
68
+ * not reach. The handler signature is preserved so existing call sites
69
+ * substitute `eagerTool(... handler)` for `eagerTool(... withAdminGate(name,
70
+ * owner, handler))` mechanically.
71
+ */
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ function withAdminGate(toolName, owner, handler) {
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ const wrapped = ((...args) => {
76
+ if (isAdminSeat(process.env)) {
77
+ process.stderr.write(`[admin-tool-gate] role=admin tool=${toolName} decision=block reason=specialist-owned owner=${owner}\n`);
78
+ return adminBlockedResponse(toolName, owner);
79
+ }
80
+ return handler(...args);
81
+ });
82
+ return wrapped;
83
+ }
27
84
  /**
28
85
  * Register an MCP tool that the Claude Code SDK eager-loads into the
29
86
  * model's prompt at session boot, bypassing the ToolSearch round-trip.
@@ -46,4 +103,23 @@ handler) {
46
103
  _meta: { [exports.ALWAYS_LOAD_META_KEY]: true },
47
104
  }, handler);
48
105
  }
106
+ /**
107
+ * Register a tool that is eager for specialists but deferred + admin-gated
108
+ * for the admin seat. The owning specialist name is named here so the
109
+ * refusal envelope (and the `[admin-tool-gate]` log line) carries it.
110
+ *
111
+ * Use for every tool whose owner is a specialist (database-operator, writer-
112
+ * craft, research-assistant, personal-assistant, content-producer). The
113
+ * admin seat sees the tool only via ToolSearch — and any invocation from the
114
+ * admin seat refuses with the dispatch hint.
115
+ */
116
+ function eagerToolGated(server, name, owner, description, inputSchema,
117
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
118
+ handler) {
119
+ if (isAdminSeat(process.env)) {
120
+ server.tool(name, description, inputSchema, withAdminGate(name, owner, handler));
121
+ return;
122
+ }
123
+ eagerTool(server, name, description, inputSchema, handler);
124
+ }
49
125
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AA4DA,8BAiBC;AA7ED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,0DAA0D;AAC7C,QAAA,oBAAoB,GAAG,sBAA+B,CAAC;AA0BpE;;;;;;;;;;;;GAYG;AACH,SAAgB,SAAS,CACvB,MAA0B,EAC1B,IAAY,EACZ,WAAmB,EACnB,WAAiB;AACjB,8DAA8D;AAC9D,OAAgC;IAEhC,MAAM,CAAC,YAAY,CACjB,IAAI,EACJ;QACE,WAAW;QACX,WAAW;QACX,KAAK,EAAE,EAAE,CAAC,4BAAoB,CAAC,EAAE,IAAI,EAAE;KACxC,EACD,OAAO,CACR,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAkCA,kCAEC;AAOD,oDAeC;AAaD,sCAgBC;AAuCD,8BAiBC;AAqBD,wCAcC;AAlLD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,0DAA0D;AAC7C,QAAA,oBAAoB,GAAG,sBAA+B,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,SAAgB,WAAW,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC9D,OAAO,GAAG,CAAC,iBAAiB,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACvG,CAAC;AAED;;;;GAIG;AACH,SAAgB,oBAAoB,CAAC,QAAgB,EAAE,KAAa;IAIlE,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EACF,UAAU,QAAQ,wBAAwB,KAAK,yDAAyD;oBACxG,cAAc,KAAK,wDAAwD;aAC9E;SACF;QACD,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,8DAA8D;AAC9D,SAAgB,aAAa,CAC3B,QAAgB,EAChB,KAAa,EACb,OAAU;IAEV,8DAA8D;IAC9D,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG,IAAW,EAAE,EAAE;QAClC,IAAI,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qCAAqC,QAAQ,iDAAiD,KAAK,IAAI,CACxG,CAAC;YACF,OAAO,oBAAoB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAiB,CAAC;IACnB,OAAO,OAAO,CAAC;AACjB,CAAC;AA0BD;;;;;;;;;;;;GAYG;AACH,SAAgB,SAAS,CACvB,MAA0B,EAC1B,IAAY,EACZ,WAAmB,EACnB,WAAiB;AACjB,8DAA8D;AAC9D,OAAgC;IAEhC,MAAM,CAAC,YAAY,CACjB,IAAI,EACJ;QACE,WAAW;QACX,WAAW;QACX,KAAK,EAAE,EAAE,CAAC,4BAAoB,CAAC,EAAE,IAAI,EAAE;KACxC,EACD,OAAO,CACR,CAAC;AACJ,CAAC;AAWD;;;;;;;;;GASG;AACH,SAAgB,cAAc,CAC5B,MAAkD,EAClD,IAAY,EACZ,KAAa,EACb,WAAmB,EACnB,WAAiB;AACjB,8DAA8D;AAC9D,OAAgC;IAEhC,IAAI,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QACjF,OAAO;IACT,CAAC;IACD,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "@maxy/mcp-eager",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "commonjs",
6
+ "main": "dist/index.js"
7
+ }
@@ -21,6 +21,72 @@
21
21
  /** Wire-format metadata key the Claude Code SDK reads. */
22
22
  export const ALWAYS_LOAD_META_KEY = "anthropic/alwaysLoad" as const;
23
23
 
24
+ /**
25
+ * Admin-seat detection. The rc-daemon exports `MAXY_SESSION_ROLE=admin` and
26
+ * `MAXY_SPECIALIST=""` for the orchestrator process and its MCP plugin
27
+ * children; specialist spawns set `MAXY_SPECIALIST=<name>`. A plugin process
28
+ * inheriting the env can therefore decide at registration AND invocation
29
+ * time whether it is loaded inside the admin seat.
30
+ *
31
+ * Mirrors `isAdminSeat` in platform/plugins/admin/mcp/src/skill-resolution.ts
32
+ * — this is a duplicate at the library layer so plugins that cannot import
33
+ * from the admin plugin (a sibling, not a dependency) share the same rule.
34
+ */
35
+ export function isAdminSeat(env: NodeJS.ProcessEnv = process.env): boolean {
36
+ return env.MAXY_SESSION_ROLE === "admin" && !(env.MAXY_SPECIALIST && env.MAXY_SPECIALIST.length > 0);
37
+ }
38
+
39
+ /**
40
+ * MCP refusal envelope returned when the admin seat invokes a specialist-
41
+ * owned tool. The text names the dispatch route so the model's next action
42
+ * is the `Agent` dispatch rather than a dead end.
43
+ */
44
+ export function adminBlockedResponse(toolName: string, owner: string): {
45
+ content: { type: "text"; text: string }[];
46
+ isError: true;
47
+ } {
48
+ return {
49
+ content: [
50
+ {
51
+ type: "text",
52
+ text:
53
+ `Tool \`${toolName}\` is owned by the \`${owner}\` specialist and is not callable from the admin seat. ` +
54
+ `Dispatch \`${owner}\` with the Agent tool and pass the work as the brief.`,
55
+ },
56
+ ],
57
+ isError: true,
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Wrap a tool handler so any admin-seat invocation short-circuits with the
63
+ * refusal envelope above and emits one `[admin-tool-gate]` line to stderr.
64
+ *
65
+ * Specialist seats pass through unchanged: this is the primitive Task 529
66
+ * defines for closing the in-message authoring path that Task 516 could
67
+ * not reach. The handler signature is preserved so existing call sites
68
+ * substitute `eagerTool(... handler)` for `eagerTool(... withAdminGate(name,
69
+ * owner, handler))` mechanically.
70
+ */
71
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
+ export function withAdminGate<H extends (...args: any[]) => any>(
73
+ toolName: string,
74
+ owner: string,
75
+ handler: H,
76
+ ): H {
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ const wrapped = ((...args: any[]) => {
79
+ if (isAdminSeat(process.env)) {
80
+ process.stderr.write(
81
+ `[admin-tool-gate] role=admin tool=${toolName} decision=block reason=specialist-owned owner=${owner}\n`,
82
+ );
83
+ return adminBlockedResponse(toolName, owner);
84
+ }
85
+ return handler(...args);
86
+ }) as unknown as H;
87
+ return wrapped;
88
+ }
89
+
24
90
  /**
25
91
  * Loose structural type for any object exposing a `registerTool` method. The
26
92
  * MCP SDK ships dual ESM/CJS packages with conditional exports, so importing
@@ -76,3 +142,38 @@ export function eagerTool<Args extends LocalZodRawShape>(
76
142
  handler,
77
143
  );
78
144
  }
145
+
146
+ /**
147
+ * Loose structural type for the legacy four-arg `server.tool(name, desc,
148
+ * schema, handler)` call shape — used when the admin seat must register a
149
+ * specialist-owned tool as deferred (not eager) so it does not appear in
150
+ * the admin's eager tool list.
151
+ */
152
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
153
+ type DeferredCapableServer = { tool: (name: string, description: string, inputSchema: any, handler: (...args: any[]) => any) => any };
154
+
155
+ /**
156
+ * Register a tool that is eager for specialists but deferred + admin-gated
157
+ * for the admin seat. The owning specialist name is named here so the
158
+ * refusal envelope (and the `[admin-tool-gate]` log line) carries it.
159
+ *
160
+ * Use for every tool whose owner is a specialist (database-operator, writer-
161
+ * craft, research-assistant, personal-assistant, content-producer). The
162
+ * admin seat sees the tool only via ToolSearch — and any invocation from the
163
+ * admin seat refuses with the dispatch hint.
164
+ */
165
+ export function eagerToolGated<Args extends LocalZodRawShape>(
166
+ server: EagerCapableServer & DeferredCapableServer,
167
+ name: string,
168
+ owner: string,
169
+ description: string,
170
+ inputSchema: Args,
171
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
172
+ handler: (...args: any[]) => any,
173
+ ): void {
174
+ if (isAdminSeat(process.env)) {
175
+ server.tool(name, description, inputSchema, withAdminGate(name, owner, handler));
176
+ return;
177
+ }
178
+ eagerTool(server, name, description, inputSchema, handler);
179
+ }
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env bash
2
+ # Task 529 — admin-tool-gate enforces orchestration-only at the PreToolUse
3
+ # hook. Admin-direct invocations of Write/Edit/MultiEdit/Bash/WebFetch/
4
+ # WebSearch are refused with the dispatch hint; in-window Task subagents
5
+ # (transcript_path matching `*/subagents/agent-*.jsonl`) pass through
6
+ # (Task 222 propagation regression class — subagents must keep their full
7
+ # tool surface). Missing transcript_path is fail-closed (matches Task 222
8
+ # contract).
9
+
10
+ set -u
11
+
12
+ HOOK="$(cd "$(dirname "$0")/.." && pwd)/pre-tool-use.sh"
13
+ if [[ ! -x "$HOOK" ]]; then
14
+ echo "FAIL: $HOOK not executable" >&2
15
+ exit 1
16
+ fi
17
+
18
+ TMPFILES=()
19
+ cleanup() {
20
+ for f in "${TMPFILES[@]:-}"; do
21
+ [[ -n "$f" ]] && rm -f "$f" 2>/dev/null || true
22
+ done
23
+ }
24
+ trap cleanup EXIT
25
+
26
+ PASS=0; FAIL=0
27
+ pass() { PASS=$((PASS+1)); echo "PASS: $1"; }
28
+ fail() { FAIL=$((FAIL+1)); echo "FAIL: $1" >&2; }
29
+
30
+ ADMIN_TRANSCRIPT="/tmp/sess/admin-3104d4b4.jsonl"
31
+ SUBAGENT_TRANSCRIPT="/tmp/sess/subagents/agent-content-producer-1.jsonl"
32
+
33
+ run() {
34
+ local tool="$1" transcript="$2" expected_rc="$3" expected_pattern="$4" label="$5"
35
+ local input_json
36
+ if [[ -n "$transcript" ]]; then
37
+ input_json=$(python3 -c '
38
+ import json, sys
39
+ print(json.dumps({"hook_event_name":"PreToolUse","tool_name":sys.argv[1],"tool_input":{"file_path":"/tmp/x","content":"x"},"transcript_path":sys.argv[2]}, separators=(",", ":")))
40
+ ' "$tool" "$transcript")
41
+ else
42
+ input_json=$(python3 -c '
43
+ import json, sys
44
+ print(json.dumps({"hook_event_name":"PreToolUse","tool_name":sys.argv[1],"tool_input":{"file_path":"/tmp/x","content":"x"}}, separators=(",", ":")))
45
+ ' "$tool")
46
+ fi
47
+ local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
48
+ local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
49
+ printf '%s' "$input_json" | bash "$HOOK" admin >"$stdout_file" 2>"$stderr_file"
50
+ local rc=$?
51
+ if [[ "$rc" -ne "$expected_rc" ]]; then
52
+ fail "$label: expected exit $expected_rc, got $rc. Stderr: $(cat "$stderr_file")"
53
+ return
54
+ fi
55
+ if [[ -n "$expected_pattern" ]] && ! grep -qE "$expected_pattern" "$stderr_file"; then
56
+ fail "$label: stderr missing pattern '$expected_pattern'. Got: $(cat "$stderr_file")"
57
+ return
58
+ fi
59
+ pass "$label"
60
+ }
61
+
62
+ # ── Admin-direct refusals ───────────────────────────────────────────────
63
+ run Write "$ADMIN_TRANSCRIPT" 2 '\[admin-tool-gate\] role=admin tool=Write decision=block.*owner=content-producer' \
64
+ "Test 1: admin-direct Write rejected with content-producer dispatch hint"
65
+ run Edit "$ADMIN_TRANSCRIPT" 2 '\[admin-tool-gate\] role=admin tool=Edit decision=block.*owner=content-producer' \
66
+ "Test 2: admin-direct Edit rejected"
67
+ run MultiEdit "$ADMIN_TRANSCRIPT" 2 '\[admin-tool-gate\] role=admin tool=MultiEdit decision=block.*owner=content-producer' \
68
+ "Test 3: admin-direct MultiEdit rejected"
69
+ run Bash "$ADMIN_TRANSCRIPT" 2 '\[admin-tool-gate\] role=admin tool=Bash decision=block' \
70
+ "Test 4: admin-direct Bash rejected"
71
+ run WebFetch "$ADMIN_TRANSCRIPT" 2 '\[admin-tool-gate\] role=admin tool=WebFetch decision=block.*owner=research-assistant' \
72
+ "Test 5: admin-direct WebFetch rejected"
73
+ run WebSearch "$ADMIN_TRANSCRIPT" 2 '\[admin-tool-gate\] role=admin tool=WebSearch decision=block.*owner=research-assistant' \
74
+ "Test 6: admin-direct WebSearch rejected"
75
+
76
+ # ── In-window subagent passthrough (Task 222 regression class) ──────────
77
+ run Write "$SUBAGENT_TRANSCRIPT" 0 '\[admin-tool-gate\] role=subagent tool=Write decision=allow' \
78
+ "Test 7: subagent Write passes through"
79
+ run Edit "$SUBAGENT_TRANSCRIPT" 0 '\[admin-tool-gate\] role=subagent tool=Edit decision=allow' \
80
+ "Test 8: subagent Edit passes through"
81
+ run Bash "$SUBAGENT_TRANSCRIPT" 0 '\[admin-tool-gate\] role=subagent tool=Bash decision=allow' \
82
+ "Test 9: subagent Bash passes through"
83
+ run WebFetch "$SUBAGENT_TRANSCRIPT" 0 '\[admin-tool-gate\] role=subagent tool=WebFetch decision=allow' \
84
+ "Test 10: subagent WebFetch passes through"
85
+
86
+ # ── Missing transcript_path is fail-closed (Task 222 contract) ──────────
87
+ run Write "" 2 '\[admin-tool-gate\] role=unknown tool=Write decision=block reason=missing-transcript-path' \
88
+ "Test 11: missing transcript_path on Write fails closed"
89
+ run Bash "" 2 '\[admin-tool-gate\] role=unknown tool=Bash decision=block reason=missing-transcript-path' \
90
+ "Test 12: missing transcript_path on Bash fails closed"
91
+
92
+ # ── Unrelated tools untouched ───────────────────────────────────────────
93
+ run Read "$ADMIN_TRANSCRIPT" 0 '' \
94
+ "Test 13: admin-direct Read passes (Read is admin-keep-list)"
95
+
96
+ echo
97
+ echo "──────── pre-tool-use admin-tool-gate test summary ────────"
98
+ echo "PASS: $PASS"
99
+ echo "FAIL: $FAIL"
100
+
101
+ [[ "$FAIL" -gt 0 ]] && exit 1
102
+ exit 0
@@ -41,7 +41,7 @@ run_bash() {
41
41
  # Build the input JSON via python3 so command_text with quotes / specials is safe.
42
42
  input_json=$(python3 -c '
43
43
  import json, sys
44
- print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Bash", "tool_input": {"command": sys.argv[1]}}, separators=(",", ":")))
44
+ print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Bash", "tool_input": {"command": sys.argv[1]}, "transcript_path": "/tmp/sess/subagents/agent-x.jsonl"}, separators=(",", ":")))
45
45
  ' "$command_text")
46
46
  local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
47
47
  local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
@@ -64,7 +64,7 @@ run_write() {
64
64
  local input_json
65
65
  input_json=$(python3 -c '
66
66
  import json, sys
67
- print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Write", "tool_input": {"file_path": "/tmp/test.html", "content": sys.argv[1]}}, separators=(",", ":")))
67
+ print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Write", "tool_input": {"file_path": "/tmp/test.html", "content": sys.argv[1]}, "transcript_path": "/tmp/sess/subagents/agent-x.jsonl"}, separators=(",", ":")))
68
68
  ' "$content")
69
69
  local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
70
70
  local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
@@ -87,7 +87,7 @@ run_edit() {
87
87
  local input_json
88
88
  input_json=$(python3 -c '
89
89
  import json, sys
90
- print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Edit", "tool_input": {"file_path": "/tmp/test.html", "old_string": "OLD", "new_string": sys.argv[1]}}, separators=(",", ":")))
90
+ print(json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Edit", "tool_input": {"file_path": "/tmp/test.html", "old_string": "OLD", "new_string": sys.argv[1]}, "transcript_path": "/tmp/sess/subagents/agent-x.jsonl"}, separators=(",", ":")))
91
91
  ' "$new_string")
92
92
  local stdout_file; stdout_file=$(mktemp); TMPFILES+=("$stdout_file")
93
93
  local stderr_file; stderr_file=$(mktemp); TMPFILES+=("$stderr_file")
@@ -185,7 +185,7 @@ else
185
185
  fi
186
186
 
187
187
  # Pre-existing guards still active — entitlement file edit still rejected.
188
- ENT_JSON=$(python3 -c 'import json; print(json.dumps({"hook_event_name":"PreToolUse","tool_name":"Write","tool_input":{"file_path":"/srv/entitlement.json","content":"{\"tier\":\"max\"}"}}, separators=(",", ":")))')
188
+ ENT_JSON=$(python3 -c 'import json; print(json.dumps({"hook_event_name":"PreToolUse","tool_name":"Write","tool_input":{"file_path":"/srv/entitlement.json","content":"{\"tier\":\"max\"}"},"transcript_path":"/tmp/sess/subagents/agent-x.jsonl"}, separators=(",", ":")))')
189
189
  STDOUT_FILE=$(mktemp); STDERR_FILE=$(mktemp); TMPFILES+=("$STDOUT_FILE" "$STDERR_FILE")
190
190
  printf '%s' "$ENT_JSON" | bash "$HOOK" admin >"$STDOUT_FILE" 2>"$STDERR_FILE"
191
191
  RC=$?
@@ -21,6 +21,62 @@ TOOL_NAME=$(echo "$INPUT" | grep -o '"tool_name":"[^"]*"' | head -1 | cut -d'"'
21
21
  # ---------------------------------------------------------------------------
22
22
  if [ "$AGENT_TYPE" = "admin" ]; then
23
23
 
24
+ # ── Orchestration-only authoring + research block (Task 529) ─────────────
25
+ # The admin seat is orchestration + clarification + delivery only.
26
+ # Write/Edit/MultiEdit/Bash/WebFetch/WebSearch are specialist-owned —
27
+ # content-producer for authoring, research-assistant for research, the
28
+ # shell tools for whichever specialist owns the workflow that needs them.
29
+ # In-window Task subagents must keep these tools (Task 222 regression
30
+ # class) — the discriminator is the transcript_path: Claude Code stores
31
+ # subagent turns in separate `*/subagents/agent-*.jsonl` files.
32
+ #
33
+ # Admin direct → exit 2 with dispatch hint.
34
+ # Subagent → exit 0 (passthrough).
35
+ # Missing/malformed transcript_path → fail-closed block (matches Task 222).
36
+ case "$TOOL_NAME" in
37
+ Write|Edit|MultiEdit|Bash|WebFetch|WebSearch)
38
+ TRANSCRIPT_PATH=$(echo "$INPUT" | python3 -c '
39
+ import sys, json
40
+ try:
41
+ print(json.load(sys.stdin).get("transcript_path", "") or "")
42
+ except Exception:
43
+ print("")
44
+ ' 2>/dev/null)
45
+ case "$TRANSCRIPT_PATH" in
46
+ */subagents/agent-*.jsonl)
47
+ # In-window subagent — passthrough. Tool name resolves on the
48
+ # subagent's own frontmatter `tools:` allowlist downstream.
49
+ echo "[admin-tool-gate] role=subagent tool=${TOOL_NAME} decision=allow source=hook-passthrough" >&2
50
+ ;;
51
+ "")
52
+ # Missing transcript_path on a denied native tool — fail closed
53
+ # rather than guess (Task 222 fail-closed contract).
54
+ echo "[admin-tool-gate] role=unknown tool=${TOOL_NAME} decision=block reason=missing-transcript-path" >&2
55
+ echo "Blocked: ${TOOL_NAME} is unavailable from the admin seat and the caller cannot be classified (no transcript_path). Failing closed." >&2
56
+ exit 2
57
+ ;;
58
+ *)
59
+ # Admin direct invocation — refuse with dispatch hint.
60
+ case "$TOOL_NAME" in
61
+ Write|Edit|MultiEdit)
62
+ OWNER="content-producer"
63
+ ;;
64
+ Bash)
65
+ OWNER="the specialist that owns the workflow needing shell access"
66
+ ;;
67
+ WebFetch|WebSearch)
68
+ OWNER="research-assistant"
69
+ ;;
70
+ esac
71
+ echo "[admin-tool-gate] role=admin tool=${TOOL_NAME} decision=block reason=orchestration-only owner=${OWNER}" >&2
72
+ echo "Blocked: \`${TOOL_NAME}\` is not callable from the admin seat. The admin seat is orchestration + clarification + delivery only — authoring, shell work, and web research are owned by specialists." >&2
73
+ echo "Dispatch the work to ${OWNER} via the Agent tool." >&2
74
+ exit 2
75
+ ;;
76
+ esac
77
+ ;;
78
+ esac
79
+
24
80
  # ── Code directory protection ────────────────────────────────────────────
25
81
  case "$TOOL_NAME" in
26
82
  Write|Edit)