@ken-jo/agent-connector 0.3.1 → 0.4.1

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 (189) hide show
  1. package/README.md +180 -16
  2. package/dist/action-WT7GVRCC.js +55 -0
  3. package/dist/action-WT7GVRCC.js.map +1 -0
  4. package/dist/amazon-q-COAL3764.js +276 -0
  5. package/dist/amazon-q-COAL3764.js.map +1 -0
  6. package/dist/{amp-YGV66GJT.js → amp-4U33CU7E.js} +81 -5
  7. package/dist/amp-4U33CU7E.js.map +1 -0
  8. package/dist/{antigravity-7IBYFO6P.js → antigravity-AMKKKHS4.js} +4 -4
  9. package/dist/{antigravity-cli-JEOUP2VN.js → antigravity-cli-XERXEH5W.js} +4 -4
  10. package/dist/{chunk-3JCRUROU.js → chunk-24IDL5RW.js} +13 -4
  11. package/dist/chunk-24IDL5RW.js.map +1 -0
  12. package/dist/chunk-2AVLX7GH.js +351 -0
  13. package/dist/chunk-2AVLX7GH.js.map +1 -0
  14. package/dist/{chunk-GSGDG2BV.js → chunk-4ENJK5S2.js} +106 -176
  15. package/dist/chunk-4ENJK5S2.js.map +1 -0
  16. package/dist/{chunk-ZAM4M7LI.js → chunk-5OWS5CMA.js} +136 -296
  17. package/dist/chunk-5OWS5CMA.js.map +1 -0
  18. package/dist/{chunk-PD4VFEBY.js → chunk-CPLK7GTE.js} +79 -5
  19. package/dist/chunk-CPLK7GTE.js.map +1 -0
  20. package/dist/chunk-DCKFYWGM.js +237 -0
  21. package/dist/chunk-DCKFYWGM.js.map +1 -0
  22. package/dist/chunk-EGCBZ2DC.js +83 -0
  23. package/dist/chunk-EGCBZ2DC.js.map +1 -0
  24. package/dist/chunk-F5RX62T7.js +21 -0
  25. package/dist/chunk-F5RX62T7.js.map +1 -0
  26. package/dist/chunk-FAHIMJRU.js +177 -0
  27. package/dist/chunk-FAHIMJRU.js.map +1 -0
  28. package/dist/{chunk-MEH7GC7R.js → chunk-I5MLE6S7.js} +33 -3
  29. package/dist/chunk-I5MLE6S7.js.map +1 -0
  30. package/dist/{chunk-KVAAFYRR.js → chunk-KHO5WNTP.js} +2 -18
  31. package/dist/chunk-KHO5WNTP.js.map +1 -0
  32. package/dist/{chunk-KQSNDFBN.js → chunk-LQRAPXS4.js} +3 -3
  33. package/dist/{chunk-3RBLIHPZ.js → chunk-O5TWCIJN.js} +27 -17
  34. package/dist/chunk-O5TWCIJN.js.map +1 -0
  35. package/dist/{chunk-WNREHWKL.js → chunk-QREL7LZJ.js} +5 -4
  36. package/dist/chunk-QREL7LZJ.js.map +1 -0
  37. package/dist/{chunk-KMAKDD3Z.js → chunk-RIMVWCZY.js} +37 -12
  38. package/dist/chunk-RIMVWCZY.js.map +1 -0
  39. package/dist/{openclaw-UZ5BMCGQ.js → chunk-URHQMFB3.js} +210 -41
  40. package/dist/chunk-URHQMFB3.js.map +1 -0
  41. package/dist/chunk-WZA4CSGJ.js +28 -0
  42. package/dist/chunk-WZA4CSGJ.js.map +1 -0
  43. package/dist/chunk-XJ4S47CT.js +44 -0
  44. package/dist/chunk-XJ4S47CT.js.map +1 -0
  45. package/dist/{claude-code-4275HQU4.js → claude-code-W4PUFZL5.js} +194 -52
  46. package/dist/claude-code-W4PUFZL5.js.map +1 -0
  47. package/dist/cli/sdk.js +5 -3
  48. package/dist/cli/sdk.js.map +1 -1
  49. package/dist/cli.js +1 -1
  50. package/dist/cline-WRYMNS7Z.js +442 -0
  51. package/dist/cline-WRYMNS7Z.js.map +1 -0
  52. package/dist/{codebuff-LE3KBZGM.js → codebuff-DH5Y7GAT.js} +106 -5
  53. package/dist/codebuff-DH5Y7GAT.js.map +1 -0
  54. package/dist/{codex-4BY2ZJIW.js → codex-DDHIV767.js} +43 -11
  55. package/dist/codex-DDHIV767.js.map +1 -0
  56. package/dist/continue-K43SUHLQ.js +790 -0
  57. package/dist/continue-K43SUHLQ.js.map +1 -0
  58. package/dist/{copilot-cli-IPISKO3V.js → copilot-cli-Q3SSPT4V.js} +70 -12
  59. package/dist/copilot-cli-Q3SSPT4V.js.map +1 -0
  60. package/dist/{crush-X3E25EQG.js → crush-MBEH2R3I.js} +130 -6
  61. package/dist/crush-MBEH2R3I.js.map +1 -0
  62. package/dist/{cursor-UH3W622V.js → cursor-TMBXDYR4.js} +119 -16
  63. package/dist/cursor-TMBXDYR4.js.map +1 -0
  64. package/dist/{detect-U3E7NVNW.js → detect-KG5FG3EV.js} +5 -3
  65. package/dist/{detect-U3E7NVNW.js.map → detect-KG5FG3EV.js.map} +1 -1
  66. package/dist/{doctor-DZTWED4O.js → doctor-FLZF34Y3.js} +226 -96
  67. package/dist/doctor-FLZF34Y3.js.map +1 -0
  68. package/dist/{droid-T5VBRXZF.js → droid-JC2NT6S4.js} +298 -10
  69. package/dist/droid-JC2NT6S4.js.map +1 -0
  70. package/dist/{gemini-cli-ZSUK55UA.js → gemini-cli-ILR7M5X3.js} +62 -10
  71. package/dist/gemini-cli-ILR7M5X3.js.map +1 -0
  72. package/dist/{goose-M3RR3CZG.js → goose-SFO5BBGW.js} +112 -10
  73. package/dist/goose-SFO5BBGW.js.map +1 -0
  74. package/dist/{hermes-CUNKIAR5.js → hermes-3N7WN4WS.js} +329 -15
  75. package/dist/hermes-3N7WN4WS.js.map +1 -0
  76. package/dist/{hook-EVY7LOUQ.js → hook-XR3Z45FB.js} +16 -10
  77. package/dist/hook-XR3Z45FB.js.map +1 -0
  78. package/dist/index.d.ts +71 -3
  79. package/dist/index.js +23 -3
  80. package/dist/{install-OWAS5UIE.js → install-BSR62UHJ.js} +12 -9
  81. package/dist/{install-OWAS5UIE.js.map → install-BSR62UHJ.js.map} +1 -1
  82. package/dist/introspect-Nw8hTReX.d.ts +53 -0
  83. package/dist/{jetbrains-copilot-C3HDGGML.js → jetbrains-copilot-NRZHI5PC.js} +85 -18
  84. package/dist/jetbrains-copilot-NRZHI5PC.js.map +1 -0
  85. package/dist/{kilo-PKAJCWPX.js → kilo-5NWOXVSD.js} +16 -16
  86. package/dist/kilo-5NWOXVSD.js.map +1 -0
  87. package/dist/{kilo-cli-QWUVXG2O.js → kilo-cli-UZ7R5UOP.js} +3 -3
  88. package/dist/{kimi-EVD7YKXS.js → kimi-2YFK2GBA.js} +87 -25
  89. package/dist/kimi-2YFK2GBA.js.map +1 -0
  90. package/dist/{kiro-UQRY6EIO.js → kiro-YL6AWUYG.js} +3 -3
  91. package/dist/{leaderboard-CSWHI4UY.js → leaderboard-MJG24O35.js} +2 -2
  92. package/dist/mimo-code-FTMGSKMQ.js +790 -0
  93. package/dist/mimo-code-FTMGSKMQ.js.map +1 -0
  94. package/dist/{mux-5CI5FP2W.js → mux-5XZFB44C.js} +156 -6
  95. package/dist/mux-5XZFB44C.js.map +1 -0
  96. package/dist/nemoclaw-W5KEJ3WX.js +58 -0
  97. package/dist/nemoclaw-W5KEJ3WX.js.map +1 -0
  98. package/dist/{omp-AXA7XY22.js → omp-2QMEY6HD.js} +138 -18
  99. package/dist/omp-2QMEY6HD.js.map +1 -0
  100. package/dist/openclaw-EXJEFNZI.js +18 -0
  101. package/dist/openclaw-EXJEFNZI.js.map +1 -0
  102. package/dist/{opencode-NZBRLJN3.js → opencode-WNXYJR4X.js} +84 -27
  103. package/dist/opencode-WNXYJR4X.js.map +1 -0
  104. package/dist/{package-WFOS4QVU.js → package-ACZXJHWB.js} +8 -6
  105. package/dist/{package-WFOS4QVU.js.map → package-ACZXJHWB.js.map} +1 -1
  106. package/dist/{pi-WMTUX6ES.js → pi-PVVPVWKG.js} +3 -2
  107. package/dist/{pi-WMTUX6ES.js.map → pi-PVVPVWKG.js.map} +1 -1
  108. package/dist/{qwen-code-MRRIUQ3P.js → qwen-code-G4CXKUHR.js} +426 -10
  109. package/dist/qwen-code-G4CXKUHR.js.map +1 -0
  110. package/dist/{roo-code-NI5UU6JB.js → roo-code-67TYJIQR.js} +137 -4
  111. package/dist/roo-code-67TYJIQR.js.map +1 -0
  112. package/dist/runtime/index.d.ts +96 -2
  113. package/dist/runtime/index.js +19 -10
  114. package/dist/sdk/index.d.ts +52 -0
  115. package/dist/sdk/index.js +82 -0
  116. package/dist/sdk/index.js.map +1 -0
  117. package/dist/sdk/test.d.ts +74 -0
  118. package/dist/sdk/test.js +292 -0
  119. package/dist/sdk/test.js.map +1 -0
  120. package/dist/{serve-3C2VFPAY.js → serve-GER7URPR.js} +13 -9
  121. package/dist/{serve-3C2VFPAY.js.map → serve-GER7URPR.js.map} +1 -1
  122. package/dist/{status-MKQYX5MW.js → status-LAJE3BM5.js} +10 -7
  123. package/dist/{status-MKQYX5MW.js.map → status-LAJE3BM5.js.map} +1 -1
  124. package/dist/statusline-L376ZPZU.js +60 -0
  125. package/dist/statusline-L376ZPZU.js.map +1 -0
  126. package/dist/{telemetry-3DN6XGYB.js → telemetry-B3DX3PWU.js} +6 -4
  127. package/dist/{telemetry-3DN6XGYB.js.map → telemetry-B3DX3PWU.js.map} +1 -1
  128. package/dist/{trae-CQC72RHH.js → trae-7CZZGOGI.js} +99 -5
  129. package/dist/trae-7CZZGOGI.js.map +1 -0
  130. package/dist/{types-CNigfEuR.d.ts → types-CRVrFWeU.d.ts} +322 -18
  131. package/dist/{uninstall-WG6PFWF3.js → uninstall-ZYTW7YMZ.js} +12 -9
  132. package/dist/{uninstall-WG6PFWF3.js.map → uninstall-ZYTW7YMZ.js.map} +1 -1
  133. package/dist/{upgrade-32NC2RKI.js → upgrade-4LUEEOZW.js} +12 -9
  134. package/dist/{upgrade-32NC2RKI.js.map → upgrade-4LUEEOZW.js.map} +1 -1
  135. package/dist/{usage-CJOM2WQM.js → usage-ZFBVQF3H.js} +2 -2
  136. package/dist/{usage-event-472X3DOY.js → usage-event-GZ32AGEB.js} +12 -8
  137. package/dist/{usage-event-472X3DOY.js.map → usage-event-GZ32AGEB.js.map} +1 -1
  138. package/dist/{vscode-copilot-4RVRTCKV.js → vscode-copilot-MF2CDNMT.js} +23 -10
  139. package/dist/vscode-copilot-MF2CDNMT.js.map +1 -0
  140. package/dist/{warp-X6MB67NT.js → warp-VKG55TAT.js} +55 -4
  141. package/dist/warp-VKG55TAT.js.map +1 -0
  142. package/dist/windsurf-FU5G4SHF.js +443 -0
  143. package/dist/windsurf-FU5G4SHF.js.map +1 -0
  144. package/dist/{zed-NTCYW4V2.js → zed-SFUHL3GS.js} +3 -3
  145. package/package.json +10 -2
  146. package/dist/amp-YGV66GJT.js.map +0 -1
  147. package/dist/chunk-3JCRUROU.js.map +0 -1
  148. package/dist/chunk-3RBLIHPZ.js.map +0 -1
  149. package/dist/chunk-GSGDG2BV.js.map +0 -1
  150. package/dist/chunk-KMAKDD3Z.js.map +0 -1
  151. package/dist/chunk-KVAAFYRR.js.map +0 -1
  152. package/dist/chunk-MEH7GC7R.js.map +0 -1
  153. package/dist/chunk-NJU275US.js +0 -238
  154. package/dist/chunk-NJU275US.js.map +0 -1
  155. package/dist/chunk-PD4VFEBY.js.map +0 -1
  156. package/dist/chunk-WNREHWKL.js.map +0 -1
  157. package/dist/chunk-ZAM4M7LI.js.map +0 -1
  158. package/dist/claude-code-4275HQU4.js.map +0 -1
  159. package/dist/codebuff-LE3KBZGM.js.map +0 -1
  160. package/dist/codex-4BY2ZJIW.js.map +0 -1
  161. package/dist/copilot-cli-IPISKO3V.js.map +0 -1
  162. package/dist/crush-X3E25EQG.js.map +0 -1
  163. package/dist/cursor-UH3W622V.js.map +0 -1
  164. package/dist/doctor-DZTWED4O.js.map +0 -1
  165. package/dist/droid-T5VBRXZF.js.map +0 -1
  166. package/dist/gemini-cli-ZSUK55UA.js.map +0 -1
  167. package/dist/goose-M3RR3CZG.js.map +0 -1
  168. package/dist/hermes-CUNKIAR5.js.map +0 -1
  169. package/dist/hook-EVY7LOUQ.js.map +0 -1
  170. package/dist/jetbrains-copilot-C3HDGGML.js.map +0 -1
  171. package/dist/kilo-PKAJCWPX.js.map +0 -1
  172. package/dist/kimi-EVD7YKXS.js.map +0 -1
  173. package/dist/mux-5CI5FP2W.js.map +0 -1
  174. package/dist/omp-AXA7XY22.js.map +0 -1
  175. package/dist/openclaw-UZ5BMCGQ.js.map +0 -1
  176. package/dist/opencode-NZBRLJN3.js.map +0 -1
  177. package/dist/qwen-code-MRRIUQ3P.js.map +0 -1
  178. package/dist/roo-code-NI5UU6JB.js.map +0 -1
  179. package/dist/trae-CQC72RHH.js.map +0 -1
  180. package/dist/vscode-copilot-4RVRTCKV.js.map +0 -1
  181. package/dist/warp-X6MB67NT.js.map +0 -1
  182. /package/dist/{antigravity-7IBYFO6P.js.map → antigravity-AMKKKHS4.js.map} +0 -0
  183. /package/dist/{antigravity-cli-JEOUP2VN.js.map → antigravity-cli-XERXEH5W.js.map} +0 -0
  184. /package/dist/{chunk-KQSNDFBN.js.map → chunk-LQRAPXS4.js.map} +0 -0
  185. /package/dist/{kilo-cli-QWUVXG2O.js.map → kilo-cli-UZ7R5UOP.js.map} +0 -0
  186. /package/dist/{kiro-UQRY6EIO.js.map → kiro-YL6AWUYG.js.map} +0 -0
  187. /package/dist/{leaderboard-CSWHI4UY.js.map → leaderboard-MJG24O35.js.map} +0 -0
  188. /package/dist/{usage-CJOM2WQM.js.map → usage-ZFBVQF3H.js.map} +0 -0
  189. /package/dist/{zed-NTCYW4V2.js.map → zed-SFUHL3GS.js.map} +0 -0
@@ -0,0 +1,790 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ resolveEnvRefsDeep
4
+ } from "./chunk-DBQMX75U.js";
5
+ import {
6
+ BaseAdapter
7
+ } from "./chunk-CPLK7GTE.js";
8
+ import "./chunk-7L6ITC4T.js";
9
+ import {
10
+ buildServeWrapperCommand,
11
+ shouldWrapForTelemetry
12
+ } from "./chunk-I5MLE6S7.js";
13
+ import "./chunk-N6HH7K6P.js";
14
+ import "./chunk-TRQKHP7D.js";
15
+
16
+ // src/adapters/mimo-code/index.ts
17
+ import {
18
+ chmodSync,
19
+ existsSync,
20
+ mkdirSync,
21
+ readFileSync,
22
+ rmSync,
23
+ writeFileSync
24
+ } from "fs";
25
+ import { homedir } from "os";
26
+ import { dirname, join } from "path";
27
+ var HOST = "mimo-code";
28
+ var MCP_ROOT_KEY = "mcp";
29
+ var CONFIG_DIR_NAME = "mimocode";
30
+ var CONFIG_FILE_NAME = "mimocode.json";
31
+ var EVENT_TO_MIMOCODE = {
32
+ PreToolUse: "tool.execute.before",
33
+ PostToolUse: "tool.execute.after",
34
+ // No real SessionStart hook (inherited from OpenCode #14808 / #5409). The
35
+ // verified surrogate is experimental.chat.system.transform.
36
+ SessionStart: "experimental.chat.system.transform",
37
+ // chat.message fires on the user's prompt submission; the handler mutates
38
+ // output (pushes a text part) to inject context. No documented block/abort.
39
+ UserPromptSubmit: "chat.message"
40
+ };
41
+ var MiMoCodeAdapter = class extends BaseAdapter {
42
+ id = HOST;
43
+ name = "MiMoCode";
44
+ paradigm = "ts-plugin";
45
+ capabilities = {
46
+ // Memory surface: AGENTS.md-first managed block (CLAUDE.md fallback, like
47
+ // OpenCode — MiMoCode keeps OpenCode's CLAUDE.md import + AGENTS.md reading).
48
+ supportsMemory: true,
49
+ preToolUse: true,
50
+ postToolUse: true,
51
+ preCompact: false,
52
+ sessionStart: true,
53
+ sessionEnd: false,
54
+ userPromptSubmit: true,
55
+ stop: false,
56
+ notification: false,
57
+ // tool.execute.before mutates output.args → input rewrite supported.
58
+ canModifyArgs: true,
59
+ // tool.execute.after mutates output.output → output rewrite supported.
60
+ canModifyOutput: true,
61
+ // injected via experimental.chat.system.transform at session start.
62
+ canInjectSessionContext: true,
63
+ transports: ["stdio", "sse", "http"],
64
+ // Content surfaces: MiMoCode authors all three natively under <mcDir>
65
+ // (inherited from OpenCode's commands/skills/agent layout).
66
+ supportsCommands: true,
67
+ supportsSkills: true,
68
+ supportsSubagents: true
69
+ };
70
+ // ── Detection ──────────────────────────────────────────────────────────
71
+ detectInstalled(projectDir) {
72
+ const userConfigDir = join(homedir(), ".config", CONFIG_DIR_NAME);
73
+ const userConfig = join(userConfigDir, CONFIG_FILE_NAME);
74
+ const projectConfig = join(projectDir, CONFIG_FILE_NAME);
75
+ const userInstalled = existsSync(userConfigDir) || existsSync(userConfig);
76
+ const projInstalled = existsSync(projectConfig);
77
+ const installed = userInstalled || projInstalled;
78
+ const scope = projInstalled && !userInstalled ? "project" : "user";
79
+ const configPath = scope === "project" ? projectConfig : userConfig;
80
+ return {
81
+ id: this.id,
82
+ name: this.name,
83
+ installed,
84
+ paradigm: this.paradigm,
85
+ capabilities: this.capabilities,
86
+ configPath,
87
+ scope,
88
+ reason: installed ? scope === "project" ? `found project MiMoCode config at ${projectConfig}` : `found MiMoCode config under ${userConfigDir}` : `no MiMoCode config at ${userConfigDir}`,
89
+ confidence: installed ? "high" : "low"
90
+ };
91
+ }
92
+ // ── Memory surface: AGENTS.md / CLAUDE.md-fallback probe (project) ──────
93
+ // Mirrors OpenCode: project AGENTS.md, falling back to CLAUDE.md ONLY when no
94
+ // AGENTS.md exists (creating AGENTS.md beside a relied-on CLAUDE.md would
95
+ // shadow the user's rules). User scope stays on the BaseAdapter default.
96
+ memoryTargets(ctx) {
97
+ if (this.memoryOverride(ctx)?.path || ctx.scope !== "project") {
98
+ return super.memoryTargets(ctx);
99
+ }
100
+ const agentsMd = join(ctx.projectDir, "AGENTS.md");
101
+ if (existsSync(agentsMd)) {
102
+ return [{ path: agentsMd, reason: "AGENTS.md (mimocode's primary project rules file)" }];
103
+ }
104
+ const claudeMd = join(ctx.projectDir, "CLAUDE.md");
105
+ if (existsSync(claudeMd)) {
106
+ return [
107
+ {
108
+ path: claudeMd,
109
+ reason: "CLAUDE.md (mimocode's fallback rules file \u2014 creating AGENTS.md would shadow it)"
110
+ }
111
+ ];
112
+ }
113
+ return [{ path: agentsMd, reason: "AGENTS.md standard (created; no mimocode rules file yet)" }];
114
+ }
115
+ // ── Native paths ───────────────────────────────────────────────────────
116
+ getConfigDir(ctx) {
117
+ return ctx.scope === "project" ? ctx.projectDir : join(homedir(), ".config", CONFIG_DIR_NAME);
118
+ }
119
+ getServerConfigPath(ctx) {
120
+ return join(this.getConfigDir(ctx), CONFIG_FILE_NAME);
121
+ }
122
+ /**
123
+ * For ts-plugin hosts the "hook config path" is the generated plugin FILE.
124
+ * MiMoCode (like OpenCode) auto-loads every file in the plugin dir; writing
125
+ * this file IS the registration.
126
+ */
127
+ getHookConfigPath(ctx) {
128
+ return join(this.pluginDir(ctx), this.pluginFileName(ctx));
129
+ }
130
+ /** Plugin directory MiMoCode auto-loads modules from, per scope. */
131
+ pluginDir(ctx) {
132
+ return ctx.scope === "project" ? join(ctx.projectDir, `.${CONFIG_DIR_NAME}`, "plugin") : join(homedir(), ".config", CONFIG_DIR_NAME, "plugin");
133
+ }
134
+ /** Plugin module file name (one per connector, kebab-case id). */
135
+ pluginFileName(ctx) {
136
+ return `${ctx.connector.id}.js`;
137
+ }
138
+ // ── MCP server install / uninstall ─────────────────────────────────────
139
+ installServer(ctx) {
140
+ const server = this.effectiveServer(ctx);
141
+ const serverPath = this.getServerConfigPath(ctx);
142
+ if (!server) {
143
+ return [
144
+ {
145
+ platform: this.id,
146
+ action: "skip",
147
+ path: serverPath,
148
+ detail: ctx.connector.server ? "server registration disabled for mimo-code" : "connector declares no MCP server"
149
+ }
150
+ ];
151
+ }
152
+ const entry = this.renderServerEntry(ctx, server);
153
+ return [
154
+ this.upsertServerInJson(
155
+ serverPath,
156
+ MCP_ROOT_KEY,
157
+ ctx.connector.id,
158
+ entry,
159
+ ctx.dryRun
160
+ )
161
+ ];
162
+ }
163
+ uninstallServer(ctx) {
164
+ const serverPath = this.getServerConfigPath(ctx);
165
+ return [
166
+ this.removeServerFromJson(
167
+ serverPath,
168
+ MCP_ROOT_KEY,
169
+ ctx.connector.id,
170
+ ctx.dryRun
171
+ )
172
+ ];
173
+ }
174
+ /** Resolve the per-platform server override into an effective ServerDef. */
175
+ effectiveServer(ctx) {
176
+ const override = ctx.connector.platforms[HOST]?.server;
177
+ if (override === false) return void 0;
178
+ const base = ctx.connector.server;
179
+ if (!base) return void 0;
180
+ return override && typeof override === "object" ? { ...base, ...override } : base;
181
+ }
182
+ /**
183
+ * Render a normalized ServerDef into MiMoCode's native "mcp" entry (OpenCode
184
+ * dialect).
185
+ *
186
+ * stdio → { type: "local", command: [exe, ...args], environment }
187
+ * remote → { type: "remote", url }
188
+ *
189
+ * MiMoCode flattens command+args into a single ARRAY. When telemetry wrapping
190
+ * applies, the wrapper's command+args (which already include the real command
191
+ * tail after `--`) become the head of that array. No native interpolation
192
+ * token → resolve every ${env:VAR} reference to a literal at install time.
193
+ */
194
+ renderServerEntry(ctx, server) {
195
+ const transport = server.transport;
196
+ if (transport === "stdio") {
197
+ let command = server.command ?? "";
198
+ let args = [...server.args ?? []];
199
+ if (shouldWrapForTelemetry(server, ctx.connector.telemetry)) {
200
+ const wrapped = buildServeWrapperCommand(
201
+ ctx.homeBinPath,
202
+ ctx.connector.id,
203
+ command,
204
+ args,
205
+ ctx.scope,
206
+ this.id
207
+ );
208
+ command = wrapped.command;
209
+ args = wrapped.args;
210
+ }
211
+ const commandArray = resolveEnvRefsDeep([command, ...args]).filter(
212
+ (s) => s !== ""
213
+ );
214
+ const entry2 = {
215
+ type: "local",
216
+ command: commandArray
217
+ };
218
+ const environment = this.renderEnv(server.env);
219
+ if (environment) entry2.environment = environment;
220
+ if (server.enabled === false) entry2.enabled = false;
221
+ return entry2;
222
+ }
223
+ const entry = {
224
+ type: "remote",
225
+ url: resolveEnvRefsDeep(server.url ?? "")
226
+ };
227
+ if (server.enabled === false) entry.enabled = false;
228
+ return entry;
229
+ }
230
+ /** Render env values, resolving every ${env:VAR} reference to a literal. */
231
+ renderEnv(env) {
232
+ if (!env || Object.keys(env).length === 0) return void 0;
233
+ return resolveEnvRefsDeep({ ...env });
234
+ }
235
+ // ── Hook install / uninstall (ts-plugin) ───────────────────────────────
236
+ installHooks(ctx) {
237
+ const pluginPath = this.getHookConfigPath(ctx);
238
+ if (ctx.connector.platforms[HOST]?.hooks === false) {
239
+ return [
240
+ {
241
+ platform: this.id,
242
+ action: "skip",
243
+ path: pluginPath,
244
+ detail: "hooks disabled for mimo-code"
245
+ }
246
+ ];
247
+ }
248
+ if (ctx.connector.hookEvents.length === 0) {
249
+ return [
250
+ {
251
+ platform: this.id,
252
+ action: "skip",
253
+ path: pluginPath,
254
+ detail: "connector declares no hooks"
255
+ }
256
+ ];
257
+ }
258
+ const files = this.synthesizePlugin(ctx);
259
+ const changes = [];
260
+ for (const file of files) {
261
+ const before = existsSync(file.path) ? this.safeRead(file.path) : void 0;
262
+ let action;
263
+ if (before === void 0) action = "create";
264
+ else if (before === file.contents) action = "skip";
265
+ else action = "update";
266
+ if (action !== "skip" && !ctx.dryRun) {
267
+ ensureDir(dirname(file.path));
268
+ writeFileSync(file.path, file.contents, "utf8");
269
+ chmodSync(file.path, file.executable ? 493 : 420);
270
+ }
271
+ changes.push({
272
+ platform: this.id,
273
+ action,
274
+ path: file.path,
275
+ detail: `mimo-code plugin module (${this.hookDetail(ctx)})`
276
+ });
277
+ }
278
+ return changes;
279
+ }
280
+ /**
281
+ * Human-facing summary of which declared events the synthesized module
282
+ * ACTUALLY wires. Only events present in EVENT_TO_MIMOCODE are mapped/wired;
283
+ * any declared event with no mapping is reported separately as "unsupported
284
+ * here" so the detail never overstates coverage.
285
+ */
286
+ hookDetail(ctx) {
287
+ const declared = ctx.connector.hookEvents;
288
+ const mapped = declared.filter((e) => EVENT_TO_MIMOCODE[e] !== void 0);
289
+ const unsupported = declared.filter((e) => EVENT_TO_MIMOCODE[e] === void 0);
290
+ const base = mapped.join(",");
291
+ return unsupported.length > 0 ? `${base}; unsupported here: ${unsupported.join(",")}` : base;
292
+ }
293
+ uninstallHooks(ctx) {
294
+ const pluginPath = this.getHookConfigPath(ctx);
295
+ if (!existsSync(pluginPath)) {
296
+ return [
297
+ {
298
+ platform: this.id,
299
+ action: "skip",
300
+ path: pluginPath,
301
+ detail: "no mimo-code plugin module present"
302
+ }
303
+ ];
304
+ }
305
+ if (!ctx.dryRun) rmSync(pluginPath, { force: true });
306
+ return [
307
+ {
308
+ platform: this.id,
309
+ action: "remove",
310
+ path: pluginPath,
311
+ detail: "mimo-code plugin module"
312
+ }
313
+ ];
314
+ }
315
+ // ── Content surfaces: commands / skills / subagents ──────────────────────
316
+ // CONTENT-ONLY: pure native-file writers under <mcDir>/{commands,skills,
317
+ // agent}. No runtime dispatch, no home-bin pointer, no telemetry wrap. Each
318
+ // method is idempotent (byte-identical → skip) and reversible. Mirrors
319
+ // OpenCode's layout: commands/<name>.md, skills/<name>/SKILL.md, and the
320
+ // SINGULAR agent/<name>.md dir for subagents. For project scope the content
321
+ // root is <projectDir>/.mimocode (NOT the project root where mimocode.json
322
+ // lives).
323
+ contentRootDir(ctx) {
324
+ return ctx.scope === "project" ? join(ctx.projectDir, `.${CONFIG_DIR_NAME}`) : this.getConfigDir(ctx);
325
+ }
326
+ commandsDir(ctx) {
327
+ return join(this.contentRootDir(ctx), "commands");
328
+ }
329
+ skillsDir(ctx) {
330
+ return join(this.contentRootDir(ctx), "skills");
331
+ }
332
+ agentDir(ctx) {
333
+ return join(this.contentRootDir(ctx), "agent");
334
+ }
335
+ /** Native command file path: <mcDir>/commands/<name>.md. */
336
+ commandPath(ctx, name) {
337
+ return join(this.commandsDir(ctx), `${name}.md`);
338
+ }
339
+ /** Native skill dir: <mcDir>/skills/<name>. */
340
+ skillDir(ctx, name) {
341
+ return join(this.skillsDir(ctx), name);
342
+ }
343
+ /** Native subagent file path: <mcDir>/agent/<name>.md (SINGULAR dir). */
344
+ subagentPath(ctx, name) {
345
+ return join(this.agentDir(ctx), `${name}.md`);
346
+ }
347
+ // ── Commands ──────────────────────────────────────────────────────────────
348
+ installCommands(ctx) {
349
+ const { connector } = ctx;
350
+ if (connector.platforms[HOST]?.commands === false) {
351
+ return [{ platform: this.id, action: "skip", detail: "commands disabled for mimo-code" }];
352
+ }
353
+ if (connector.commands.length === 0) {
354
+ return [{ platform: this.id, action: "skip", detail: "connector declares no commands" }];
355
+ }
356
+ return connector.commands.map(
357
+ (cmd) => this.writeContentFile(
358
+ this.commandPath(ctx, cmd.name),
359
+ this.renderCommand(cmd),
360
+ ctx.dryRun
361
+ )
362
+ );
363
+ }
364
+ uninstallCommands(ctx) {
365
+ const { connector } = ctx;
366
+ if (connector.commands.length === 0) {
367
+ return [{ platform: this.id, action: "skip", detail: "connector declares no commands" }];
368
+ }
369
+ return connector.commands.map(
370
+ (cmd) => this.removeContentFile(this.commandPath(ctx, cmd.name), ctx.dryRun)
371
+ );
372
+ }
373
+ /** Render a command to md+frontmatter (description, model, subtask). */
374
+ renderCommand(cmd) {
375
+ const frontmatter = {};
376
+ if (cmd.description !== void 0) frontmatter.description = cmd.description;
377
+ if (cmd.model !== void 0) frontmatter.model = cmd.model;
378
+ if (cmd.subtask !== void 0) frontmatter.subtask = cmd.subtask;
379
+ if (cmd.extra) Object.assign(frontmatter, cmd.extra);
380
+ return this.renderFrontmatterMd(frontmatter, cmd.prompt);
381
+ }
382
+ // ── Skills ────────────────────────────────────────────────────────────────
383
+ installSkills(ctx) {
384
+ const { connector } = ctx;
385
+ if (connector.platforms[HOST]?.skills === false) {
386
+ return [{ platform: this.id, action: "skip", detail: "skills disabled for mimo-code" }];
387
+ }
388
+ if (connector.skills.length === 0) {
389
+ return [{ platform: this.id, action: "skip", detail: "connector declares no skills" }];
390
+ }
391
+ const changes = [];
392
+ for (const skill of connector.skills) {
393
+ const dir = this.skillDir(ctx, skill.name);
394
+ changes.push(
395
+ this.writeContentFile(join(dir, "SKILL.md"), this.renderSkill(skill), ctx.dryRun)
396
+ );
397
+ for (const [rel, contents] of Object.entries(skill.resources ?? {})) {
398
+ const target = this.resolveWithin(dir, rel);
399
+ if (target === null) {
400
+ changes.push({
401
+ platform: this.id,
402
+ action: "warn",
403
+ detail: `skill resource "${rel}" escapes the skill dir; skipped`
404
+ });
405
+ continue;
406
+ }
407
+ changes.push(this.writeContentFile(target, contents, ctx.dryRun));
408
+ }
409
+ }
410
+ return changes;
411
+ }
412
+ uninstallSkills(ctx) {
413
+ const { connector } = ctx;
414
+ if (connector.skills.length === 0) {
415
+ return [{ platform: this.id, action: "skip", detail: "connector declares no skills" }];
416
+ }
417
+ const changes = [];
418
+ for (const skill of connector.skills) {
419
+ const dir = this.skillDir(ctx, skill.name);
420
+ changes.push(this.removeContentFile(join(dir, "SKILL.md"), ctx.dryRun));
421
+ for (const rel of Object.keys(skill.resources ?? {})) {
422
+ const target = this.resolveWithin(dir, rel);
423
+ if (target === null) continue;
424
+ changes.push(this.removeContentFile(target, ctx.dryRun));
425
+ }
426
+ changes.push(this.removeDirIfEmpty(dir, ctx.dryRun));
427
+ }
428
+ return changes;
429
+ }
430
+ /**
431
+ * Render a skill's SKILL.md (UNIFORM across platforms): frontmatter
432
+ * (name, description + optional model, allowed-tools, disable-model-invocation)
433
+ * + markdown body.
434
+ */
435
+ renderSkill(skill) {
436
+ const frontmatter = {
437
+ name: skill.name,
438
+ description: skill.description
439
+ };
440
+ if (skill.model !== void 0) frontmatter.model = skill.model;
441
+ const allow = skill.tools?.allow;
442
+ if (allow && allow.length > 0) frontmatter["allowed-tools"] = allow.join(", ");
443
+ if (skill.disableModelInvocation !== void 0) {
444
+ frontmatter["disable-model-invocation"] = skill.disableModelInvocation;
445
+ }
446
+ if (skill.extra) Object.assign(frontmatter, skill.extra);
447
+ return this.renderFrontmatterMd(frontmatter, skill.body);
448
+ }
449
+ // ── Subagents ───────────────────────────────────────────────────────────────
450
+ installSubagents(ctx) {
451
+ const { connector } = ctx;
452
+ if (connector.platforms[HOST]?.subagents === false) {
453
+ return [{ platform: this.id, action: "skip", detail: "subagents disabled for mimo-code" }];
454
+ }
455
+ if (connector.subagents.length === 0) {
456
+ return [{ platform: this.id, action: "skip", detail: "connector declares no subagents" }];
457
+ }
458
+ return connector.subagents.map(
459
+ (agent) => this.writeContentFile(
460
+ this.subagentPath(ctx, agent.name),
461
+ this.renderSubagent(agent),
462
+ ctx.dryRun
463
+ )
464
+ );
465
+ }
466
+ uninstallSubagents(ctx) {
467
+ const { connector } = ctx;
468
+ if (connector.subagents.length === 0) {
469
+ return [{ platform: this.id, action: "skip", detail: "connector declares no subagents" }];
470
+ }
471
+ return connector.subagents.map(
472
+ (agent) => this.removeContentFile(this.subagentPath(ctx, agent.name), ctx.dryRun)
473
+ );
474
+ }
475
+ /**
476
+ * Render a subagent to md+frontmatter (OpenCode's shape, inherited by
477
+ * MiMoCode): (description, mode:"subagent", model, permission) with the system
478
+ * prompt as the body. `name` comes from the filename. A readonly agent gets a
479
+ * per-tool deny map (edit/bash) so it cannot mutate the workspace.
480
+ */
481
+ renderSubagent(agent) {
482
+ const frontmatter = {
483
+ description: agent.description,
484
+ mode: "subagent"
485
+ };
486
+ if (agent.model !== void 0) frontmatter.model = agent.model;
487
+ if (agent.readonly === true) {
488
+ frontmatter.permission = { edit: "deny", bash: "deny" };
489
+ }
490
+ if (agent.extra) Object.assign(frontmatter, agent.extra);
491
+ return this.renderFrontmatterMd(frontmatter, agent.prompt);
492
+ }
493
+ // ── ts-plugin synthesis ────────────────────────────────────────────────
494
+ /**
495
+ * Build ONE self-contained ESM bridge module for MiMoCode. The module imports
496
+ * nothing from agent-connector; it embeds the home-bin path + connector id and
497
+ * a `bridge()` helper that shells out to the universal hook entrypoint
498
+ * (`<homeBin> hook mimo-code <event> --connector <id>`), feeding the payload on
499
+ * stdin and JSON.parsing the normalized HookResponse from stdout (fail-open).
500
+ */
501
+ synthesizePlugin(ctx) {
502
+ const path = this.getHookConfigPath(ctx);
503
+ const contents = this.buildPluginSource(ctx);
504
+ return [{ path, contents, executable: false }];
505
+ }
506
+ /** Compose the generated plugin source with plain string concatenation. */
507
+ buildPluginSource(ctx) {
508
+ const homeBin = JSON.stringify(ctx.homeBinPath);
509
+ const connectorId = JSON.stringify(ctx.connector.id);
510
+ const events = ctx.connector.hookEvents.filter(
511
+ (e) => EVENT_TO_MIMOCODE[e] !== void 0
512
+ );
513
+ const has = (e) => events.includes(e);
514
+ const header = `/**
515
+ * AUTO-GENERATED by agent-connector \u2014 DO NOT EDIT.
516
+ *
517
+ * Self-contained MiMoCode plugin bridge for connector ${ctx.connector.id}.
518
+ * It imports nothing from agent-connector: every hook invocation shells out to
519
+ * the stable home binary's universal entrypoint and JSON-parses the normalized
520
+ * response. Fail-open: any bridge error degrades to "allow".
521
+ */
522
+ import { execFileSync, execSync } from "node:child_process";
523
+
524
+ const HOME_BIN = ${homeBin};
525
+ const CONNECTOR_ID = ${connectorId};
526
+
527
+ /**
528
+ * Invoke the universal hook entrypoint for one event.
529
+ * @param {string} event canonical event name (PreToolUse|PostToolUse|SessionStart)
530
+ * @param {object} payload MiMoCode-shaped payload posted on stdin
531
+ * @returns {object|null} normalized HookResponse, or null on any failure
532
+ */
533
+ function bridge(event, payload) {
534
+ try {
535
+ // On Windows HOME_BIN is the agent-connector.cmd launcher: Node cannot
536
+ // execFile a batch file, and shell+args is deprecated (DEP0190), so run one
537
+ // quoted command line via a shell. POSIX keeps the direct execFile (no shell).
538
+ const args = ["hook", "mimo-code", event, "--connector", CONNECTOR_ID];
539
+ const opts = { input: JSON.stringify(payload), encoding: "utf8" };
540
+ const stdout =
541
+ process.platform === "win32"
542
+ ? execSync([HOME_BIN, ...args].map((a) => '"' + a + '"').join(" "), opts)
543
+ : execFileSync(HOME_BIN, args, opts);
544
+ const text = (stdout || "").trim();
545
+ if (text === "") return { decision: "allow" };
546
+ return JSON.parse(text);
547
+ } catch {
548
+ // Fail-open \u2014 never wedge a tool call on a bridge error.
549
+ return null;
550
+ }
551
+ }
552
+ `;
553
+ const handlers = [];
554
+ if (has("PreToolUse")) {
555
+ handlers.push(` // PreToolUse \u2192 block (throw) / rewrite args (mutate output.args).
556
+ "tool.execute.before": async (input, output) => {
557
+ const payload = {
558
+ toolName: input.tool ?? "",
559
+ toolInput: (output && output.args) ?? {},
560
+ sessionId: input.sessionID ?? "",
561
+ projectDir: PROJECT_DIR,
562
+ };
563
+ const res = bridge("PreToolUse", payload);
564
+ if (!res) return;
565
+ // MiMoCode has no "ask" gate \u2014 degrade "ask" to a block (safe direction).
566
+ if (res.decision === "deny" || res.decision === "ask") {
567
+ throw new Error(res.reason || "Blocked by ${ctx.connector.id}");
568
+ }
569
+ if (res.updatedInput && output && output.args) {
570
+ Object.assign(output.args, res.updatedInput);
571
+ }
572
+ },`);
573
+ }
574
+ if (has("PostToolUse")) {
575
+ handlers.push(` // PostToolUse \u2192 observe / rewrite tool output (mutate output.output).
576
+ "tool.execute.after": async (input, output) => {
577
+ const payload = {
578
+ toolName: input.tool ?? "",
579
+ toolInput: input.args ?? {},
580
+ toolOutput: output ? output.output : undefined,
581
+ sessionId: input.sessionID ?? "",
582
+ projectDir: PROJECT_DIR,
583
+ };
584
+ const res = bridge("PostToolUse", payload);
585
+ if (!res) return;
586
+ if (typeof res.updatedOutput === "string" && output) {
587
+ output.output = res.updatedOutput;
588
+ }
589
+ },`);
590
+ }
591
+ if (has("SessionStart")) {
592
+ handlers.push(` // SessionStart surrogate \u2192 inject context into the system prompt.
593
+ // MiMoCode has no real SessionStart hook (inherited from OpenCode
594
+ // #14808 / #5409); experimental.chat.system.transform is the injection point.
595
+ "experimental.chat.system.transform": async (input, output) => {
596
+ const payload = {
597
+ sessionId: (input && input.sessionID) ?? "",
598
+ projectDir: PROJECT_DIR,
599
+ };
600
+ const res = bridge("SessionStart", payload);
601
+ if (!res) return;
602
+ if (res.additionalContext && output && Array.isArray(output.system)) {
603
+ // Insert at index 1 (after the header) to preserve the prompt-cache
604
+ // fold (header must remain system[0]).
605
+ output.system.splice(1, 0, res.additionalContext);
606
+ }
607
+ },`);
608
+ }
609
+ if (has("UserPromptSubmit")) {
610
+ handlers.push(` // UserPromptSubmit \u2192 context injection on prompt submission.
611
+ // chat.message fires on the USER's prompt; output.message is a UserMessage
612
+ // and output.parts holds the prompt parts. Inject context by PUSHING a text
613
+ // part onto output.parts. chat.message has NO documented block/abort
614
+ // mechanism, so a "deny" decision degrades to a NO-OP (best-effort) \u2014 same
615
+ // way the tool/system handlers degrade decisions this host cannot honor.
616
+ "chat.message": async (input, output) => {
617
+ const promptText =
618
+ output && Array.isArray(output.parts)
619
+ ? output.parts
620
+ .filter((p) => p && p.type === "text" && typeof p.text === "string")
621
+ .map((p) => p.text)
622
+ .join("\\n")
623
+ : "";
624
+ const res = bridge("UserPromptSubmit", {
625
+ prompt: promptText,
626
+ sessionId: (input && input.sessionID) || "",
627
+ projectDir: PROJECT_DIR,
628
+ });
629
+ if (!res) return;
630
+ if (res.additionalContext && output && Array.isArray(output.parts)) {
631
+ output.parts.push({ type: "text", text: res.additionalContext });
632
+ }
633
+ },`);
634
+ }
635
+ const factory = `
636
+ export default async function (ctx) {
637
+ // ctx.directory is the MiMoCode project root; fall back to cwd.
638
+ const PROJECT_DIR =
639
+ (ctx && (ctx.directory || (ctx.worktree && ctx.worktree.path))) ||
640
+ process.cwd();
641
+
642
+ return {
643
+ ${handlers.join("\n")}
644
+ };
645
+ }
646
+ `;
647
+ return header + factory;
648
+ }
649
+ // ── Runtime: parse OUR bridge payload → normalized event ───────────────
650
+ /**
651
+ * `raw` is the payload OUR generated plugin posts (NOT a host-native shape):
652
+ * { toolName, toolInput, toolOutput?, isError?, sessionId, projectDir }
653
+ * so this maps straight through.
654
+ */
655
+ parseEvent(event, raw) {
656
+ const input = raw ?? {};
657
+ const base = {
658
+ hostPlatform: HOST,
659
+ connectorId: "",
660
+ sessionId: typeof input.sessionId === "string" ? input.sessionId : "",
661
+ raw,
662
+ ...typeof input.projectDir === "string" ? { projectDir: input.projectDir } : {}
663
+ };
664
+ switch (event) {
665
+ case "PreToolUse": {
666
+ const ev = {
667
+ ...base,
668
+ toolName: input.toolName ?? "",
669
+ toolInput: input.toolInput ?? {}
670
+ };
671
+ return ev;
672
+ }
673
+ case "PostToolUse": {
674
+ const ev = {
675
+ ...base,
676
+ toolName: input.toolName ?? "",
677
+ toolInput: input.toolInput ?? {},
678
+ ...typeof input.toolOutput === "string" ? { toolOutput: input.toolOutput } : {},
679
+ ...typeof input.isError === "boolean" ? { isError: input.isError } : {}
680
+ };
681
+ return ev;
682
+ }
683
+ case "SessionStart": {
684
+ const ev = { ...base, source: "startup" };
685
+ return ev;
686
+ }
687
+ case "UserPromptSubmit": {
688
+ const ev = {
689
+ ...base,
690
+ prompt: typeof input.prompt === "string" ? input.prompt : ""
691
+ };
692
+ return ev;
693
+ }
694
+ default:
695
+ return { ...base, source: "startup" };
696
+ }
697
+ }
698
+ // ── Runtime: normalized response → reply the generated bridge parses ───
699
+ /**
700
+ * Our generated bridge consumes this stdout directly, so the reply body is the
701
+ * NORMALIZED HookResponse itself — the bridge JSON.parses it and reads
702
+ * decision / updatedInput / updatedOutput / additionalContext.
703
+ */
704
+ formatReply(_event, response) {
705
+ return {
706
+ exitCode: 0,
707
+ stdout: JSON.stringify(response ?? { decision: "allow" })
708
+ };
709
+ }
710
+ // ── Diagnostics ────────────────────────────────────────────────────────
711
+ getHealthChecks(ctx) {
712
+ const serverPath = this.getServerConfigPath(ctx);
713
+ const pluginPath = this.getHookConfigPath(ctx);
714
+ const connectorId = ctx.connector.id;
715
+ const hasHooks = ctx.connector.hookEvents.length > 0;
716
+ const checks = [
717
+ {
718
+ name: `${this.name}: mimocode.json present`,
719
+ check: () => existsSync(serverPath) ? { status: "OK", detail: serverPath } : { status: "FAIL", detail: `not found: ${serverPath}` }
720
+ },
721
+ {
722
+ name: `${this.name}: mcp.${connectorId} registered`,
723
+ check: () => {
724
+ if (!ctx.connector.server) {
725
+ return { status: "OK", detail: "no MCP server declared" };
726
+ }
727
+ const cfg = this.readJson(
728
+ serverPath
729
+ );
730
+ const bucket = cfg?.[MCP_ROOT_KEY];
731
+ if (!cfg || !bucket) {
732
+ return { status: "FAIL", detail: `no ${MCP_ROOT_KEY} in ${serverPath}` };
733
+ }
734
+ return connectorId in bucket ? { status: "OK", detail: `${MCP_ROOT_KEY}.${connectorId} present` } : {
735
+ status: "FAIL",
736
+ detail: `no ${MCP_ROOT_KEY}.${connectorId} in ${serverPath}`
737
+ };
738
+ }
739
+ },
740
+ {
741
+ name: `${this.name}: plugin module present`,
742
+ check: () => {
743
+ if (!hasHooks) return { status: "OK", detail: "no hooks declared" };
744
+ return existsSync(pluginPath) ? { status: "OK", detail: pluginPath } : { status: "FAIL", detail: `not found: ${pluginPath}` };
745
+ }
746
+ }
747
+ ];
748
+ for (const cmd of ctx.connector.commands) {
749
+ const p = this.commandPath(ctx, cmd.name);
750
+ checks.push({
751
+ name: `${this.name}: command ${cmd.name} present`,
752
+ check: () => existsSync(p) ? { status: "OK", detail: p } : { status: "FAIL", detail: `not found: ${p}` }
753
+ });
754
+ }
755
+ for (const skill of ctx.connector.skills) {
756
+ const p = join(this.skillDir(ctx, skill.name), "SKILL.md");
757
+ checks.push({
758
+ name: `${this.name}: skill ${skill.name} present`,
759
+ check: () => existsSync(p) ? { status: "OK", detail: p } : { status: "FAIL", detail: `not found: ${p}` }
760
+ });
761
+ }
762
+ for (const agent of ctx.connector.subagents) {
763
+ const p = this.subagentPath(ctx, agent.name);
764
+ checks.push({
765
+ name: `${this.name}: subagent ${agent.name} present`,
766
+ check: () => existsSync(p) ? { status: "OK", detail: p } : { status: "FAIL", detail: `not found: ${p}` }
767
+ });
768
+ }
769
+ return checks;
770
+ }
771
+ /** Read a file, returning undefined on any error (idempotency compare). */
772
+ safeRead(path) {
773
+ try {
774
+ return readFileSync(path, "utf8");
775
+ } catch {
776
+ return void 0;
777
+ }
778
+ }
779
+ };
780
+ function ensureDir(dir) {
781
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
782
+ }
783
+ var adapter = new MiMoCodeAdapter();
784
+ var mimo_code_default = adapter;
785
+ export {
786
+ MiMoCodeAdapter,
787
+ adapter,
788
+ mimo_code_default as default
789
+ };
790
+ //# sourceMappingURL=mimo-code-FTMGSKMQ.js.map