@luchenglong/cli-bridge 0.0.0-stage → 0.2.2
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/CHANGELOG.md +148 -0
- package/LICENSE +21 -0
- package/MCP-POLICY.md +53 -0
- package/PROVIDER-CONTRACT.md +235 -0
- package/README.en.md +98 -0
- package/README.md +150 -2
- package/assets/indicator-states.svg +62 -0
- package/bin/agy-quota.mjs +223 -0
- package/core/bridge-kit.mjs +549 -0
- package/core/scheduling.mjs +149 -0
- package/docs/ARCHITECTURE.md +117 -0
- package/docs/FALLBACK-AND-INDICATOR.md +93 -0
- package/docs/INSTALL.md +118 -0
- package/docs/MCP-POLICY.zh.md +49 -0
- package/docs/MIGRATION.md +43 -0
- package/docs/RELEASE-SOP.md +77 -0
- package/docs/history/agy/CHANGELOG.md +326 -0
- package/docs/history/codebuddy/CHANGELOG.md +668 -0
- package/docs/history/mimo/CHANGELOG.md +346 -0
- package/dynamic/agy.host.js +1237 -0
- package/dynamic/agy.host.template.mjs +140 -0
- package/dynamic/codebuddy.host.js +1787 -0
- package/dynamic/codebuddy.host.template.mjs +162 -0
- package/dynamic/mimo.host.js +1732 -0
- package/dynamic/mimo.host.template.mjs +169 -0
- package/home-plugin/cli-bridge-indicator/cordis.patch.yml +31 -0
- package/home-plugin/cli-bridge-indicator/lib/client.js +606 -0
- package/home-plugin/cli-bridge-indicator/lib/index.mjs +420 -0
- package/home-plugin/cli-bridge-indicator/lib/preset-declarations.mjs +137 -0
- package/home-plugin/cli-bridge-indicator/lib/settings.mjs +300 -0
- package/mcp/agy-mcp-server.mjs +6 -0
- package/mcp/cli-bridge-mcp.mjs +184 -0
- package/mcp/codebuddy-mcp-server.mjs +6 -0
- package/mcp/mimo-mcp-server.mjs +6 -0
- package/mcp/providers/agy.mjs +254 -0
- package/mcp/providers/codebuddy.mjs +397 -0
- package/mcp/providers/mimo.mjs +290 -0
- package/package.json +87 -4
- package/preset/agy-first/agent.cordis.yml +141 -0
- package/preset/agy-first/cli-bridge.mjs +169 -0
- package/preset/agy-first/plugin-list.mjs +26 -0
- package/preset/agy-first/preset.yml +3 -0
- package/preset/codebuddy-first/agent.cordis.yml +141 -0
- package/preset/codebuddy-first/cli-bridge.mjs +188 -0
- package/preset/codebuddy-first/plugin-list.mjs +20 -0
- package/preset/codebuddy-first/preset.yml +3 -0
- package/preset/mimo-first/agent.cordis.yml +143 -0
- package/preset/mimo-first/cli-bridge.mjs +236 -0
- package/preset/mimo-first/plugin-list.mjs +39 -0
- package/preset/mimo-first/preset.yml +3 -0
- package/preset/standard-base.mjs +102 -0
- package/providers/agy.mjs +398 -0
- package/providers/codebuddy.mjs +926 -0
- package/providers/mimo.mjs +864 -0
- package/providers/registry.mjs +34 -0
- package/scripts/build.mjs +235 -0
- package/scripts/deploy.mjs +61 -0
- package/scripts/verify.mjs +148 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
// mcp/providers/mimo.mjs — mimo MCP 工具面(契约 §4;移植自旧 mimo-mcp-server.mjs 的
|
|
2
|
+
// runMimo/runDesktop/handleTool/TOOLS/renderResultText,framing/生命周期归 keystone
|
|
3
|
+
// 统一服务器 mcp/cli-bridge-mcp.mjs)。
|
|
4
|
+
//
|
|
5
|
+
// 旧语义保持:
|
|
6
|
+
// - mimocode:直接 spawn mimo CLI(MIMO_BIN/MIMO_CLI > ~/.mimocode/bin/mimo[.exe] >
|
|
7
|
+
// 'mimo';.mjs/.cjs/.js 目标经 process.execPath 包装,供测试 fixture 使用)。
|
|
8
|
+
// 实时折叠走 bridge-kit 行流 + engine.foldEvent(旧 foldStep 语义)。
|
|
9
|
+
// - mimo-desktop:委托 provider.execute(provider.delegates('mimo-desktop')=true);
|
|
10
|
+
// 非 spawn 走本地 HTTP。begin/end 由本模块做(MCP 无 kit runner)。
|
|
11
|
+
// - cwd 兜底 = env MIMO_MCP_CWD || MIMO_BRIDGE_CWD || process.cwd()(旧 CWD_FALLBACK;
|
|
12
|
+
// 旧仓曾硬编码机器绝对路径,新实现按 agy 定案改 process.cwd() 兜底)。
|
|
13
|
+
// - 无 ctx.emit:状态走快照文件 {homeDir}/cli-bridge-mcp-mimo.json(keystone
|
|
14
|
+
// indicator readMcpFile 只认 'cli-bridge-mcp-<id>.json' 模式,Lead 复核纠错定案)
|
|
15
|
+
// (bridge-kit buildMcpBridgePayload + 原子写 tmp→rename;写失败静默,红线:绝不影响
|
|
16
|
+
// 工具结果)。旧 mcp-live.json 兼容读归 keystone,本面不写旧文件。
|
|
17
|
+
// - 限流(isLimited)触发即落 fallbackResult(旧 handleTool verbatim:run 内部各退出
|
|
18
|
+
// 路径均已 engine.end,此处仅改返回值对象,绝不二次 engine.end —— 防 runs 虚标/计数下溢)。
|
|
19
|
+
|
|
20
|
+
import { spawn as nodeSpawn } from 'node:child_process'
|
|
21
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from 'node:fs'
|
|
22
|
+
import { dirname, join as joinPath, resolve as resolvePath } from 'node:path'
|
|
23
|
+
|
|
24
|
+
import defineProvider from '../../providers/mimo.mjs'
|
|
25
|
+
import {
|
|
26
|
+
createStatusEngine, createLineStream, buildMcpBridgePayload,
|
|
27
|
+
fallbackResult, shortLabel, renderStatus
|
|
28
|
+
} from '../../core/bridge-kit.mjs'
|
|
29
|
+
|
|
30
|
+
export const meta = { id: 'mimo', label: 'MIMO', version: '0.2.2' }
|
|
31
|
+
|
|
32
|
+
const BACKEND_DESKTOP = 'mimo-desktop'
|
|
33
|
+
const SETTINGS_FILE_NAME = 'cli-bridge-settings.json'
|
|
34
|
+
|
|
35
|
+
// 统一设置文件分区读(与 preset 同规则;MCP 子进程无 ctx.get,只读 env/文件)。
|
|
36
|
+
function resolveSettingsFile(env) {
|
|
37
|
+
try {
|
|
38
|
+
if (env.CLI_BRIDGE_SETTINGS_FILE) return env.CLI_BRIDGE_SETTINGS_FILE
|
|
39
|
+
if (env.DSH_HOME) return joinPath(env.DSH_HOME, SETTINGS_FILE_NAME)
|
|
40
|
+
if (process.platform === 'win32' && env.APPDATA) return joinPath(env.APPDATA, 'DSH Desktop', 'dsh-home', SETTINGS_FILE_NAME)
|
|
41
|
+
if (env.HOME) return joinPath(env.HOME, '.dsh', SETTINGS_FILE_NAME)
|
|
42
|
+
} catch (e) { }
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
function readPartition(env) {
|
|
46
|
+
const p = resolveSettingsFile(env)
|
|
47
|
+
if (!p) return { partition: null, path: null, home: null }
|
|
48
|
+
try {
|
|
49
|
+
const all = JSON.parse(readFileSync(p, 'utf8'))
|
|
50
|
+
return { partition: (all && typeof all === 'object' && all.mimo) ? all.mimo : null, path: p, home: dirname(p) }
|
|
51
|
+
} catch (e) {
|
|
52
|
+
return { partition: null, path: p, home: dirname(p) }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const provider = defineProvider({
|
|
57
|
+
env: process.env,
|
|
58
|
+
process,
|
|
59
|
+
nodeFs: { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync },
|
|
60
|
+
nodeChild: { spawn: nodeSpawn },
|
|
61
|
+
nodePath: { join: joinPath, dirname },
|
|
62
|
+
readSettings: () => readPartition(process.env)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
let lastIo = null
|
|
66
|
+
function publishSnapshot(snapshot) {
|
|
67
|
+
const io = lastIo
|
|
68
|
+
if (!io) return
|
|
69
|
+
try {
|
|
70
|
+
// 文件名必须 'cli-bridge-mcp-<id>.json':keystone indicator 的 readMcpFile 按此模式
|
|
71
|
+
// 枚举(agy 面同款);写成别的名字 MCP 形态活动永远进不了状态灯(Lead 复核纠错)。
|
|
72
|
+
const target = io.path.join(io.homeDir, 'cli-bridge-mcp-mimo.json')
|
|
73
|
+
const payload = buildMcpBridgePayload(snapshot, process.pid, Date.now())
|
|
74
|
+
const tmp = target + '.' + process.pid + '.tmp'
|
|
75
|
+
try { io.fs.mkdirSync(io.path.dirname(target), { recursive: true }) } catch (e) { }
|
|
76
|
+
io.fs.writeFileSync(tmp, JSON.stringify(payload), 'utf8')
|
|
77
|
+
io.fs.renameSync(tmp, target)
|
|
78
|
+
} catch (e) { /* 灯数据尽力而为,绝不影响工具调用 */ }
|
|
79
|
+
}
|
|
80
|
+
// MCP 形态不 attach(无 ctx.subprocess),用本模块自持 engine。
|
|
81
|
+
const engine = createStatusEngine({ provider, publish: publishSnapshot })
|
|
82
|
+
provider.engine = engine
|
|
83
|
+
|
|
84
|
+
// ── exe / cwd 解析(旧 resolveMimoExe + CWD_FALLBACK verbatim 语义)──────────────
|
|
85
|
+
function exePrefix() {
|
|
86
|
+
const env = process.env
|
|
87
|
+
const candidates = []
|
|
88
|
+
if (env.MIMO_BIN) candidates.push(env.MIMO_BIN)
|
|
89
|
+
if (env.MIMO_CLI) candidates.push(env.MIMO_CLI)
|
|
90
|
+
if (env.USERPROFILE) candidates.push(env.USERPROFILE + '\\.mimocode\\bin\\mimo.exe')
|
|
91
|
+
if (env.HOME) {
|
|
92
|
+
candidates.push(env.HOME + '\\.mimocode\\bin\\mimo.exe')
|
|
93
|
+
candidates.push(env.HOME + '/.mimocode/bin/mimo')
|
|
94
|
+
}
|
|
95
|
+
for (const exe of candidates) {
|
|
96
|
+
try { if (exe && existsSync(exe)) return /\.(mjs|cjs|js)$/i.test(exe) ? [process.execPath, exe] : [exe] } catch (e) { }
|
|
97
|
+
}
|
|
98
|
+
const bin = env.MIMO_BIN || env.MIMO_CLI || ''
|
|
99
|
+
if (bin) return /\.(mjs|cjs|js)$/i.test(bin) ? [process.execPath, bin] : [bin]
|
|
100
|
+
return ['mimo']
|
|
101
|
+
}
|
|
102
|
+
function resolveCwd(a) {
|
|
103
|
+
if (a && a.cwd) return resolvePath(String(a.cwd))
|
|
104
|
+
return process.env.MIMO_MCP_CWD || process.env.MIMO_BRIDGE_CWD || process.cwd()
|
|
105
|
+
}
|
|
106
|
+
function preferredBackendFromEnv() {
|
|
107
|
+
const env = process.env
|
|
108
|
+
return env.MIMO_BRIDGE_BACKEND || env.MIMO_BACKEND || (readPartition(env).partition || {}).preferredBackend || null
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── spawn(旧 runMimo verbatim 语义;实时折叠走 bridge-kit 行流)────────────────
|
|
112
|
+
function runMimo(args) {
|
|
113
|
+
return new Promise((resolve) => {
|
|
114
|
+
const cwd = resolveCwd(args)
|
|
115
|
+
const built = provider.buildArgv(exePrefix(), args, { planActive: false, defaultMode: 'auto', backend: 'mimocode' })
|
|
116
|
+
engine.begin(cwd)
|
|
117
|
+
let child
|
|
118
|
+
try {
|
|
119
|
+
child = nodeSpawn(built.argv[0], built.argv.slice(1), { cwd, stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true })
|
|
120
|
+
} catch (e) {
|
|
121
|
+
const res = { ok: false, status: 'MIMO_UNAVAILABLE', response: '', sessionId: null, exitCode: null, mode: built.mode, backend: 'mimocode', stderr: 'mimo spawn failed: ' + String(e && e.message || e), errorMessage: String(e && e.message || e) }
|
|
122
|
+
engine.end(res, cwd)
|
|
123
|
+
resolve(res)
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
let out = ''
|
|
127
|
+
let err = ''
|
|
128
|
+
let killed = false
|
|
129
|
+
const timer = setTimeout(() => { killed = true; try { child.kill('SIGTERM') } catch (e) { } }, (built.timeoutSec + 60) * 1000)
|
|
130
|
+
const stream = createLineStream((ln) => {
|
|
131
|
+
const t = ln.trim()
|
|
132
|
+
if (!t.startsWith('{')) return
|
|
133
|
+
try { engine.foldEvent(JSON.parse(t), cwd) } catch (e) { }
|
|
134
|
+
})
|
|
135
|
+
child.stdout.on('data', (d) => {
|
|
136
|
+
out += d
|
|
137
|
+
if (out.length > 4000000) out = out.slice(-2000000)
|
|
138
|
+
stream.pushChunk(String(d))
|
|
139
|
+
})
|
|
140
|
+
child.stderr.on('data', (d) => { err += d; if (err.length > 1000000) err = err.slice(-500000) })
|
|
141
|
+
child.on('error', (e) => {
|
|
142
|
+
clearTimeout(timer)
|
|
143
|
+
stream.flush()
|
|
144
|
+
const res = { ok: false, status: 'MIMO_UNAVAILABLE', response: '', sessionId: null, exitCode: null, mode: built.mode, backend: 'mimocode', stderr: String(e && e.message || e), errorMessage: String(e && e.message || e) }
|
|
145
|
+
engine.end(res, cwd)
|
|
146
|
+
resolve(res)
|
|
147
|
+
})
|
|
148
|
+
child.on('close', (code) => {
|
|
149
|
+
clearTimeout(timer)
|
|
150
|
+
stream.flush()
|
|
151
|
+
const outcome = { exitCode: killed ? 124 : (code == null ? -1 : code) }
|
|
152
|
+
let res = provider.buildResult(provider.parseJson(out), outcome, built.mode, err, out, 'mimocode')
|
|
153
|
+
if (killed && !res.ok) {
|
|
154
|
+
res.status = 'HUNG_TIMEOUT'
|
|
155
|
+
res.stderr = (res.stderr ? res.stderr + ' ' : '') + '[killed by DSH timeout guard after ' + built.timeoutSec + 's; if this was a long-running script (build/test) raise timeoutSec]'
|
|
156
|
+
}
|
|
157
|
+
engine.end(res, cwd)
|
|
158
|
+
resolve(res)
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ── 文本渲染(旧 renderResultText verbatim;契约 §4:错误也是文本)───────────────
|
|
164
|
+
function textResult(res) {
|
|
165
|
+
if (!res) return '(no result)'
|
|
166
|
+
const b = res.backend ? ' backend=' + res.backend : ''
|
|
167
|
+
if (res.fallback) {
|
|
168
|
+
return 'status=FALLBACK_TO_DSH fallback=true' + b + '\n' + (res.reason ? 'reason=' + res.reason + '\n' : '') +
|
|
169
|
+
'The user/host chose DSH-local. Finish with native tools and NEVER call mimo again for this task.'
|
|
170
|
+
}
|
|
171
|
+
if (res.ok === false && res.error) return 'mimo_settings/error' + b + ': ' + res.error
|
|
172
|
+
if (!res.ok) {
|
|
173
|
+
const hint = provider.failureHint ? provider.failureHint(res) : ''
|
|
174
|
+
return 'mimo failed status=' + (res.status || 'ERROR') + b + '\n' + shortLabel(res.stderr || res.errorMessage || '') + (hint ? '\n' + hint : '')
|
|
175
|
+
}
|
|
176
|
+
return 'mimo ok status=' + res.status + b + (res.sessionId ? ' sessionId=' + res.sessionId : '') + '\n\n' + String(res.response || '')
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ── 工具面(旧 MCP TOOLS verbatim:含 sessionId 关键注释与 mimo_settings)────────
|
|
180
|
+
// 直接复用描述符的 schema/description(三形态单一真源);MCP 面去掉 background 参数
|
|
181
|
+
// (stdio 调用恒为同步阻塞,无宿主 jobs 服务)—— 旧 MCP 的 run/continue 本就无 background。
|
|
182
|
+
function stripBackground(schema) {
|
|
183
|
+
const copy = JSON.parse(JSON.stringify(schema))
|
|
184
|
+
if (copy.properties) delete copy.properties.background
|
|
185
|
+
return copy
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function tools(env) {
|
|
189
|
+
const run = { name: provider.tools.run.name, description: provider.tools.run.description, inputSchema: stripBackground(provider.tools.run.inputSchema) }
|
|
190
|
+
const cont = { name: provider.tools.continue.name, description: provider.tools.continue.description, inputSchema: stripBackground(provider.tools.continue.inputSchema) }
|
|
191
|
+
const status = { name: provider.tools.status.name, description: provider.tools.status.description, inputSchema: provider.tools.status.inputSchema }
|
|
192
|
+
const settings = provider.extraTools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema }))[0]
|
|
193
|
+
return [run, cont, status, settings]
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export async function call(name, args, io) {
|
|
197
|
+
if (io) lastIo = io
|
|
198
|
+
const a = args || {}
|
|
199
|
+
const runtimeBackend = provider.resolveBackend(a, null, preferredBackendFromEnv())
|
|
200
|
+
|
|
201
|
+
if (name === 'mimo_settings') {
|
|
202
|
+
const v = await provider.extraTools[0].logic(a, null)
|
|
203
|
+
return provider.extraTools[0].render(v)[0].text
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (name === 'mimo_status') {
|
|
207
|
+
const snap = await provider.statusToolLogic(a)
|
|
208
|
+
snap.defaultBackend = runtimeBackend
|
|
209
|
+
snap.backend = runtimeBackend
|
|
210
|
+
return renderStatus(snap, provider)[0].text
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (name === 'mimo_run' || name === 'mimo_continue') {
|
|
214
|
+
if (!a.prompt || !String(a.prompt).trim()) return 'mimo error: prompt is required'
|
|
215
|
+
const mapped = {
|
|
216
|
+
prompt: a.prompt,
|
|
217
|
+
mode: a.mode,
|
|
218
|
+
model: a.model,
|
|
219
|
+
effort: a.effort,
|
|
220
|
+
cwd: a.cwd,
|
|
221
|
+
timeoutSec: a.timeoutSec,
|
|
222
|
+
title: a.title,
|
|
223
|
+
backend: a.backend,
|
|
224
|
+
background: false, // MCP 恒同步阻塞(无宿主 jobs)
|
|
225
|
+
// 【关键】desktop 后端的 sessionId 必须转发。旧 handleTool 此处曾是白名单转发,
|
|
226
|
+
// 漏了 sessionId(只在 mimo_continue 分支补)→ mimo_run 传 sessionId 被静默丢弃
|
|
227
|
+
// → 永远 newDesktopSessionId 造新 id → 因 Desktop API 不能创建会话,mimo_run 在
|
|
228
|
+
// desktop 后端下完全不可用。
|
|
229
|
+
sessionId: a.sessionId
|
|
230
|
+
}
|
|
231
|
+
if (name === 'mimo_continue') {
|
|
232
|
+
if (a.sessionId) mapped.sessionId = a.sessionId
|
|
233
|
+
else mapped.continueLatest = true
|
|
234
|
+
}
|
|
235
|
+
const backend = provider.resolveBackend(mapped, null, preferredBackendFromEnv())
|
|
236
|
+
let res
|
|
237
|
+
if (backend === BACKEND_DESKTOP) {
|
|
238
|
+
const pf = provider.preflight({ backend })
|
|
239
|
+
if (pf && pf.abort) {
|
|
240
|
+
res = pf.abort
|
|
241
|
+
engine.begin(resolveCwd(mapped))
|
|
242
|
+
engine.end(res, resolveCwd(mapped))
|
|
243
|
+
} else {
|
|
244
|
+
const cwd = resolveCwd(mapped)
|
|
245
|
+
const built = provider.buildArgv(exePrefix(), mapped, { planActive: false, defaultMode: 'auto', backend })
|
|
246
|
+
engine.begin(cwd)
|
|
247
|
+
res = await provider.execute({ args: mapped, cwd, built, backend })
|
|
248
|
+
engine.end(res, cwd)
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
res = await runMimo(mapped)
|
|
252
|
+
}
|
|
253
|
+
if (!res.ok && provider.isLimited(res)) {
|
|
254
|
+
// runMimo / desktop 分支所有退出路径均已 engine.end;此处仅改返回值对象,
|
|
255
|
+
// 绝不二次 engine.end(防 runs 虚标与 running 计数下溢)。
|
|
256
|
+
res = fallbackResult(res, res.mode, provider)
|
|
257
|
+
res.backend = backend
|
|
258
|
+
}
|
|
259
|
+
return textResult(res)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return 'mimo error: unknown tool: ' + name
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ── 自检(keystone --check 调用;argv 契约防漂移)───────────────────────────────
|
|
266
|
+
export function check(env) {
|
|
267
|
+
const ts = tools(env || process.env)
|
|
268
|
+
const names = ts.map((t) => t.name)
|
|
269
|
+
for (const req of ['mimo_run', 'mimo_continue', 'mimo_status', 'mimo_settings']) {
|
|
270
|
+
if (!names.includes(req)) throw new Error('mimo mcp: missing tool ' + req)
|
|
271
|
+
}
|
|
272
|
+
for (const t of ts) {
|
|
273
|
+
if (!t.name || !t.description || !t.inputSchema || t.inputSchema.type !== 'object') throw new Error('mimo mcp: bad tool def ' + JSON.stringify(t && t.name))
|
|
274
|
+
}
|
|
275
|
+
// MCP 面必须覆盖描述符三件套 + settings(名字一致,提示词连续性红线)。
|
|
276
|
+
for (const k of ['run', 'continue', 'status']) {
|
|
277
|
+
if (!names.includes(provider.tools[k].name)) throw new Error('mimo mcp: tools surface missing descriptor name ' + provider.tools[k].name)
|
|
278
|
+
}
|
|
279
|
+
// mimocode argv 契约(旧 core verbatim)。
|
|
280
|
+
const b = provider.buildArgv(['mimo'], { prompt: 'x' }, { planActive: false, backend: 'mimocode' })
|
|
281
|
+
for (const f of ['run', '--format', 'json', '--dangerously-skip-permissions', '--agent', 'build']) {
|
|
282
|
+
if (!b.argv.includes(f)) throw new Error('mimo mcp: argv contract drift: ' + f)
|
|
283
|
+
}
|
|
284
|
+
// mimo-desktop 空壳(非 spawn 路径,交 execute)。
|
|
285
|
+
const d = provider.buildArgv(['mimo'], { prompt: 'x' }, { planActive: false, backend: BACKEND_DESKTOP })
|
|
286
|
+
if (d.argv.length !== 0) throw new Error('mimo mcp: desktop buildArgv must be empty shell')
|
|
287
|
+
// delegates 规则:只有 desktop 委托。
|
|
288
|
+
if (provider.delegates(BACKEND_DESKTOP) !== true || provider.delegates('mimocode') !== false) throw new Error('mimo mcp: delegates contract drift')
|
|
289
|
+
return true
|
|
290
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,89 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luchenglong/cli-bridge",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "CLI Bridge for DeepSeek Harness (DSH): one package bridging three local coding-CLI agents — agy (Google Antigravity CLI), Tencent CodeBuddy Code, and Xiaomi MiMo (MiMoCode / MiMo Desktop). Each provider keeps its own agent preset (cordis-agy / codebuddy-first / cordis-mimo), tools (agy_run / codebuddy_run / mimo_run + continue + status), rate-limit/network fallback dialog, and zero-dependency MCP server, unified under a single home-level status light, one settings file, and one bundle patch. Merges agy-first-bridge, codebuddy-first-bridge and mimo-first-bridge.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=18",
|
|
11
|
+
"dsh": ">=0.1.1-rc.2 <0.3.0-0"
|
|
12
|
+
},
|
|
13
|
+
"main": "./home-plugin/cli-bridge-indicator/lib/index.mjs",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": "./home-plugin/cli-bridge-indicator/lib/index.mjs",
|
|
16
|
+
"./client": "./home-plugin/cli-bridge-indicator/lib/client.js",
|
|
17
|
+
"./bridge-kit": "./core/bridge-kit.mjs",
|
|
18
|
+
"./registry": "./providers/registry.mjs",
|
|
19
|
+
"./providers/agy": "./providers/agy.mjs",
|
|
20
|
+
"./providers/codebuddy": "./providers/codebuddy.mjs",
|
|
21
|
+
"./providers/mimo": "./providers/mimo.mjs",
|
|
22
|
+
"./preset-agy": "./preset/agy-first/cli-bridge.mjs",
|
|
23
|
+
"./preset-codebuddy": "./preset/codebuddy-first/cli-bridge.mjs",
|
|
24
|
+
"./preset-mimo": "./preset/mimo-first/cli-bridge.mjs",
|
|
25
|
+
"./package.json": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
"dsh": {
|
|
28
|
+
"client": {
|
|
29
|
+
"platform": "web"
|
|
30
|
+
},
|
|
31
|
+
"bundle": {
|
|
32
|
+
"patch": "./home-plugin/cli-bridge-indicator/cordis.patch.yml"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"bin": {
|
|
36
|
+
"cli-bridge-mcp": "mcp/cli-bridge-mcp.mjs",
|
|
37
|
+
"agy-mcp-server": "mcp/agy-mcp-server.mjs",
|
|
38
|
+
"codebuddy-mcp-server": "mcp/codebuddy-mcp-server.mjs",
|
|
39
|
+
"mimo-mcp-server": "mcp/mimo-mcp-server.mjs"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "node scripts/build.mjs",
|
|
43
|
+
"test": "node scripts/build.mjs --check && node --test",
|
|
44
|
+
"verify": "node scripts/verify.mjs",
|
|
45
|
+
"check": "node scripts/build.mjs --check && node scripts/verify.mjs && node --test && node mcp/cli-bridge-mcp.mjs --check",
|
|
46
|
+
"prepack": "node scripts/verify.mjs && node scripts/build.mjs --check",
|
|
47
|
+
"deploy:local": "node scripts/deploy.mjs"
|
|
48
|
+
},
|
|
49
|
+
"files": [
|
|
50
|
+
"core/",
|
|
51
|
+
"providers/",
|
|
52
|
+
"preset/",
|
|
53
|
+
"home-plugin/",
|
|
54
|
+
"dynamic/",
|
|
55
|
+
"mcp/",
|
|
56
|
+
"bin/",
|
|
57
|
+
"scripts/",
|
|
58
|
+
"docs/",
|
|
59
|
+
"assets/",
|
|
60
|
+
"locale/",
|
|
61
|
+
"CHANGELOG.md",
|
|
62
|
+
"MCP-POLICY.md",
|
|
63
|
+
"PROVIDER-CONTRACT.md",
|
|
64
|
+
"README.md",
|
|
65
|
+
"README.en.md",
|
|
66
|
+
"LICENSE"
|
|
67
|
+
],
|
|
68
|
+
"keywords": [
|
|
69
|
+
"dsh",
|
|
70
|
+
"cordis",
|
|
71
|
+
"cli-bridge",
|
|
72
|
+
"agy",
|
|
73
|
+
"codebuddy",
|
|
74
|
+
"mimo",
|
|
75
|
+
"mcp",
|
|
76
|
+
"mcp-server",
|
|
77
|
+
"agent",
|
|
78
|
+
"deepseek-harness"
|
|
79
|
+
],
|
|
80
|
+
"author": "chenglong",
|
|
81
|
+
"repository": {
|
|
82
|
+
"type": "git",
|
|
83
|
+
"url": "git+https://github.com/new-256/cli-bridge.git"
|
|
84
|
+
},
|
|
85
|
+
"homepage": "https://github.com/new-256/cli-bridge#readme",
|
|
86
|
+
"bugs": {
|
|
87
|
+
"url": "https://github.com/new-256/cli-bridge/issues"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# 生成物 — 由 node scripts/build.mjs 从 preset/agy-first/plugin-list.mjs 生成,勿手改。
|
|
2
|
+
# legacy 目录形态($DSH_HOME/.agent-presets/cordis-agy/);新 DSH 走声明制
|
|
3
|
+
# (home-plugin/cli-bridge-indicator/lib/preset-declarations.mjs 直接 import 同一源)。
|
|
4
|
+
- id: persona
|
|
5
|
+
name: '@deepseek-ai/dsh-persona'
|
|
6
|
+
config:
|
|
7
|
+
suffix: 'Your working directory is {{cwd}}.'
|
|
8
|
+
prefix: 'You are a coding agent powered by the {{model}} model.'
|
|
9
|
+
- id: agent-instructions
|
|
10
|
+
name: '@deepseek-ai/dsh-agent-instructions'
|
|
11
|
+
config:
|
|
12
|
+
maxBytes: 65536
|
|
13
|
+
- id: tool-bash
|
|
14
|
+
name: '@deepseek-ai/dsh-tool-bash'
|
|
15
|
+
disabled: !!js 'process.platform === ''win32'''
|
|
16
|
+
- id: tool-pwsh
|
|
17
|
+
name: '@deepseek-ai/dsh-tool-pwsh'
|
|
18
|
+
disabled: !!js 'process.platform !== ''win32'''
|
|
19
|
+
- id: tool-fs
|
|
20
|
+
name: '@deepseek-ai/dsh-tool-fs'
|
|
21
|
+
- id: tool-fs-search
|
|
22
|
+
name: '@deepseek-ai/dsh-tool-fs-search'
|
|
23
|
+
config:
|
|
24
|
+
sampleOverCapGlobResults: false
|
|
25
|
+
- id: tool-jobs
|
|
26
|
+
name: '@deepseek-ai/dsh-tool-jobs'
|
|
27
|
+
- id: skill-filesystem
|
|
28
|
+
name: '@deepseek-ai/dsh-skill-filesystem'
|
|
29
|
+
- id: tool-skill
|
|
30
|
+
name: '@deepseek-ai/dsh-tool-skill'
|
|
31
|
+
- id: command-goal
|
|
32
|
+
name: '@deepseek-ai/dsh-command-goal'
|
|
33
|
+
- id: tool-goal
|
|
34
|
+
name: '@deepseek-ai/dsh-tool-goal'
|
|
35
|
+
- id: planning
|
|
36
|
+
name: 'cordis:group'
|
|
37
|
+
group: true
|
|
38
|
+
isolate:
|
|
39
|
+
planMode: true
|
|
40
|
+
config:
|
|
41
|
+
- id: plan-mode
|
|
42
|
+
name: '@deepseek-ai/dsh-plan-mode'
|
|
43
|
+
config:
|
|
44
|
+
section: |
|
|
45
|
+
You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
|
|
46
|
+
|
|
47
|
+
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
|
|
48
|
+
|
|
49
|
+
The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
|
|
50
|
+
|
|
51
|
+
Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
|
|
52
|
+
|
|
53
|
+
Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
|
|
54
|
+
|
|
55
|
+
When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
|
|
56
|
+
- id: compaction
|
|
57
|
+
name: 'cordis:group'
|
|
58
|
+
group: true
|
|
59
|
+
isolate:
|
|
60
|
+
compaction: true
|
|
61
|
+
toolResultPruner: true
|
|
62
|
+
config:
|
|
63
|
+
- id: compaction-basic
|
|
64
|
+
name: '@deepseek-ai/dsh-compaction-basic'
|
|
65
|
+
- id: command-compact
|
|
66
|
+
name: '@deepseek-ai/dsh-command-compact'
|
|
67
|
+
- id: tool-result-pruner
|
|
68
|
+
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
69
|
+
config:
|
|
70
|
+
thresholdChars: 8192
|
|
71
|
+
headChars: 4096
|
|
72
|
+
tailChars: 1024
|
|
73
|
+
- id: delegation
|
|
74
|
+
name: 'cordis:group'
|
|
75
|
+
group: true
|
|
76
|
+
isolate:
|
|
77
|
+
workflowEngine: true
|
|
78
|
+
config:
|
|
79
|
+
- id: tool-subagent-control
|
|
80
|
+
name: '@deepseek-ai/dsh-tool-subagent-control'
|
|
81
|
+
- id: tool-subagent-list-agents
|
|
82
|
+
name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
|
|
83
|
+
- id: tool-subagent
|
|
84
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
85
|
+
config:
|
|
86
|
+
provider: spawn
|
|
87
|
+
toolName: subagent
|
|
88
|
+
modelSelectionSettings: true
|
|
89
|
+
backgroundMode: continuable
|
|
90
|
+
- id: tool-subagent-fork
|
|
91
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
92
|
+
config:
|
|
93
|
+
provider: fork
|
|
94
|
+
toolName: subagent_fork
|
|
95
|
+
backgroundMode: continuable
|
|
96
|
+
- id: tool-subagent-codex
|
|
97
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
98
|
+
disabled: true
|
|
99
|
+
config:
|
|
100
|
+
provider: codex
|
|
101
|
+
toolName: subagent_codex
|
|
102
|
+
backgroundMode: one-shot
|
|
103
|
+
maxDepth: provider-managed
|
|
104
|
+
- id: tool-subagent-claude-code
|
|
105
|
+
name: '@deepseek-ai/dsh-tool-subagent'
|
|
106
|
+
disabled: true
|
|
107
|
+
config:
|
|
108
|
+
provider: claude-code
|
|
109
|
+
toolName: subagent_claude_code
|
|
110
|
+
backgroundMode: one-shot
|
|
111
|
+
maxDepth: provider-managed
|
|
112
|
+
- id: workflow-ptc
|
|
113
|
+
name: '@deepseek-ai/dsh-workflow-ptc'
|
|
114
|
+
config:
|
|
115
|
+
provider: spawn
|
|
116
|
+
- id: tool-workflow
|
|
117
|
+
name: '@deepseek-ai/dsh-tool-workflow'
|
|
118
|
+
- id: tool-ralph
|
|
119
|
+
name: '@deepseek-ai/dsh-tool-ralph'
|
|
120
|
+
disabled: true
|
|
121
|
+
config:
|
|
122
|
+
subagentProvider: spawn
|
|
123
|
+
maxRounds: 64
|
|
124
|
+
- id: tool-ask-user
|
|
125
|
+
name: '@deepseek-ai/dsh-tool-ask-user'
|
|
126
|
+
- id: tool-todo
|
|
127
|
+
name: '@deepseek-ai/dsh-tool-todo'
|
|
128
|
+
config:
|
|
129
|
+
allowParallelInProgress: true
|
|
130
|
+
- id: tool-web
|
|
131
|
+
name: '@deepseek-ai/dsh-tool-web'
|
|
132
|
+
config:
|
|
133
|
+
fetch: true
|
|
134
|
+
searchTimeoutMs: 60000
|
|
135
|
+
- id: present
|
|
136
|
+
name: '@deepseek-ai/dsh-tool-present'
|
|
137
|
+
- id: tool-plugin-manager
|
|
138
|
+
name: '@deepseek-ai/dsh-plugin-manager/tools'
|
|
139
|
+
disabled: true
|
|
140
|
+
- id: cli-bridge-agy
|
|
141
|
+
name: '@luchenglong/cli-bridge/preset-agy'
|