Skip to content
This repository was archived by the owner on Sep 7, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
02a9924
Add plugin cli-agent-bridge
Hylouis233 Aug 15, 2026
9cd9011
Harden cli-agent-bridge headless templates and git snapshot
Hylouis233 Aug 15, 2026
78515cf
Launch Windows command shims through PowerShell runner
Hylouis233 Aug 15, 2026
77b9cb2
Address PR review: serialize, cancel, fail-closed, honest results
Hylouis233 Aug 15, 2026
db2553e
Align docs with review-hardened server behavior
Hylouis233 Aug 15, 2026
fd9c432
Add plugin test suite and precise parallel wording
Hylouis233 Aug 15, 2026
a42a84d
Key locks by worktree root, harden cancellation and process-tree kills
Hylouis233 Aug 15, 2026
d27f2e6
Run git snapshot commands serially; make tests leak-proof
Hylouis233 Aug 15, 2026
6ee0a0b
fix(cli-agent-bridge): harden delegation lifecycle
Hylouis233 Aug 16, 2026
0c7a401
fix(cli-agent-bridge): address current review gaps
Hylouis233 Aug 16, 2026
1612d21
fix(cli-agent-bridge): close remaining review gaps
Hylouis233 Aug 16, 2026
f46431a
fix(cli-agent-bridge): serialize across server processes
Hylouis233 Aug 16, 2026
c7a4a15
fix(cli-agent-bridge): harden Linux zombie cleanup
Hylouis233 Aug 16, 2026
8f599d2
fix(cli-agent-bridge): harden cross-process isolation
Hylouis233 Aug 16, 2026
13d4570
fix(cli-agent-bridge): use CAS workspace leases
Hylouis233 Aug 16, 2026
4a6fb48
fix(cli-agent-bridge): close audit findings on leases, trees, and att…
Hylouis233 Aug 16, 2026
72a0fb2
fix(cli-agent-bridge): close latest review round
Hylouis233 Aug 16, 2026
34b9690
fix(cli-agent-bridge): isolate shared refs and process scans
Hylouis233 Aug 16, 2026
47b4eca
fix(cli-agent-bridge): close current review gaps
Hylouis233 Aug 16, 2026
36988ef
fix(cli-agent-bridge): address delayed review findings
Hylouis233 Aug 16, 2026
183aa04
fix(cli-agent-bridge): cover complete baselines and paths
Hylouis233 Aug 16, 2026
d91e923
fix(cli-agent-bridge): observe late children and fetched tags
Hylouis233 Aug 16, 2026
8e6c5aa
fix(cli-agent-bridge): preserve moved tag attribution
Hylouis233 Aug 16, 2026
c6e6ec7
fix(cli-agent-bridge): close current review gaps
Hylouis233 Aug 16, 2026
b17c929
fix(cli-agent-bridge): isolate lock metadata from mirrors
Hylouis233 Aug 16, 2026
dd7d4b4
test(cli-agent-bridge): gate Linux-only fixtures
Hylouis233 Aug 16, 2026
a1ecd0a
fix(cli-agent-bridge): close latest review findings
Hylouis233 Aug 16, 2026
6f158b2
fix(cli-agent-bridge): harden shared lock lifecycle
Hylouis233 Aug 16, 2026
bf51a02
fix(cli-agent-bridge): harden delegated execution
Hylouis233 Aug 16, 2026
95af068
fix(cli-agent-bridge): close latest runtime review gaps
Hylouis233 Aug 16, 2026
68bbb14
test(cli-agent-bridge): isolate commit-base attribution fixture
Hylouis233 Aug 16, 2026
2bbce49
fix(cli-agent-bridge): close latest safety gaps
Hylouis233 Aug 17, 2026
7ac1c50
fix(cli-agent-bridge): harden delegation state handling
Hylouis233 Aug 17, 2026
50664e4
fix(cli-agent-bridge): harden configuration and trace handling
Hylouis233 Aug 17, 2026
5db74bf
fix(cli-agent-bridge): fail closed without reliable containment
Hylouis233 Aug 17, 2026
6be15eb
fix(cli-agent-bridge): harden interrupted setup and lock recovery
Hylouis233 Aug 17, 2026
05bfcff
fix(cli-agent-bridge): reconcile uncertain cleanup outcomes
Hylouis233 Aug 17, 2026
51437d9
fix(cli-agent-bridge): preserve config cleanup failures
Hylouis233 Aug 17, 2026
b2f7811
fix review safety and cancellation gaps
Hylouis233 Aug 17, 2026
8f87ae4
fix(cli-agent-bridge): harden Git snapshot handling
Hylouis233 Aug 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"scripts": {
"create": "node scripts/create-plugin.mjs",
"check": "npm run validate && npm test",
"test": "node --test",
"test": "node --test --test-concurrency=1",
"validate": "node scripts/validate.mjs"
},
"license": "Apache-2.0"
Expand Down
21 changes: 21 additions & 0 deletions plugins/Hylouis233/cli-agent-bridge/LICENSE
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.
14 changes: 14 additions & 0 deletions plugins/Hylouis233/cli-agent-bridge/NOTICE
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.
236 changes: 236 additions & 0 deletions plugins/Hylouis233/cli-agent-bridge/README.md

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions plugins/Hylouis233/cli-agent-bridge/backends.json
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." }
}
}
132 changes: 132 additions & 0 deletions plugins/Hylouis233/cli-agent-bridge/git-executable.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { createHash } from "node:crypto";
import { constants } from "node:fs";
import { access, mkdir, realpath, stat } from "node:fs/promises";
import os from "node:os";
import path from "node:path";

let executablePromise = null;
let hooksRootPromise = null;
const pathCommandPromises = new Map();

function userScope() {
let identity;
try {
const user = os.userInfo();
identity = Number.isInteger(user.uid) && user.uid >= 0
? process.platform + ":uid:" + String(user.uid)
: process.platform + ":" + user.username + ":" + user.homedir;
} catch {
identity = process.platform + ":" + (process.env.USERNAME ?? process.env.USER ?? os.homedir());
}
return createHash("sha256").update(identity).digest("hex").slice(0, 20);
}

const DISABLED_HOOKS_ROOT = path.join(
os.tmpdir(), "minimax-cli-agent-bridge-git-" + userScope(), "disabled-hooks",
);

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 (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;
}

export function resolvePathCommand(command) {
if (typeof command !== "string" || !command) return Promise.resolve(null);
if (!pathCommandPromises.has(command)) {
const resolution = resolvePathCommandUncached(command);
pathCommandPromises.set(command, resolution);
// Share an in-flight lookup and retain positive results, but do not make a
// missing/not-yet-installed CLI permanent for the lifetime of the server.
// The identity guard prevents an older completion from deleting a newer
// retry that has already occupied the same cache slot.
void resolution.then((resolved) => {
if (resolved === null && pathCommandPromises.get(command) === resolution) {
pathCommandPromises.delete(command);
}
}, () => {
if (pathCommandPromises.get(command) === resolution) pathCommandPromises.delete(command);
});
}
return pathCommandPromises.get(command);
}

export function trustedGitExecutable() {
executablePromise ??= resolveGitExecutable();
return executablePromise;
}

async function disabledHooksRoot() {
hooksRootPromise ??= mkdir(DISABLED_HOOKS_ROOT, { recursive: true, mode: 0o700 })
.then(() => DISABLED_HOOKS_ROOT)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Secure the disabled-hooks directory before trusting it

When another local user pre-creates this predictable temp path (the suffix is derived from the platform and UID), mkdir({ recursive: true }) accepts the attacker-controlled directory without verifying its owner, permissions, or whether a component is a symlink. The attacker can place an executable reference-transaction hook there, and every safeGitInvocation() then selects it through core.hooksPath; Git's githooks documentation says this hook runs during ref updates, so lock-store update-ref operations execute attacker-controlled code with the bridge user's privileges. Create the directory with exclusive, user-owned semantics and verify every reused path before treating it as an empty hooks directory.

Useful? React with 👍 / 👎.

.catch((error) => {
hooksRootPromise = null;
throw error;
});
return await hooksRootPromise;
}

export async function safeGitInvocation(args) {
const safeArgs = [
"-c", "core.hooksPath=" + await disabledHooksRoot(),
"-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");
const env = {
...process.env,
GIT_OPTIONAL_LOCKS: "0",
GIT_PAGER: "",
PAGER: "",
};
for (const name of [
"GIT_ALTERNATE_OBJECT_DIRECTORIES", "GIT_CONFIG_COUNT", "GIT_DIR", "GIT_DIFF_OPTS",
"GIT_EXTERNAL_DIFF", "GIT_INDEX_FILE", "GIT_OBJECT_DIRECTORY", "GIT_WORK_TREE",
]) delete env[name];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clear GIT_COMMON_DIR before repository discovery

When the MCP server inherits GIT_COMMON_DIR—for example because it was launched from Git-related tooling—this environment sanitization leaves it active while clearing the related GIT_DIR. Git documents GIT_COMMON_DIR as overriding the common administrative directory, and git rev-parse --git-common-dir consequently returns that inherited path rather than the requested workspace's common directory. The bridge then creates/acquires its lock in the wrong repository while snapshotting and launching the worker in the requested one, so a normally launched bridge can concurrently edit the same workspace under a different lock; remove GIT_COMMON_DIR along with the other repository-routing variables.

Useful? React with 👍 / 👎.

for (const name of Object.keys(env)) {
if (/^GIT_CONFIG_(?:KEY|VALUE)_\d+$/u.test(name)) delete env[name];
}
return { command: await trustedGitExecutable(), args: safeArgs, env };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound Git executable resolution by request interruption

When an absolute PATH entry before the real Git executable is on a stalled network or FUSE filesystem, trustedGitExecutable() can remain pending in access(), stat(), or realpath() indefinitely. This await does not receive the delegation deadline or cancellation token, so timeoutMs, workspace_status cancellation, and awaited shutdown cannot unwind the request; resolve Git through the same detachable deadline-aware subscription used for backend commands.

Useful? React with 👍 / 👎.

}
10 changes: 10 additions & 0 deletions plugins/Hylouis233/cli-agent-bridge/mcp.json
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"]
}
}
}
9 changes: 9 additions & 0 deletions plugins/Hylouis233/cli-agent-bridge/plugin.json
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"]
}
Loading
Loading