This repository was archived by the owner on Sep 7, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 5
Add plugin cli-agent-bridge #16
Closed
Closed
Changes from 33 commits
Commits
Show all changes
40 commits
Select commit
Hold shift + click to select a range
02a9924
Add plugin cli-agent-bridge
Hylouis233 9cd9011
Harden cli-agent-bridge headless templates and git snapshot
Hylouis233 78515cf
Launch Windows command shims through PowerShell runner
Hylouis233 77b9cb2
Address PR review: serialize, cancel, fail-closed, honest results
Hylouis233 db2553e
Align docs with review-hardened server behavior
Hylouis233 fd9c432
Add plugin test suite and precise parallel wording
Hylouis233 a42a84d
Key locks by worktree root, harden cancellation and process-tree kills
Hylouis233 d27f2e6
Run git snapshot commands serially; make tests leak-proof
Hylouis233 6ee0a0b
fix(cli-agent-bridge): harden delegation lifecycle
Hylouis233 0c7a401
fix(cli-agent-bridge): address current review gaps
Hylouis233 1612d21
fix(cli-agent-bridge): close remaining review gaps
Hylouis233 f46431a
fix(cli-agent-bridge): serialize across server processes
Hylouis233 c7a4a15
fix(cli-agent-bridge): harden Linux zombie cleanup
Hylouis233 8f599d2
fix(cli-agent-bridge): harden cross-process isolation
Hylouis233 13d4570
fix(cli-agent-bridge): use CAS workspace leases
Hylouis233 4a6fb48
fix(cli-agent-bridge): close audit findings on leases, trees, and att…
Hylouis233 72a0fb2
fix(cli-agent-bridge): close latest review round
Hylouis233 34b9690
fix(cli-agent-bridge): isolate shared refs and process scans
Hylouis233 47b4eca
fix(cli-agent-bridge): close current review gaps
Hylouis233 36988ef
fix(cli-agent-bridge): address delayed review findings
Hylouis233 183aa04
fix(cli-agent-bridge): cover complete baselines and paths
Hylouis233 d91e923
fix(cli-agent-bridge): observe late children and fetched tags
Hylouis233 8e6c5aa
fix(cli-agent-bridge): preserve moved tag attribution
Hylouis233 c6e6ec7
fix(cli-agent-bridge): close current review gaps
Hylouis233 b17c929
fix(cli-agent-bridge): isolate lock metadata from mirrors
Hylouis233 dd7d4b4
test(cli-agent-bridge): gate Linux-only fixtures
Hylouis233 a1ecd0a
fix(cli-agent-bridge): close latest review findings
Hylouis233 6f158b2
fix(cli-agent-bridge): harden shared lock lifecycle
Hylouis233 bf51a02
fix(cli-agent-bridge): harden delegated execution
Hylouis233 95af068
fix(cli-agent-bridge): close latest runtime review gaps
Hylouis233 68bbb14
test(cli-agent-bridge): isolate commit-base attribution fixture
Hylouis233 2bbce49
fix(cli-agent-bridge): close latest safety gaps
Hylouis233 7ac1c50
fix(cli-agent-bridge): harden delegation state handling
Hylouis233 50664e4
fix(cli-agent-bridge): harden configuration and trace handling
Hylouis233 5db74bf
fix(cli-agent-bridge): fail closed without reliable containment
Hylouis233 6be15eb
fix(cli-agent-bridge): harden interrupted setup and lock recovery
Hylouis233 05bfcff
fix(cli-agent-bridge): reconcile uncertain cleanup outcomes
Hylouis233 51437d9
fix(cli-agent-bridge): preserve config cleanup failures
Hylouis233 b2f7811
fix review safety and cancellation gaps
Hylouis233 8f87ae4
fix(cli-agent-bridge): harden Git snapshot handling
Hylouis233 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| MIT License | ||
|
|
||
| Copyright (c) 2026 Hylouis233 | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| cli-agent-bridge is an original implementation informed by the following | ||
| open-source projects. Their licenses are retained where applicable. | ||
|
|
||
| - claude-subagent-mcp (https://github.com/ltxzs/claude-subagent-mcp) | ||
| MIT License. Copyright (c) ltxzs contributors. | ||
| Its headless spawn, timeout, capture-cap, and git-snapshot patterns | ||
| informed server.mjs. | ||
|
|
||
| - subagent-mcp (https://github.com/Heretyc/subagent-mcp) | ||
| Apache License 2.0. Copyright 2026 Lexi Blackburn. | ||
| Its delegated-CLI orchestration concepts informed the Skill guidance. | ||
|
|
||
| - wshobson/agents (https://github.com/wshobson/agents) | ||
| Reference for multi-harness agent plugin packaging patterns. |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "$comment": "Backend command templates for cli-agent-bridge. <task> and <session> are placeholders. Edit command or buildArgs to point at another binary or add flags. Never store credentials here; each CLI uses your own local authentication.", | ||
| "backends": { | ||
| "claude": { "label": "Claude Code", "command": "claude", "buildArgs": ["-p", "<task>", "--output-format", "text", "--permission-mode", "acceptEdits"], "resumeArgs": ["-p", "<task>", "--output-format", "text", "--permission-mode", "acceptEdits", "--resume", "<session>"], "experimental": false }, | ||
| "codex": { "label": "OpenAI Codex CLI", "command": "codex", "buildArgs": ["exec", "--", "<task>"], "resumeArgs": ["exec", "resume", "<session>", "--", "<task>"], "experimental": false }, | ||
| "kimi": { "label": "Kimi Code", "command": "kimi", "buildArgs": ["-p", "<task>"], "resumeArgs": ["-S", "<session>", "-p", "<task>"], "experimental": false }, | ||
| "zcode": { "label": "ZCode", "command": "zcode", "buildArgs": ["-p", "<task>"], "resumeArgs": null, "experimental": true, "notes": "Desktop ZCode builds have no verified headless mode; set command to your CLI if your distribution provides one." }, | ||
| "dsh": { "label": "DeepSeek Harness (dsh)", "command": "dsh", "buildArgs": ["--profile", "headless", "<task>"], "resumeArgs": null, "experimental": true, "notes": "Uses the documented headless profile; requires a headless profile under DSH_HOME/profiles." } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| import { constants } from "node:fs"; | ||
| import { access, appendFile, realpath, stat } from "node:fs/promises"; | ||
| import path from "node:path"; | ||
|
|
||
| let executablePromise = null; | ||
| const pathCommandEntries = new Map(); | ||
|
|
||
| async function resolveGitExecutable() { | ||
| const names = process.platform === "win32" ? ["git.exe", "git.com"] : ["git"]; | ||
| for (const rawDirectory of (process.env.PATH ?? "").split(path.delimiter)) { | ||
| const directory = rawDirectory.replace(/^"|"$/gu, ""); | ||
| // Never let a relative PATH component reinterpret an untrusted workspace | ||
| // as an executable search root after a Git command changes cwd. | ||
| if (!directory || !path.isAbsolute(directory)) continue; | ||
| for (const name of names) { | ||
| const candidate = path.join(directory, name); | ||
| try { | ||
| await access(candidate, process.platform === "win32" ? constants.F_OK : constants.X_OK); | ||
| if (!(await stat(candidate)).isFile()) continue; | ||
| return await realpath(candidate); | ||
| } catch { /* try the next trusted PATH entry */ } | ||
| } | ||
| } | ||
| throw new Error("cannot locate git in an absolute PATH directory"); | ||
| } | ||
|
|
||
| async function resolvePathCommandUncached(command) { | ||
| if (process.env.NODE_ENV === "test") { | ||
| const delayMs = Number(process.env.CLI_AGENT_BRIDGE_TEST_COMMAND_RESOLUTION_DELAY_MS ?? 0); | ||
| if (Number.isFinite(delayMs) && delayMs > 0) { | ||
| const startedFile = process.env.CLI_AGENT_BRIDGE_TEST_COMMAND_RESOLUTION_STARTED_FILE; | ||
| if (typeof startedFile === "string" && path.isAbsolute(startedFile)) { | ||
| await appendFile(startedFile, "started\n"); | ||
| } | ||
| await new Promise((resolve) => setTimeout(resolve, delayMs)); | ||
| } | ||
| } | ||
| if (path.isAbsolute(command)) { | ||
| try { | ||
| await access(command, process.platform === "win32" ? constants.F_OK : constants.X_OK); | ||
| return (await stat(command)).isFile() ? await realpath(command) : null; | ||
| } catch { return null; } | ||
| } | ||
| if (/[\\/]/u.test(command)) return null; | ||
| const extensions = process.platform === "win32" | ||
| ? (path.extname(command) | ||
| ? [""] | ||
| : [...(process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean), ".ps1"]) | ||
| : [""]; | ||
| for (const rawDirectory of (process.env.PATH ?? "").split(path.delimiter)) { | ||
| const directory = rawDirectory.replace(/^"|"$/gu, ""); | ||
| if (!directory || !path.isAbsolute(directory)) continue; | ||
| for (const extension of extensions) { | ||
| const candidate = path.join(directory, command + extension); | ||
| try { | ||
| await access(candidate, process.platform === "win32" ? constants.F_OK : constants.X_OK); | ||
| if ((await stat(candidate)).isFile()) return await realpath(candidate); | ||
| } catch { /* continue searching */ } | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| function pathCommandEntry(command) { | ||
| if (typeof command !== "string" || !command) { | ||
| return { promise: Promise.resolve(null), settled: true, value: null, error: null }; | ||
| } | ||
| if (!pathCommandEntries.has(command)) { | ||
| const entry = { | ||
| promise: resolvePathCommandUncached(command), | ||
| settled: false, | ||
| value: null, | ||
| error: null, | ||
| waiters: new Set(), | ||
| }; | ||
| pathCommandEntries.set(command, entry); | ||
| // The core lookup has exactly one settlement reaction. Request-scoped | ||
| // waiters subscribe below and can be removed on cancel/deadline, so a | ||
| // permanently stalled filesystem lookup cannot retain one closure per | ||
| // abandoned request. | ||
| void entry.promise.then((resolved) => { | ||
| entry.settled = true; | ||
| entry.value = resolved; | ||
| for (const waiter of entry.waiters) waiter.resolve(resolved); | ||
| entry.waiters.clear(); | ||
| // Retain positive results, but retry a missing/not-yet-installed CLI. | ||
| if (resolved === null && pathCommandEntries.get(command) === entry) { | ||
| pathCommandEntries.delete(command); | ||
| } | ||
| }, (error) => { | ||
| entry.settled = true; | ||
| entry.error = error; | ||
| for (const waiter of entry.waiters) waiter.reject(error); | ||
| entry.waiters.clear(); | ||
| if (pathCommandEntries.get(command) === entry) pathCommandEntries.delete(command); | ||
| }); | ||
| } | ||
| return pathCommandEntries.get(command); | ||
| } | ||
|
|
||
| export function resolvePathCommand(command) { | ||
| return pathCommandEntry(command).promise; | ||
| } | ||
|
|
||
| export function subscribePathCommand(command, resolve, reject) { | ||
| const entry = pathCommandEntry(command); | ||
| if (entry.settled) { | ||
| queueMicrotask(() => entry.error ? reject(entry.error) : resolve(entry.value)); | ||
| return () => {}; | ||
| } | ||
| const waiter = { resolve, reject }; | ||
| entry.waiters.add(waiter); | ||
| return () => { entry.waiters.delete(waiter); }; | ||
| } | ||
|
|
||
| export function trustedGitExecutable() { | ||
| executablePromise ??= resolveGitExecutable(); | ||
| return executablePromise; | ||
| } | ||
|
|
||
| export async function safeGitInvocation(args, baseEnvironment = process.env) { | ||
| const safeArgs = [ | ||
| // Git documents /dev/null as the way to disable hooks. Unlike a shared | ||
| // empty directory, this sink cannot be pre-created or populated by another | ||
| // local user before a coordination update-ref operation. | ||
| "-c", "core.hooksPath=/dev/null", | ||
| "-c", "core.fsmonitor=false", | ||
| "-c", "gc.autoDetach=false", | ||
| "-c", "maintenance.auto=false", | ||
| ...args, | ||
| ]; | ||
| if (args[0] === "diff") safeArgs.splice(9, 0, "--no-ext-diff", "--no-textconv"); | ||
| // Repository-routing variables must never leak from the process that | ||
| // launched the bridge. Clear every case variant of GIT_* (Windows | ||
| // environment names are case-insensitive), then restore only the settings | ||
| // required by these local, non-interactive bridge operations. | ||
| const env = Object.fromEntries( | ||
| Object.entries(baseEnvironment).filter(([name]) => !/^GIT_/iu.test(name)), | ||
| ); | ||
| Object.assign(env, { | ||
| GIT_OPTIONAL_LOCKS: "0", | ||
| GIT_PAGER: "", | ||
| PAGER: "", | ||
| }); | ||
| return { command: await trustedGitExecutable(), args: safeArgs, env }; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", | ||
| "mcpServers": { | ||
| "cli-agent-bridge": { | ||
| "type": "stdio", | ||
| "command": "node", | ||
| "args": ["./server.mjs"] | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", | ||
| "name": "cli-agent-bridge", | ||
| "version": "0.1.0", | ||
| "description": "Delegate coding tasks from MiniMax Code to locally installed coding CLIs (Claude Code, Codex, Kimi Code, ZCode, DSH) through a dependency-free stdio MCP server with git-diff review.", | ||
| "author": { "name": "Hylouis233", "url": "https://github.com/Hylouis233" }, | ||
| "license": "MIT", | ||
| "keywords": ["minimax-code", "plugin", "mcp", "multi-agent", "delegation", "orchestration"] | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an absolute PATH entry before the real Git executable is on a stalled network or FUSE filesystem,
trustedGitExecutable()can remain pending inaccess(),stat(), orrealpath()indefinitely. This await does not receive the delegation deadline or cancellation token, sotimeoutMs,workspace_statuscancellation, and awaited shutdown cannot unwind the request; resolve Git through the same detachable deadline-aware subscription used for backend commands.Useful? React with 👍 / 👎.