@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.
- package/README.md +180 -16
- package/dist/action-WT7GVRCC.js +55 -0
- package/dist/action-WT7GVRCC.js.map +1 -0
- package/dist/amazon-q-COAL3764.js +276 -0
- package/dist/amazon-q-COAL3764.js.map +1 -0
- package/dist/{amp-YGV66GJT.js → amp-4U33CU7E.js} +81 -5
- package/dist/amp-4U33CU7E.js.map +1 -0
- package/dist/{antigravity-7IBYFO6P.js → antigravity-AMKKKHS4.js} +4 -4
- package/dist/{antigravity-cli-JEOUP2VN.js → antigravity-cli-XERXEH5W.js} +4 -4
- package/dist/{chunk-3JCRUROU.js → chunk-24IDL5RW.js} +13 -4
- package/dist/chunk-24IDL5RW.js.map +1 -0
- package/dist/chunk-2AVLX7GH.js +351 -0
- package/dist/chunk-2AVLX7GH.js.map +1 -0
- package/dist/{chunk-GSGDG2BV.js → chunk-4ENJK5S2.js} +106 -176
- package/dist/chunk-4ENJK5S2.js.map +1 -0
- package/dist/{chunk-ZAM4M7LI.js → chunk-5OWS5CMA.js} +136 -296
- package/dist/chunk-5OWS5CMA.js.map +1 -0
- package/dist/{chunk-PD4VFEBY.js → chunk-CPLK7GTE.js} +79 -5
- package/dist/chunk-CPLK7GTE.js.map +1 -0
- package/dist/chunk-DCKFYWGM.js +237 -0
- package/dist/chunk-DCKFYWGM.js.map +1 -0
- package/dist/chunk-EGCBZ2DC.js +83 -0
- package/dist/chunk-EGCBZ2DC.js.map +1 -0
- package/dist/chunk-F5RX62T7.js +21 -0
- package/dist/chunk-F5RX62T7.js.map +1 -0
- package/dist/chunk-FAHIMJRU.js +177 -0
- package/dist/chunk-FAHIMJRU.js.map +1 -0
- package/dist/{chunk-MEH7GC7R.js → chunk-I5MLE6S7.js} +33 -3
- package/dist/chunk-I5MLE6S7.js.map +1 -0
- package/dist/{chunk-KVAAFYRR.js → chunk-KHO5WNTP.js} +2 -18
- package/dist/chunk-KHO5WNTP.js.map +1 -0
- package/dist/{chunk-KQSNDFBN.js → chunk-LQRAPXS4.js} +3 -3
- package/dist/{chunk-3RBLIHPZ.js → chunk-O5TWCIJN.js} +27 -17
- package/dist/chunk-O5TWCIJN.js.map +1 -0
- package/dist/{chunk-WNREHWKL.js → chunk-QREL7LZJ.js} +5 -4
- package/dist/chunk-QREL7LZJ.js.map +1 -0
- package/dist/{chunk-KMAKDD3Z.js → chunk-RIMVWCZY.js} +37 -12
- package/dist/chunk-RIMVWCZY.js.map +1 -0
- package/dist/{openclaw-UZ5BMCGQ.js → chunk-URHQMFB3.js} +210 -41
- package/dist/chunk-URHQMFB3.js.map +1 -0
- package/dist/chunk-WZA4CSGJ.js +28 -0
- package/dist/chunk-WZA4CSGJ.js.map +1 -0
- package/dist/chunk-XJ4S47CT.js +44 -0
- package/dist/chunk-XJ4S47CT.js.map +1 -0
- package/dist/{claude-code-4275HQU4.js → claude-code-W4PUFZL5.js} +194 -52
- package/dist/claude-code-W4PUFZL5.js.map +1 -0
- package/dist/cli/sdk.js +5 -3
- package/dist/cli/sdk.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cline-WRYMNS7Z.js +442 -0
- package/dist/cline-WRYMNS7Z.js.map +1 -0
- package/dist/{codebuff-LE3KBZGM.js → codebuff-DH5Y7GAT.js} +106 -5
- package/dist/codebuff-DH5Y7GAT.js.map +1 -0
- package/dist/{codex-4BY2ZJIW.js → codex-DDHIV767.js} +43 -11
- package/dist/codex-DDHIV767.js.map +1 -0
- package/dist/continue-K43SUHLQ.js +790 -0
- package/dist/continue-K43SUHLQ.js.map +1 -0
- package/dist/{copilot-cli-IPISKO3V.js → copilot-cli-Q3SSPT4V.js} +70 -12
- package/dist/copilot-cli-Q3SSPT4V.js.map +1 -0
- package/dist/{crush-X3E25EQG.js → crush-MBEH2R3I.js} +130 -6
- package/dist/crush-MBEH2R3I.js.map +1 -0
- package/dist/{cursor-UH3W622V.js → cursor-TMBXDYR4.js} +119 -16
- package/dist/cursor-TMBXDYR4.js.map +1 -0
- package/dist/{detect-U3E7NVNW.js → detect-KG5FG3EV.js} +5 -3
- package/dist/{detect-U3E7NVNW.js.map → detect-KG5FG3EV.js.map} +1 -1
- package/dist/{doctor-DZTWED4O.js → doctor-FLZF34Y3.js} +226 -96
- package/dist/doctor-FLZF34Y3.js.map +1 -0
- package/dist/{droid-T5VBRXZF.js → droid-JC2NT6S4.js} +298 -10
- package/dist/droid-JC2NT6S4.js.map +1 -0
- package/dist/{gemini-cli-ZSUK55UA.js → gemini-cli-ILR7M5X3.js} +62 -10
- package/dist/gemini-cli-ILR7M5X3.js.map +1 -0
- package/dist/{goose-M3RR3CZG.js → goose-SFO5BBGW.js} +112 -10
- package/dist/goose-SFO5BBGW.js.map +1 -0
- package/dist/{hermes-CUNKIAR5.js → hermes-3N7WN4WS.js} +329 -15
- package/dist/hermes-3N7WN4WS.js.map +1 -0
- package/dist/{hook-EVY7LOUQ.js → hook-XR3Z45FB.js} +16 -10
- package/dist/hook-XR3Z45FB.js.map +1 -0
- package/dist/index.d.ts +71 -3
- package/dist/index.js +23 -3
- package/dist/{install-OWAS5UIE.js → install-BSR62UHJ.js} +12 -9
- package/dist/{install-OWAS5UIE.js.map → install-BSR62UHJ.js.map} +1 -1
- package/dist/introspect-Nw8hTReX.d.ts +53 -0
- package/dist/{jetbrains-copilot-C3HDGGML.js → jetbrains-copilot-NRZHI5PC.js} +85 -18
- package/dist/jetbrains-copilot-NRZHI5PC.js.map +1 -0
- package/dist/{kilo-PKAJCWPX.js → kilo-5NWOXVSD.js} +16 -16
- package/dist/kilo-5NWOXVSD.js.map +1 -0
- package/dist/{kilo-cli-QWUVXG2O.js → kilo-cli-UZ7R5UOP.js} +3 -3
- package/dist/{kimi-EVD7YKXS.js → kimi-2YFK2GBA.js} +87 -25
- package/dist/kimi-2YFK2GBA.js.map +1 -0
- package/dist/{kiro-UQRY6EIO.js → kiro-YL6AWUYG.js} +3 -3
- package/dist/{leaderboard-CSWHI4UY.js → leaderboard-MJG24O35.js} +2 -2
- package/dist/mimo-code-FTMGSKMQ.js +790 -0
- package/dist/mimo-code-FTMGSKMQ.js.map +1 -0
- package/dist/{mux-5CI5FP2W.js → mux-5XZFB44C.js} +156 -6
- package/dist/mux-5XZFB44C.js.map +1 -0
- package/dist/nemoclaw-W5KEJ3WX.js +58 -0
- package/dist/nemoclaw-W5KEJ3WX.js.map +1 -0
- package/dist/{omp-AXA7XY22.js → omp-2QMEY6HD.js} +138 -18
- package/dist/omp-2QMEY6HD.js.map +1 -0
- package/dist/openclaw-EXJEFNZI.js +18 -0
- package/dist/openclaw-EXJEFNZI.js.map +1 -0
- package/dist/{opencode-NZBRLJN3.js → opencode-WNXYJR4X.js} +84 -27
- package/dist/opencode-WNXYJR4X.js.map +1 -0
- package/dist/{package-WFOS4QVU.js → package-ACZXJHWB.js} +8 -6
- package/dist/{package-WFOS4QVU.js.map → package-ACZXJHWB.js.map} +1 -1
- package/dist/{pi-WMTUX6ES.js → pi-PVVPVWKG.js} +3 -2
- package/dist/{pi-WMTUX6ES.js.map → pi-PVVPVWKG.js.map} +1 -1
- package/dist/{qwen-code-MRRIUQ3P.js → qwen-code-G4CXKUHR.js} +426 -10
- package/dist/qwen-code-G4CXKUHR.js.map +1 -0
- package/dist/{roo-code-NI5UU6JB.js → roo-code-67TYJIQR.js} +137 -4
- package/dist/roo-code-67TYJIQR.js.map +1 -0
- package/dist/runtime/index.d.ts +96 -2
- package/dist/runtime/index.js +19 -10
- package/dist/sdk/index.d.ts +52 -0
- package/dist/sdk/index.js +82 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/test.d.ts +74 -0
- package/dist/sdk/test.js +292 -0
- package/dist/sdk/test.js.map +1 -0
- package/dist/{serve-3C2VFPAY.js → serve-GER7URPR.js} +13 -9
- package/dist/{serve-3C2VFPAY.js.map → serve-GER7URPR.js.map} +1 -1
- package/dist/{status-MKQYX5MW.js → status-LAJE3BM5.js} +10 -7
- package/dist/{status-MKQYX5MW.js.map → status-LAJE3BM5.js.map} +1 -1
- package/dist/statusline-L376ZPZU.js +60 -0
- package/dist/statusline-L376ZPZU.js.map +1 -0
- package/dist/{telemetry-3DN6XGYB.js → telemetry-B3DX3PWU.js} +6 -4
- package/dist/{telemetry-3DN6XGYB.js.map → telemetry-B3DX3PWU.js.map} +1 -1
- package/dist/{trae-CQC72RHH.js → trae-7CZZGOGI.js} +99 -5
- package/dist/trae-7CZZGOGI.js.map +1 -0
- package/dist/{types-CNigfEuR.d.ts → types-CRVrFWeU.d.ts} +322 -18
- package/dist/{uninstall-WG6PFWF3.js → uninstall-ZYTW7YMZ.js} +12 -9
- package/dist/{uninstall-WG6PFWF3.js.map → uninstall-ZYTW7YMZ.js.map} +1 -1
- package/dist/{upgrade-32NC2RKI.js → upgrade-4LUEEOZW.js} +12 -9
- package/dist/{upgrade-32NC2RKI.js.map → upgrade-4LUEEOZW.js.map} +1 -1
- package/dist/{usage-CJOM2WQM.js → usage-ZFBVQF3H.js} +2 -2
- package/dist/{usage-event-472X3DOY.js → usage-event-GZ32AGEB.js} +12 -8
- package/dist/{usage-event-472X3DOY.js.map → usage-event-GZ32AGEB.js.map} +1 -1
- package/dist/{vscode-copilot-4RVRTCKV.js → vscode-copilot-MF2CDNMT.js} +23 -10
- package/dist/vscode-copilot-MF2CDNMT.js.map +1 -0
- package/dist/{warp-X6MB67NT.js → warp-VKG55TAT.js} +55 -4
- package/dist/warp-VKG55TAT.js.map +1 -0
- package/dist/windsurf-FU5G4SHF.js +443 -0
- package/dist/windsurf-FU5G4SHF.js.map +1 -0
- package/dist/{zed-NTCYW4V2.js → zed-SFUHL3GS.js} +3 -3
- package/package.json +10 -2
- package/dist/amp-YGV66GJT.js.map +0 -1
- package/dist/chunk-3JCRUROU.js.map +0 -1
- package/dist/chunk-3RBLIHPZ.js.map +0 -1
- package/dist/chunk-GSGDG2BV.js.map +0 -1
- package/dist/chunk-KMAKDD3Z.js.map +0 -1
- package/dist/chunk-KVAAFYRR.js.map +0 -1
- package/dist/chunk-MEH7GC7R.js.map +0 -1
- package/dist/chunk-NJU275US.js +0 -238
- package/dist/chunk-NJU275US.js.map +0 -1
- package/dist/chunk-PD4VFEBY.js.map +0 -1
- package/dist/chunk-WNREHWKL.js.map +0 -1
- package/dist/chunk-ZAM4M7LI.js.map +0 -1
- package/dist/claude-code-4275HQU4.js.map +0 -1
- package/dist/codebuff-LE3KBZGM.js.map +0 -1
- package/dist/codex-4BY2ZJIW.js.map +0 -1
- package/dist/copilot-cli-IPISKO3V.js.map +0 -1
- package/dist/crush-X3E25EQG.js.map +0 -1
- package/dist/cursor-UH3W622V.js.map +0 -1
- package/dist/doctor-DZTWED4O.js.map +0 -1
- package/dist/droid-T5VBRXZF.js.map +0 -1
- package/dist/gemini-cli-ZSUK55UA.js.map +0 -1
- package/dist/goose-M3RR3CZG.js.map +0 -1
- package/dist/hermes-CUNKIAR5.js.map +0 -1
- package/dist/hook-EVY7LOUQ.js.map +0 -1
- package/dist/jetbrains-copilot-C3HDGGML.js.map +0 -1
- package/dist/kilo-PKAJCWPX.js.map +0 -1
- package/dist/kimi-EVD7YKXS.js.map +0 -1
- package/dist/mux-5CI5FP2W.js.map +0 -1
- package/dist/omp-AXA7XY22.js.map +0 -1
- package/dist/openclaw-UZ5BMCGQ.js.map +0 -1
- package/dist/opencode-NZBRLJN3.js.map +0 -1
- package/dist/qwen-code-MRRIUQ3P.js.map +0 -1
- package/dist/roo-code-NI5UU6JB.js.map +0 -1
- package/dist/trae-CQC72RHH.js.map +0 -1
- package/dist/vscode-copilot-4RVRTCKV.js.map +0 -1
- package/dist/warp-X6MB67NT.js.map +0 -1
- /package/dist/{antigravity-7IBYFO6P.js.map → antigravity-AMKKKHS4.js.map} +0 -0
- /package/dist/{antigravity-cli-JEOUP2VN.js.map → antigravity-cli-XERXEH5W.js.map} +0 -0
- /package/dist/{chunk-KQSNDFBN.js.map → chunk-LQRAPXS4.js.map} +0 -0
- /package/dist/{kilo-cli-QWUVXG2O.js.map → kilo-cli-UZ7R5UOP.js.map} +0 -0
- /package/dist/{kiro-UQRY6EIO.js.map → kiro-YL6AWUYG.js.map} +0 -0
- /package/dist/{leaderboard-CSWHI4UY.js.map → leaderboard-MJG24O35.js.map} +0 -0
- /package/dist/{usage-CJOM2WQM.js.map → usage-ZFBVQF3H.js.map} +0 -0
- /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
|