@rubytech/create-maxy-code 0.1.246 → 0.1.248

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 (168) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/config/brand.json +3 -3
  3. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  4. package/payload/platform/plugins/admin/PLUGIN.md +4 -1
  5. package/payload/platform/plugins/admin/hooks/__tests__/pin-identity-gate.test.sh +96 -0
  6. package/payload/platform/plugins/admin/hooks/pin-identity-gate.sh +136 -0
  7. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.d.ts +2 -0
  8. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.d.ts.map +1 -0
  9. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.js +34 -0
  10. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-identity-authenticate.test.js.map +1 -0
  11. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.d.ts +2 -0
  12. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.d.ts.map +1 -0
  13. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.js +50 -0
  14. package/payload/platform/plugins/admin/mcp/dist/__tests__/skill-no-prescribed-role.test.js.map +1 -0
  15. package/payload/platform/plugins/admin/mcp/dist/index.js +8 -0
  16. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  17. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.d.ts +6 -0
  18. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.d.ts.map +1 -0
  19. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.js +32 -0
  20. package/payload/platform/plugins/admin/mcp/dist/tools/admin-identity-authenticate.js.map +1 -0
  21. package/payload/platform/plugins/admin/skills/investigate/SKILL.md +34 -17
  22. package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +15 -8
  23. package/payload/platform/plugins/admin/skills/publish-site/SKILL.md +5 -0
  24. package/payload/platform/plugins/admin/skills/unzip-attachment/SKILL.md +5 -0
  25. package/payload/platform/plugins/admin/skills/update-knowledge/SKILL.md +5 -0
  26. package/payload/platform/plugins/docs/references/admin-identity-gate.md +81 -0
  27. package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +1 -0
  28. package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +5 -0
  29. package/payload/platform/plugins/workflows/skills/workflow-manager/SKILL.md +5 -0
  30. package/payload/platform/scripts/check-skill-frontmatter.mjs +139 -0
  31. package/payload/platform/scripts/setup-account.sh +5 -16
  32. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.d.ts +39 -0
  33. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.d.ts.map +1 -0
  34. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.js +133 -0
  35. package/payload/platform/services/claude-session-manager/dist/admin-identity-audit.js.map +1 -0
  36. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  37. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
  38. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  39. package/payload/platform/services/claude-session-manager/dist/index.js +29 -0
  40. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  41. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts +28 -0
  42. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -0
  43. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +99 -0
  44. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -0
  45. package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
  46. package/payload/server/{chunk-JMEX5NRX.js → chunk-UEIA5YUG.js} +30 -18
  47. package/payload/server/maxy-edge.js +7 -5
  48. package/payload/server/public/assets/{AdminShell-D_eg2QKE.js → AdminShell-892Jy_rs.js} +1 -1
  49. package/payload/server/public/assets/AdminShell-qc_xy7Az.css +1 -0
  50. package/payload/server/public/assets/{Checkbox-CfE7p1Tv.js → Checkbox-Bc2QzX9b.js} +1 -1
  51. package/payload/server/public/assets/{admin-DfeXRW1m.js → admin-D3K13ndi.js} +1 -1
  52. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-DvDVmNbJ.js → architectureDiagram-Q4EWVU46-c09loTER.js} +1 -1
  53. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-CVCQHVcw.js → blockDiagram-DXYQGD6D-Cjdeyoq1.js} +1 -1
  54. package/payload/server/public/assets/brand-D0gNihp7.css +1 -0
  55. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-DancbaY-.js → c4Diagram-AHTNJAMY-NY6Wlzo2.js} +1 -1
  56. package/payload/server/public/assets/channel-B1IT7to2.js +1 -0
  57. package/payload/server/public/assets/chunk-336JU56O-CdKRCIeE.js +2 -0
  58. package/payload/server/public/assets/{chunk-426QAEUC-Dqt07sZz.js → chunk-426QAEUC-BybuQ3Ve.js} +1 -1
  59. package/payload/server/public/assets/{chunk-4TB4RGXK-Bdm77ygl.js → chunk-4TB4RGXK-BXpto3yW.js} +1 -1
  60. package/payload/server/public/assets/{chunk-5FUZZQ4R-nDbgOH1b.js → chunk-5FUZZQ4R-C403gCUk.js} +1 -1
  61. package/payload/server/public/assets/{chunk-5PVQY5BW-mYip6Ias.js → chunk-5PVQY5BW-CjVzXQEp.js} +1 -1
  62. package/payload/server/public/assets/{chunk-EDXVE4YY-DubyAY7F.js → chunk-EDXVE4YY-DJcJAsAg.js} +1 -1
  63. package/payload/server/public/assets/{chunk-ENJZ2VHE-DIa8CJbF.js → chunk-ENJZ2VHE-CFDNvYu1.js} +1 -1
  64. package/payload/server/public/assets/{chunk-ICPOFSXX-bKmK-tZo.js → chunk-ICPOFSXX-ecLOxGhL.js} +1 -1
  65. package/payload/server/public/assets/{chunk-OYMX7WX6-KcUQTwd8.js → chunk-OYMX7WX6-B7MW66KB.js} +1 -1
  66. package/payload/server/public/assets/{chunk-U2HBQHQK-3p-zDDFG.js → chunk-U2HBQHQK-BMawmsyk.js} +1 -1
  67. package/payload/server/public/assets/{chunk-X2U36JSP-C5F60GoO.js → chunk-X2U36JSP-CT6g7pno.js} +1 -1
  68. package/payload/server/public/assets/{chunk-YZCP3GAM-CEhP6XMC.js → chunk-YZCP3GAM-xeAluiAH.js} +1 -1
  69. package/payload/server/public/assets/{chunk-ZZ45TVLE-Bac24FOS.js → chunk-ZZ45TVLE-BRN9qUC5.js} +1 -1
  70. package/payload/server/public/assets/classDiagram-6PBFFD2Q-rjCize6i.js +1 -0
  71. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BORWOUt0.js +1 -0
  72. package/payload/server/public/assets/clone-Csqv5U6T.js +1 -0
  73. package/payload/server/public/assets/{dagre-xntZVDsO.js → dagre-DTjePoco.js} +1 -1
  74. package/payload/server/public/assets/{dagre-KV5264BT-DqGsPaYJ.js → dagre-KV5264BT-DHBkRke4.js} +1 -1
  75. package/payload/server/public/assets/{data-JoiWCmz6.js → data-Br-pdljK.js} +1 -1
  76. package/payload/server/public/assets/{diagram-5BDNPKRD-Co8NC28s.js → diagram-5BDNPKRD-BIq1-idL.js} +1 -1
  77. package/payload/server/public/assets/{diagram-G4DWMVQ6-HewQArEf.js → diagram-G4DWMVQ6-BsIUDzV6.js} +1 -1
  78. package/payload/server/public/assets/{diagram-MMDJMWI5-s6NqURmM.js → diagram-MMDJMWI5-CgHSri2i.js} +1 -1
  79. package/payload/server/public/assets/{diagram-TYMM5635-CAof9UmL.js → diagram-TYMM5635-Ce2Wh9ZX.js} +1 -1
  80. package/payload/server/public/assets/{erDiagram-SMLLAGMA-CUqIICFD.js → erDiagram-SMLLAGMA-BU0Kh6OQ.js} +1 -1
  81. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-DN1n7xgU.js → flowDiagram-DWJPFMVM-B0N06MF7.js} +1 -1
  82. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-sGoWsEWZ.js → ganttDiagram-T4ZO3ILL-BVbx4ARZ.js} +1 -1
  83. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-Dz_cZL1Q.js → gitGraphDiagram-UUTBAWPF-C-xRJ94t.js} +1 -1
  84. package/payload/server/public/assets/{graph-DmOvqjTa.js → graph-g48ZcA5M.js} +1 -1
  85. package/payload/server/public/assets/{graph-labels-iPC15OmD.js → graph-labels-BYH-IPCb.js} +1 -1
  86. package/payload/server/public/assets/{graphlib-eZzlPtt9.js → graphlib-YmNcoMjY.js} +1 -1
  87. package/payload/server/public/assets/{infoDiagram-42DDH7IO-DFMOgYMn.js → infoDiagram-42DDH7IO-Di6oPQ_-.js} +1 -1
  88. package/payload/server/public/assets/inter-cyrillic-400-normal-HOLc17fK.woff +0 -0
  89. package/payload/server/public/assets/inter-cyrillic-400-normal-obahsSVq.woff2 +0 -0
  90. package/payload/server/public/assets/inter-cyrillic-500-normal-BasfLYem.woff2 +0 -0
  91. package/payload/server/public/assets/inter-cyrillic-500-normal-CxZf_p3X.woff +0 -0
  92. package/payload/server/public/assets/inter-cyrillic-ext-400-normal-BQZuk6qB.woff2 +0 -0
  93. package/payload/server/public/assets/inter-cyrillic-ext-400-normal-DQukG94-.woff +0 -0
  94. package/payload/server/public/assets/inter-cyrillic-ext-500-normal-B0yAr1jD.woff2 +0 -0
  95. package/payload/server/public/assets/inter-cyrillic-ext-500-normal-BmqWE9Dz.woff +0 -0
  96. package/payload/server/public/assets/inter-greek-400-normal-B4URO6DV.woff2 +0 -0
  97. package/payload/server/public/assets/inter-greek-400-normal-q2sYcFCs.woff +0 -0
  98. package/payload/server/public/assets/inter-greek-500-normal-BIZE56-Y.woff2 +0 -0
  99. package/payload/server/public/assets/inter-greek-500-normal-Xzm54t5V.woff +0 -0
  100. package/payload/server/public/assets/inter-greek-ext-400-normal-DGGRlc-M.woff2 +0 -0
  101. package/payload/server/public/assets/inter-greek-ext-400-normal-KugGGMne.woff +0 -0
  102. package/payload/server/public/assets/inter-greek-ext-500-normal-2j5mBUwD.woff +0 -0
  103. package/payload/server/public/assets/inter-greek-ext-500-normal-C4iEst2y.woff2 +0 -0
  104. package/payload/server/public/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
  105. package/payload/server/public/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
  106. package/payload/server/public/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
  107. package/payload/server/public/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
  108. package/payload/server/public/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
  109. package/payload/server/public/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
  110. package/payload/server/public/assets/inter-latin-ext-500-normal-BxGbmqWO.woff +0 -0
  111. package/payload/server/public/assets/inter-latin-ext-500-normal-CV4jyFjo.woff2 +0 -0
  112. package/payload/server/public/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
  113. package/payload/server/public/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
  114. package/payload/server/public/assets/inter-vietnamese-500-normal-DOriooB6.woff2 +0 -0
  115. package/payload/server/public/assets/inter-vietnamese-500-normal-mJboJaSs.woff +0 -0
  116. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-C9AGnqZn.js → ishikawaDiagram-UXIWVN3A-DTrq54yC.js} +1 -1
  117. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-C4-y1Faf.js → journeyDiagram-VCZTEJTY-OZZZMrFX.js} +1 -1
  118. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-DIynz-Si.js → kanban-definition-6JOO6SKY--w-IP9pN.js} +1 -1
  119. package/payload/server/public/assets/{line-CzaIC22F.js → line-Ckeulv5T.js} +1 -1
  120. package/payload/server/public/assets/{mermaid-parser.core-Dcdye_D2.js → mermaid-parser.core-CVRAxYRD.js} +1 -1
  121. package/payload/server/public/assets/{mermaid.core-HWxg8X0R.js → mermaid.core-B-mE18I1.js} +3 -3
  122. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-ve0J3SsF.js → mindmap-definition-QFDTVHPH-Bm8mDicL.js} +1 -1
  123. package/payload/server/public/assets/newsreader-latin-300-normal-AOSWdb_s.woff +0 -0
  124. package/payload/server/public/assets/newsreader-latin-300-normal-FGBQ0wlI.woff2 +0 -0
  125. package/payload/server/public/assets/newsreader-latin-400-normal-BFBkh4jY.woff2 +0 -0
  126. package/payload/server/public/assets/newsreader-latin-400-normal-gRTjlS2D.woff +0 -0
  127. package/payload/server/public/assets/newsreader-latin-500-normal-B66TYsaK.woff2 +0 -0
  128. package/payload/server/public/assets/newsreader-latin-500-normal-DFwuUcdu.woff +0 -0
  129. package/payload/server/public/assets/newsreader-latin-ext-300-normal-CFtw49Zd.woff +0 -0
  130. package/payload/server/public/assets/newsreader-latin-ext-300-normal-DRMzurxT.woff2 +0 -0
  131. package/payload/server/public/assets/newsreader-latin-ext-400-normal-DYA1XoQK.woff +0 -0
  132. package/payload/server/public/assets/newsreader-latin-ext-400-normal-svq1FPys.woff2 +0 -0
  133. package/payload/server/public/assets/newsreader-latin-ext-500-normal-BNHmvKvI.woff2 +0 -0
  134. package/payload/server/public/assets/newsreader-latin-ext-500-normal-CZruMFou.woff +0 -0
  135. package/payload/server/public/assets/newsreader-vietnamese-300-normal-CsrIkm-V.woff +0 -0
  136. package/payload/server/public/assets/newsreader-vietnamese-300-normal-D3VHEe81.woff2 +0 -0
  137. package/payload/server/public/assets/newsreader-vietnamese-400-normal-BekUZro8.woff +0 -0
  138. package/payload/server/public/assets/newsreader-vietnamese-400-normal-DdKr49mV.woff2 +0 -0
  139. package/payload/server/public/assets/newsreader-vietnamese-500-normal-BEAbKU8A.woff +0 -0
  140. package/payload/server/public/assets/newsreader-vietnamese-500-normal-CL6a8tp2.woff2 +0 -0
  141. package/payload/server/public/assets/{pieDiagram-DEJITSTG-DdiZi8Iv.js → pieDiagram-DEJITSTG-BCmRLgGO.js} +1 -1
  142. package/payload/server/public/assets/{public-DbW8Up_b.js → public-DknO-g9S.js} +5 -5
  143. package/payload/server/public/assets/public-vnj7OhQj.css +1 -0
  144. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-CvNd8CMM.js → quadrantDiagram-34T5L4WZ-CniTIUTm.js} +1 -1
  145. package/payload/server/public/assets/{requirementDiagram-MS252O5E-8LsItfG7.js → requirementDiagram-MS252O5E-CoxBSj9M.js} +1 -1
  146. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-CoXsggQF.js → sankeyDiagram-XADWPNL6-BjS-4jzq.js} +1 -1
  147. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-CxtIGmey.js → sequenceDiagram-FGHM5R23-B9jVOnPR.js} +1 -1
  148. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-C2zgoy3P.js → stateDiagram-FHFEXIEX-BvOQPzP8.js} +1 -1
  149. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-v4ND10uR.js +1 -0
  150. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-DQ_J-0lx.js → timeline-definition-GMOUNBTQ-CdfgWLo1.js} +1 -1
  151. package/payload/server/public/assets/{useSelectionMode-BbLqtKTJ.js → useSelectionMode-DwsyptOw.js} +1 -1
  152. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-B7Y7Cw54.js → vennDiagram-DHZGUBPP-JCgpIbj-.js} +1 -1
  153. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-1XkR1IH-.js → wardleyDiagram-NUSXRM2D-Dei3VqHo.js} +1 -1
  154. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-CcatDElf.js → xychartDiagram-5P7HB3ND-DUtIyoIb.js} +1 -1
  155. package/payload/server/public/brand-defaults.css +3 -3
  156. package/payload/server/public/data.html +6 -5
  157. package/payload/server/public/graph.html +7 -6
  158. package/payload/server/public/index.html +7 -6
  159. package/payload/server/public/public.html +7 -6
  160. package/payload/server/server.js +290 -157
  161. package/payload/server/public/assets/brand-hVIfBy3s.css +0 -1
  162. package/payload/server/public/assets/channel-t5srld-W.js +0 -1
  163. package/payload/server/public/assets/chunk-336JU56O-DL5O4-qf.js +0 -2
  164. package/payload/server/public/assets/classDiagram-6PBFFD2Q-197c9FtA.js +0 -1
  165. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-C5RIsovf.js +0 -1
  166. package/payload/server/public/assets/clone-BJPTkE0R.js +0 -1
  167. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-C3CpI3Q7.js +0 -1
  168. /package/payload/server/public/assets/{brand-Dmpzr6Qq.js → brand-CcN3dELF.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.246",
3
+ "version": "0.1.248",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -24,13 +24,13 @@
24
24
  "primarySubtle": "rgba(124, 140, 114, 0.08)",
25
25
  "primaryGlow": "rgba(124, 140, 114, 0.15)",
26
26
  "accent": "#E8ECF0",
27
- "background": "#FAFAF8",
27
+ "background": "#F6F5F2",
28
28
  "agentBubble": "#F5F2ED"
29
29
  },
30
30
 
31
31
  "defaultFonts": {
32
- "display": "'Cormorant', Georgia, serif",
33
- "body": "'DM Sans', -apple-system, BlinkMacSystemFont, sans-serif"
32
+ "display": "'Newsreader', Georgia, serif",
33
+ "body": "'Inter', -apple-system, BlinkMacSystemFont, sans-serif"
34
34
  },
35
35
 
36
36
  "logoContainsName": true,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "admin",
3
- "description": "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, action-approval tools (action-pending, action-approve, action-reject, action-edit), and session-retrospective-mark-complete (the deterministic sentinel the session-end Stop hook reads via JSONL grep to release the retrospective gate; admin calls it once per session after running the three-pass retrospective) for managing the Maxy platform.",
3
+ "description": "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, action-approval tools (action-pending, action-approve, action-reject, action-edit), session-retrospective-mark-complete (the deterministic sentinel the session-end Stop hook reads via JSONL grep to release the retrospective gate; admin calls it once per session after running the three-pass retrospective), and admin-identity-authenticate (native-CC PIN identity gate — the operator submits their PIN as the first action of every session to bind their authenticated userId; the PreToolUse pin-identity-gate hook blocks all other tools until this succeeds) for managing the Maxy platform.",
4
4
  "version": "0.1.0",
5
5
  "author": {
6
6
  "name": "Rubytech LLC"
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: admin
3
3
  surface: platform
4
- description: "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, action-approval tools (action-pending, action-approve, action-reject, action-edit), and session-retrospective-mark-complete (the deterministic sentinel the session-end Stop hook reads via JSONL grep to release the retrospective gate; admin calls it once per session after running the three-pass retrospective) for managing the Maxy platform."
4
+ description: "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, action-approval tools (action-pending, action-approve, action-reject, action-edit), session-retrospective-mark-complete (the deterministic sentinel the session-end Stop hook reads via JSONL grep to release the retrospective gate; admin calls it once per session after running the three-pass retrospective), and admin-identity-authenticate (native-CC PIN identity gate — the operator submits their PIN as the first action of every session to bind their authenticated userId; the PreToolUse pin-identity-gate hook blocks all other tools until this succeeds) for managing the Maxy platform."
5
5
  tools:
6
6
  - name: system-status
7
7
  publicAllowlist: false
@@ -75,6 +75,9 @@ tools:
75
75
  - name: session-retrospective-mark-complete
76
76
  publicAllowlist: false
77
77
  adminAllowlist: true
78
+ - name: admin-identity-authenticate
79
+ publicAllowlist: false
80
+ adminAllowlist: true
78
81
  hidden:
79
82
  - remote-auth-status
80
83
  - remote-auth-set-password
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env bash
2
+ # Regression test for pin-identity-gate.sh (Task 619).
3
+ #
4
+ # Covers:
5
+ # 1. Non-admin role → ALLOW (no-op)
6
+ # 2. admin, unauthenticated, ordinary tool → BLOCK (op=pin-prompted)
7
+ # 3. admin, already authenticated (state file) → ALLOW
8
+ # 4. admin, auth tool, endpoint match → ALLOW + state written (op=pin-bound)
9
+ # 5. admin, auth tool, endpoint miss → BLOCK (op=session-terminated)
10
+ # 6. admin, auth tool, endpoint unavailable → BLOCK (op=pin-unavailable)
11
+ # 7. admin, auth tool, endpoint unreachable → BLOCK (op=pin-error)
12
+ #
13
+ # The validate endpoint is stubbed via a `curl` shim on PATH whose response is
14
+ # driven by $FAKE_VALIDATE_KIND.
15
+
16
+ set -u
17
+ HOOK="$(cd "$(dirname "$0")/.." && pwd)/pin-identity-gate.sh"
18
+ [ -x "$HOOK" ] || { echo "FAIL: $HOOK not executable" >&2; exit 1; }
19
+
20
+ TMP_DIR=$(mktemp -d)
21
+ trap 'rm -rf "$TMP_DIR"' EXIT
22
+ mkdir -p "$TMP_DIR/bin" "$TMP_DIR/state"
23
+
24
+ # curl shim: emit a canned validate response based on $FAKE_VALIDATE_KIND.
25
+ cat > "$TMP_DIR/bin/curl" <<'SHIM'
26
+ #!/usr/bin/env bash
27
+ case "${FAKE_VALIDATE_KIND:-}" in
28
+ match) echo '{"kind":"match","userId":"user-0001"}' ;;
29
+ miss) echo '{"kind":"miss"}' ;;
30
+ unavailable) echo '{"kind":"unavailable"}' ;;
31
+ corrupt) echo '{"kind":"corrupt"}' ;;
32
+ unreachable) echo '' ;;
33
+ *) echo '' ;;
34
+ esac
35
+ SHIM
36
+ chmod +x "$TMP_DIR/bin/curl"
37
+
38
+ AUTH_TOOL='mcp__plugin_admin_admin__admin-identity-authenticate'
39
+ PASS=0; FAIL=0
40
+
41
+ # run <role> <kind> <stdin-json> [specialist] ; sets EXIT and ERR
42
+ run() {
43
+ ERR=$(printf '%s' "$3" | \
44
+ PATH="$TMP_DIR/bin:$PATH" \
45
+ MAXY_SESSION_ROLE="$1" \
46
+ MAXY_SPECIALIST="${4:-}" \
47
+ FAKE_VALIDATE_KIND="$2" \
48
+ MAXY_IDENTITY_GATE_STATE_DIR="$TMP_DIR/state" \
49
+ MAXY_IDENTITY_VALIDATE_URL="http://stub/validate" \
50
+ bash "$HOOK" 2>&1 >/dev/null)
51
+ EXIT=$?
52
+ }
53
+
54
+ check() { # <label> <want-exit> <grep-or-->
55
+ if [ "$EXIT" != "$2" ]; then echo "FAIL[$1]: exit=$EXIT want=$2 err=$ERR"; FAIL=$((FAIL+1)); return; fi
56
+ if [ "$3" != "-" ] && ! printf '%s' "$ERR" | grep -q "$3"; then echo "FAIL[$1]: missing '$3' in: $ERR"; FAIL=$((FAIL+1)); return; fi
57
+ echo "PASS[$1]"; PASS=$((PASS+1))
58
+ }
59
+
60
+ # 1. non-admin → allow
61
+ run public '' '{"session_id":"s1","tool_name":"Bash","tool_input":{}}'
62
+ check non-admin 0 -
63
+
64
+ # 1b. admin but specialist subagent (MAXY_SPECIALIST set) → allow (no-op)
65
+ run admin '' '{"session_id":"s1b","tool_name":"Bash","tool_input":{}}' database-operator
66
+ check specialist-exempt 0 -
67
+
68
+ # 2. admin unauth, ordinary tool → block
69
+ run admin '' '{"session_id":"s2","tool_name":"Bash","tool_input":{}}'
70
+ check unauth-block 2 "op=pin-prompted"
71
+
72
+ # 3. admin, authenticated (state file present) → allow
73
+ echo '{"userId":"user-0001"}' > "$TMP_DIR/state/s3.json"
74
+ run admin '' '{"session_id":"s3","tool_name":"Bash","tool_input":{}}'
75
+ check authed-allow 0 -
76
+
77
+ # 4. admin, auth tool, match → allow + state written + pin-bound
78
+ run admin match "{\"session_id\":\"s4\",\"tool_name\":\"$AUTH_TOOL\",\"tool_input\":{\"pin\":\"1234\"}}"
79
+ check match-allow 0 "op=pin-bound"
80
+ [ -f "$TMP_DIR/state/s4.json" ] && { echo "PASS[match-state-written]"; PASS=$((PASS+1)); } || { echo "FAIL[match-state-written]"; FAIL=$((FAIL+1)); }
81
+
82
+ # 5. admin, auth tool, miss → block + terminate
83
+ run admin miss "{\"session_id\":\"s5\",\"tool_name\":\"$AUTH_TOOL\",\"tool_input\":{\"pin\":\"9999\"}}"
84
+ check miss-block 2 "op=session-terminated"
85
+
86
+ # 6. admin, auth tool, unavailable → block
87
+ run admin unavailable "{\"session_id\":\"s6\",\"tool_name\":\"$AUTH_TOOL\",\"tool_input\":{\"pin\":\"1\"}}"
88
+ check unavailable-block 2 "op=pin-unavailable"
89
+
90
+ # 7. admin, auth tool, unreachable endpoint → block (fail-closed)
91
+ run admin unreachable "{\"session_id\":\"s7\",\"tool_name\":\"$AUTH_TOOL\",\"tool_input\":{\"pin\":\"1\"}}"
92
+ check unreachable-block 2 "op=pin-error"
93
+
94
+ echo "---"
95
+ echo "PASS=$PASS FAIL=$FAIL"
96
+ [ "$FAIL" = 0 ] || exit 1
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env bash
2
+ # Native-CC admin PIN identity gate (Task 619).
3
+ #
4
+ # PreToolUse hook, registered under the universal (no-matcher) entry so it fires
5
+ # for EVERY tool call on a native-CC admin session. It denies all tools until the
6
+ # operator authenticates: the agent must call `admin-identity-authenticate` with a
7
+ # PIN that the loopback validate endpoint matches against users.json. On match the
8
+ # hook records per-session auth state (keyed by the conversation `session_id` it
9
+ # reads from stdin) and lets every subsequent tool through. Enforcement is here —
10
+ # a model that skips the prompt cannot reach any other tool.
11
+ #
12
+ # Scope: only native loopback admin sessions (MAXY_SESSION_ROLE=admin). Specialist
13
+ # subagents dispatched via the Task tool share the parent conversation's
14
+ # session_id, so they pass once the operator session is authenticated.
15
+ #
16
+ # Protocol: exit 0 = allow, exit 2 = block (stderr message shown to the agent),
17
+ # matching the sibling gate hooks. Fail-closed on terminal stdin.
18
+ #
19
+ # Lifecycle (one [admin-identity] line per step, correlated by sessionId):
20
+ # op=loopback-spawn-received → op=pin-prompted → (op=pin-bound source=pin |
21
+ # op=pin-miss → op=session-terminated) ; the MCP tool emits op=about-owner-loaded.
22
+
23
+ set -uo pipefail
24
+
25
+ if [ -t 0 ]; then
26
+ echo "Blocked: pin-identity-gate received no stdin (cannot inspect tool call). Failing closed." >&2
27
+ exit 2
28
+ fi
29
+ INPUT=$(cat)
30
+
31
+ # Only native loopback admin OPERATOR sessions are in scope:
32
+ # - MAXY_SESSION_ROLE=admin excludes public/access sessions.
33
+ # - MAXY_SPECIALIST empty excludes pty-spawned specialist/recorder sessions,
34
+ # which carry role=admin but are not human operators and never enter a PIN
35
+ # (mirrors the recursion guard the other admin hooks use). Task-tool
36
+ # specialist subagents share the operator's session_id + empty MAXY_SPECIALIST,
37
+ # so they pass via the operator's auth-state file once it is bound.
38
+ [ "${MAXY_SESSION_ROLE:-}" = "admin" ] || exit 0
39
+ [ -z "${MAXY_SPECIALIST:-}" ] || exit 0
40
+
41
+ # ----- Parse fields from stdin (python3, robust on nested tool_input) -------
42
+ SESSION_ID=$(printf '%s' "$INPUT" | python3 -c 'import json,sys
43
+ try: d=json.load(sys.stdin)
44
+ except Exception: d={}
45
+ print(d.get("session_id","") or "")' 2>/dev/null)
46
+ TOOL_NAME=$(printf '%s' "$INPUT" | python3 -c 'import json,sys
47
+ try: d=json.load(sys.stdin)
48
+ except Exception: d={}
49
+ print(d.get("tool_name","") or "")' 2>/dev/null)
50
+
51
+ SID8="${SESSION_ID:0:8}"; [ -z "$SID8" ] && SID8='-'
52
+ log() { echo "[admin-identity] $1" >&2; }
53
+
54
+ # ----- Resolve account/state dir -------------------------------------------
55
+ # The account dir basename IS the ACCOUNT_ID (resolve-account-dir.sh), and the
56
+ # rc child env carries ACCOUNT_ID, so resolve deterministically rather than
57
+ # `find | head -1` — which does not skip dotfiles and could pick the sibling
58
+ # `data/accounts/.trash/`, diverging from the manager's `spawnCwd` (= the
59
+ # account dir) and stranding state the standing audit can never reconcile.
60
+ HOOK_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
61
+ PLATFORM_ROOT_RESOLVED="${HOOK_DIR}/../../.."
62
+ ACCOUNTS_DIR="${PLATFORM_ROOT_RESOLVED}/../data/accounts"
63
+ ACCOUNT_DIR=""
64
+ if [ -n "${ACCOUNT_ID:-}" ]; then
65
+ ACCOUNT_DIR="${ACCOUNTS_DIR}/${ACCOUNT_ID}"
66
+ elif [ -d "$ACCOUNTS_DIR" ]; then
67
+ # Defensive fallback only when ACCOUNT_ID is unset — exclude dot-entries.
68
+ ACCOUNT_DIR=$(find "$ACCOUNTS_DIR" -mindepth 1 -maxdepth 1 -type d ! -name '.*' 2>/dev/null | head -1)
69
+ fi
70
+ STATE_DIR="${MAXY_IDENTITY_GATE_STATE_DIR:-${ACCOUNT_DIR}/state/admin-identity}"
71
+ STATE_FILE="${STATE_DIR}/${SESSION_ID}.json"
72
+
73
+ AUTH_TOOL='mcp__plugin_admin_admin__admin-identity-authenticate'
74
+ VALIDATE_URL="${MAXY_IDENTITY_VALIDATE_URL:-http://127.0.0.1:${MAXY_UI_INTERNAL_PORT:-0}/api/admin/identity/validate}"
75
+
76
+ # Already authenticated for this conversation → pass everything through.
77
+ if [ -n "$SESSION_ID" ] && [ -f "$STATE_FILE" ]; then
78
+ exit 0
79
+ fi
80
+
81
+ log "op=loopback-spawn-received sessionId=${SID8}"
82
+
83
+ # ----- The authenticate tool: validate, bind on match, terminate on miss ----
84
+ if [ "$TOOL_NAME" = "$AUTH_TOOL" ]; then
85
+ PIN=$(printf '%s' "$INPUT" | python3 -c 'import json,sys
86
+ try: d=json.load(sys.stdin)
87
+ except Exception: d={}
88
+ ti=d.get("tool_input") or {}
89
+ print(ti.get("pin","") or "")' 2>/dev/null)
90
+
91
+ REQ=$(python3 -c 'import json,sys
92
+ print(json.dumps({"pin": sys.argv[1], "sessionId": sys.argv[2]}))' "$PIN" "$SESSION_ID" 2>/dev/null)
93
+ RESP=$(printf '%s' "$REQ" | curl -s --max-time 5 -X POST -H 'content-type: application/json' -d @- "$VALIDATE_URL" 2>/dev/null)
94
+ KIND=$(printf '%s' "$RESP" | python3 -c 'import json,sys
95
+ try: print((json.load(sys.stdin) or {}).get("kind","") or "")
96
+ except Exception: print("")' 2>/dev/null)
97
+
98
+ case "$KIND" in
99
+ match)
100
+ USERID=$(printf '%s' "$RESP" | python3 -c 'import json,sys
101
+ print(json.load(sys.stdin).get("userId","") or "")' 2>/dev/null)
102
+ mkdir -p "$STATE_DIR" 2>/dev/null
103
+ printf '{"userId":%s,"ts":"%s"}' \
104
+ "$(python3 -c 'import json,sys;print(json.dumps(sys.argv[1]))' "$USERID")" \
105
+ "$(date -u +%FT%TZ)" > "$STATE_FILE" 2>/dev/null
106
+ log "op=pin-bound sessionId=${SID8} userId=${USERID:0:8} source=pin authSurface=loopback"
107
+ exit 0
108
+ ;;
109
+ miss)
110
+ log "op=pin-miss sessionId=${SID8} reason=no-matching-user"
111
+ log "op=session-terminated sessionId=${SID8} reason=pin-auth-failed"
112
+ echo "PIN did not match. This session is being terminated. Start a new session and enter a valid PIN." >&2
113
+ exit 2
114
+ ;;
115
+ unavailable)
116
+ log "op=pin-unavailable sessionId=${SID8} reason=users-json-empty"
117
+ echo "No admin PIN is configured for this install. Complete onboarding before using the admin agent." >&2
118
+ exit 2
119
+ ;;
120
+ corrupt)
121
+ log "op=pin-error sessionId=${SID8} reason=users-json-corrupt"
122
+ echo "Admin user configuration is corrupt. Re-run the seed script before authenticating." >&2
123
+ exit 2
124
+ ;;
125
+ *)
126
+ log "op=pin-error sessionId=${SID8} reason=validate-unreachable"
127
+ echo "Identity validation is unavailable; cannot proceed without authentication." >&2
128
+ exit 2
129
+ ;;
130
+ esac
131
+ fi
132
+
133
+ # ----- Unauthenticated and not the auth tool → block -----------------------
134
+ log "op=pin-prompted sessionId=${SID8}"
135
+ echo "Authenticate first: call admin-identity-authenticate with the operator's PIN. No other tool is reachable until a matching PIN is bound for this session." >&2
136
+ exit 2
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=admin-identity-authenticate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-identity-authenticate.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/admin-identity-authenticate.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,34 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { runAuthenticate } from "../tools/admin-identity-authenticate.js";
3
+ function fetchReturning(json) {
4
+ return vi.fn(async () => ({ json: async () => json }));
5
+ }
6
+ describe("runAuthenticate", () => {
7
+ it("returns the operator name and profile on match", async () => {
8
+ const f = fetchReturning({ kind: "match", userId: "u-1", userName: "Ada", aboutOwner: { ok: true, body: "PROFILE" } });
9
+ const res = await runAuthenticate("1234", { fetchImpl: f, uiPort: "1" });
10
+ expect(res).toContain("Ada");
11
+ expect(res).toContain("PROFILE");
12
+ });
13
+ it("returns Invalid PIN on miss", async () => {
14
+ const f = fetchReturning({ kind: "miss" });
15
+ const res = await runAuthenticate("9999", { fetchImpl: f, uiPort: "1" });
16
+ expect(res).toBe("Invalid PIN.");
17
+ });
18
+ it("omits the profile when aboutOwner is unresolved but still authenticates", async () => {
19
+ const f = fetchReturning({ kind: "match", userId: "u-1", userName: "Ada", aboutOwner: { ok: false, reason: "profile-not-found" } });
20
+ const res = await runAuthenticate("1234", { fetchImpl: f, uiPort: "1" });
21
+ expect(res).toBe("Authenticated as Ada.");
22
+ });
23
+ it("reports service-unavailable when the endpoint throws", async () => {
24
+ const f = vi.fn(async () => { throw new Error("ECONNREFUSED"); });
25
+ const res = await runAuthenticate("1234", { fetchImpl: f, uiPort: "1" });
26
+ expect(res).toContain("unavailable");
27
+ });
28
+ it("posts the pin to the loopback validate endpoint", async () => {
29
+ const f = vi.fn(async () => ({ json: async () => ({ kind: "miss" }) }));
30
+ await runAuthenticate("1234", { fetchImpl: f, uiPort: "19200" });
31
+ expect(f).toHaveBeenCalledWith("http://127.0.0.1:19200/api/admin/identity/validate", expect.objectContaining({ method: "POST", body: JSON.stringify({ pin: "1234" }) }));
32
+ });
33
+ });
34
+ //# sourceMappingURL=admin-identity-authenticate.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-identity-authenticate.test.js","sourceRoot":"","sources":["../../src/__tests__/admin-identity-authenticate.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=skill-no-prescribed-role.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-no-prescribed-role.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/skill-no-prescribed-role.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,50 @@
1
+ // Standing guard for Task 622 — no prescribed lead-capture role in any admin skill.
2
+ //
3
+ // SOUL.md is personality plus the role the operator defines at creation. The
4
+ // skill text must never re-prescribe a fixed Rubytech role (lead-capture /
5
+ // contact-form / "give basic public info"). This is a no-event failure mode:
6
+ // nothing logs when the prescribed role creeps back into the skill prose, so
7
+ // the reconciliation between expected state (no prescribed role) and actual
8
+ // file content lives here. A green run is the success signal; a match prints
9
+ // the offending `file:line` and fails the build.
10
+ import { describe, it, expect } from "vitest";
11
+ import { readdirSync, readFileSync } from "node:fs";
12
+ import { join, dirname } from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ const SKILLS_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "skills");
15
+ // Case-insensitive. The exact phrases the operator confirmed as prescriptive
16
+ // and wrong (session ebff1750); see .tasks/archive/622-*.md.
17
+ const FORBIDDEN = [
18
+ "capture the visitor's contact details",
19
+ "landing-page contact form",
20
+ "give basic public info",
21
+ ];
22
+ function skillFiles(dir) {
23
+ const out = [];
24
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
25
+ const full = join(dir, entry.name);
26
+ if (entry.isDirectory())
27
+ out.push(...skillFiles(full));
28
+ else if (entry.name === "SKILL.md")
29
+ out.push(full);
30
+ }
31
+ return out;
32
+ }
33
+ describe("admin skills carry no prescribed lead-capture role (Task 622)", () => {
34
+ it("no SKILL.md contains a forbidden prescribed-role phrase", () => {
35
+ const hits = [];
36
+ for (const file of skillFiles(SKILLS_DIR)) {
37
+ const lines = readFileSync(file, "utf8").split("\n");
38
+ lines.forEach((line, i) => {
39
+ const lower = line.toLowerCase();
40
+ for (const phrase of FORBIDDEN) {
41
+ if (lower.includes(phrase.toLowerCase())) {
42
+ hits.push(`${file}:${i + 1} — ${phrase}`);
43
+ }
44
+ }
45
+ });
46
+ }
47
+ expect(hits, `prescribed-role phrasing found:\n${hits.join("\n")}`).toEqual([]);
48
+ });
49
+ });
50
+ //# sourceMappingURL=skill-no-prescribed-role.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-no-prescribed-role.test.js","sourceRoot":"","sources":["../../src/__tests__/skill-no-prescribed-role.test.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,6EAA6E;AAC7E,iDAAiD;AACjD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AAE7F,6EAA6E;AAC7E,6DAA6D;AAC7D,MAAM,SAAS,GAAG;IAChB,uCAAuC;IACvC,2BAA2B;IAC3B,wBAAwB;CACzB,CAAC;AAEF,SAAS,UAAU,CAAC,GAAW;IAC7B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,WAAW,EAAE;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;aAClD,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU;YAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,QAAQ,CAAC,+DAA+D,EAAE,GAAG,EAAE;IAC7E,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACrD,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;gBACxB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjC,KAAK,MAAM,MAAM,IAAI,SAAS,EAAE,CAAC;oBAC/B,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;wBACzC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,MAAM,EAAE,CAAC,CAAC;oBAC5C,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QACD,MAAM,CAAC,IAAI,EAAE,oCAAoC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -23,6 +23,7 @@ import { resolvePublicHostname } from "./lib/public-hostname.js";
23
23
  import { publishSite } from "./tools/publish-site.js";
24
24
  import { resolveCapabilitiesHere } from "./tools/capabilities-here.js";
25
25
  import { runCatalogueCensus } from "./lib/catalogue-census.js";
26
+ import { runAuthenticate } from "./tools/admin-identity-authenticate.js";
26
27
  function refusalGuidance(kind) {
27
28
  switch (kind) {
28
29
  case "unsafe-slug":
@@ -3146,6 +3147,13 @@ server.tool("session-retrospective-mark-complete", "Marks the end-of-session ret
3146
3147
  }],
3147
3148
  };
3148
3149
  });
3150
+ // Task 619 — native-CC admin PIN identity gate. Eager-loaded so it is visible
3151
+ // from the first turn: the operator must authenticate before any other tool is
3152
+ // reachable (the PreToolUse pin-identity-gate hook enforces that; this tool is
3153
+ // the agent-callable PIN-submit surface and returns the operator's profile).
3154
+ eagerTool(server, "admin-identity-authenticate", "Authenticate the current operator by PIN. MUST be the first tool you call in a session: submit the operator's PIN to bind their identity. No other tool is reachable until this succeeds. Returns the authenticated operator's profile.", { pin: z.string().describe("The operator's PIN, exactly as they typed it.") }, async ({ pin }) => ({
3155
+ content: [{ type: "text", text: await runAuthenticate(pin) }],
3156
+ }));
3149
3157
  // Cleanup on exit (SIGTERM for systemd service stops on Pi)
3150
3158
  const cleanup = async () => {
3151
3159
  await closeDriver();