ccgx-workflow 1.0.10 → 2.1.0

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/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import cac from 'cac';
3
3
  import ansis from 'ansis';
4
- import { d as diagnoseMcpConfig, i as isWindows, r as readClaudeCodeConfig, f as fixWindowsMcpConfig, w as writeClaudeCodeConfig, a as readCcgConfig, b as initI18n, c as i18n, s as showMainMenu, e as init, g as configMcp, v as version } from './shared/ccgx-workflow.9nxM0FK2.mjs';
4
+ import { d as diagnoseMcpConfig, i as isWindows, r as readClaudeCodeConfig, f as fixWindowsMcpConfig, w as writeClaudeCodeConfig, a as readCcgConfig, b as initI18n, c as i18n, s as showMainMenu, e as init, g as configMcp, v as version } from './shared/ccgx-workflow.DVsx1kL4.mjs';
5
5
  import { execSync } from 'node:child_process';
6
6
  import 'inquirer';
7
7
  import 'ora';
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { h as collectSkills, j as resolvePluginBashCommand } from './shared/ccgx-workflow.9nxM0FK2.mjs';
2
- export { k as changeLanguage, G as checkForUpdates, I as collectInvocableSkills, H as compareVersions, m as createDefaultConfig, n as createDefaultRouting, J as generateCommandContent, o as getCcgDir, p as getConfigPath, E as getCurrentVersion, F as getLatestVersion, t as getWorkflowById, q as getWorkflowConfigs, c as i18n, e as init, b as initI18n, y as installAceTool, z as installAceToolRs, K as installSkillCommands, x as installWorkflows, C as migrateToV1_4_0, D as needsMigration, L as parseFrontmatter, a as readCcgConfig, s as showMainMenu, B as uninstallAceTool, A as uninstallWorkflows, u as update, l as writeCcgConfig } from './shared/ccgx-workflow.9nxM0FK2.mjs';
1
+ import { h as collectSkills, j as resolvePluginBashCommand } from './shared/ccgx-workflow.DVsx1kL4.mjs';
2
+ export { k as changeLanguage, G as checkForUpdates, I as collectInvocableSkills, H as compareVersions, m as createDefaultConfig, n as createDefaultRouting, J as generateCommandContent, o as getCcgDir, p as getConfigPath, E as getCurrentVersion, F as getLatestVersion, t as getWorkflowById, q as getWorkflowConfigs, c as i18n, e as init, b as initI18n, y as installAceTool, z as installAceToolRs, K as installSkillCommands, x as installWorkflows, C as migrateToV1_4_0, D as needsMigration, L as parseFrontmatter, a as readCcgConfig, s as showMainMenu, B as uninstallAceTool, A as uninstallWorkflows, u as update, l as writeCcgConfig } from './shared/ccgx-workflow.DVsx1kL4.mjs';
3
3
  import { existsSync, readFileSync, mkdirSync, writeFileSync, statSync, readdirSync } from 'node:fs';
4
4
  import { join, dirname } from 'node:path';
5
5
  import { homedir } from 'node:os';
@@ -775,7 +775,7 @@ function pluginRoot(home = homedir()) {
775
775
  const PLUGIN_MARKERS = ["SKILL.md", "plugin.json", "package.json", "manifest.json"];
776
776
  const PLUGIN_PREFIXES = {
777
777
  codex: ["codex@", "codex-rescue@", "openai-codex@"],
778
- gemini: ["gemini@", "gemini-rescue@", "google-gemini@"]
778
+ gemini: ["gemini@", "gemini-rescue@", "gemini-ccgx@", "google-gemini@"]
779
779
  };
780
780
  function detectPlugin(name, homeDir = homedir()) {
781
781
  const root = pluginRoot(homeDir);
@@ -13,7 +13,7 @@ import { join as join$2 } from 'node:path/posix';
13
13
  import { join as join$1 } from 'node:path';
14
14
  import i18next from 'i18next';
15
15
 
16
- const version = "1.0.10";
16
+ const version = "2.1.0";
17
17
 
18
18
  function cmd(id, order, category, name, nameEn, description, descriptionEn, cmdOverride) {
19
19
  return {
@@ -295,8 +295,18 @@ function getMcpCommand(command) {
295
295
  }
296
296
 
297
297
  const VENDOR_MARKETPLACE_KEYS = {
298
- codex: "codex@openai-codex",
299
- gemini: "gemini@google-gemini"
298
+ codex: ["codex@openai-codex"],
299
+ gemini: ["gemini@gemini-ccgx", "gemini@google-gemini"]
300
+ };
301
+ const VENDOR_PRIMARY_INSTALL = {
302
+ codex: {
303
+ marketplace: "openai/codex-plugin-cc",
304
+ key: "codex@openai-codex"
305
+ },
306
+ gemini: {
307
+ marketplace: "wzyxdwll/gemini-plugin-cc",
308
+ key: "gemini@gemini-ccgx"
309
+ }
300
310
  };
301
311
  function discoverCompanion(vendor, homeDir = homedir()) {
302
312
  const ssotPath = join$1(homeDir, ".claude", "plugins", "installed_plugins.json");
@@ -307,10 +317,16 @@ function discoverCompanion(vendor, homeDir = homedir()) {
307
317
  } catch {
308
318
  return null;
309
319
  }
310
- const key = VENDOR_MARKETPLACE_KEYS[vendor];
311
- const instances = raw?.plugins?.[key];
312
- if (!Array.isArray(instances) || instances.length === 0) return null;
313
- const inst = instances[0];
320
+ const keys = VENDOR_MARKETPLACE_KEYS[vendor];
321
+ let inst = null;
322
+ for (const key of keys) {
323
+ const instances = raw?.plugins?.[key];
324
+ if (Array.isArray(instances) && instances.length > 0) {
325
+ inst = instances[0];
326
+ break;
327
+ }
328
+ }
329
+ if (!inst) return null;
314
330
  const installPath = inst?.installPath;
315
331
  if (typeof installPath !== "string" || !installPath) return null;
316
332
  const companionPath = join$1(installPath, "scripts", `${vendor}-companion.mjs`);
@@ -335,10 +351,12 @@ function buildBashCommand(_loc, options = {}) {
335
351
  return `node ${quotedHelper} ${vendor}${jsonFlag}`;
336
352
  }
337
353
  function buildPluginMissingFallback(vendor) {
338
- const key = VENDOR_MARKETPLACE_KEYS[vendor];
354
+ const { marketplace, key } = VENDOR_PRIMARY_INSTALL[vendor];
339
355
  return [
340
- `# CCG: ${vendor} plugin (${key}) not installed at CCG install time.`,
341
- `# Install with: claude plugin install ${key}`,
356
+ `# CCG: ${vendor} plugin not installed at CCG install time.`,
357
+ `# Install with:`,
358
+ `# claude plugin marketplace add ${marketplace}`,
359
+ `# claude plugin install ${key}`,
342
360
  `# Then re-run: npx ccgx-workflow init --skip-prompt --skip-mcp`,
343
361
  `echo 'CCG: ${vendor} plugin not available' >&2 && exit 1`
344
362
  ].join("\n");
@@ -4064,7 +4082,9 @@ ${exportCommand}
4064
4082
  console.log();
4065
4083
  console.log(` ${ansis.green("\u2605")} ${ansis.bold("Plugin (recommended, one-click in Claude Code):")}`);
4066
4084
  console.log(` ${ansis.cyan("/plugins install codex@openai-codex")}`);
4067
- console.log(` ${ansis.cyan("/plugins install gemini@google-gemini")}`);
4085
+ console.log(` ${ansis.cyan("claude plugin marketplace add wzyxdwll/gemini-plugin-cc")} ${ansis.gray("# 2.0.0+ ccgx fork")}`);
4086
+ console.log(` ${ansis.cyan("/plugins install gemini@gemini-ccgx")} ${ansis.gray("# preferred (\u542B P-1..P-21 + W1/W2/I1 patch)")}`);
4087
+ console.log(` ${ansis.gray(" \u2014 or upstream: /plugins install gemini@google-gemini (requires repatch)")}`);
4068
4088
  console.log();
4069
4089
  console.log(` ${ansis.green("\u2606")} ${ansis.bold("CLI fallback (standalone binaries):")}`);
4070
4090
  console.log(` ${ansis.cyan("npm i -g @openai/codex")} ${ansis.gray("# then: codex login")}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccgx-workflow",
3
- "version": "1.0.10",
3
+ "version": "2.1.0",
4
4
  "description": "Multi-model orchestration for Claude Code. Codex + Gemini parallel collaboration with fresh-context subagent protocols, OS-level process isolation, and Plan-Critic-Verify quality tiers. Successor to ccg-workflow.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.17.1",
@@ -39,7 +39,7 @@ argument-hint: "<分析问题或任务> [--role=architect|critic|implementer|tes
39
39
 
40
40
  CCG 把双模型并行通道从 `Bash(codeagent-wrapper)` **默认切换**为 plugin spawn:
41
41
 
42
- 1. **优先 plugin spawn**(默认):装了 `codex@openai-codex` + `gemini@google-gemini` plugin 用 `Agent(subagent_type="codex:codex-rescue")` + `Agent(subagent_type="gemini:gemini-rescue")` 并行,主线接 ≤200 token 摘要。
42
+ 1. **优先 plugin spawn**(默认):装了 `codex@openai-codex` + gemini plugin(推荐 `gemini@gemini-ccgx` fork,含 P-1..P-21 + W1/W2/I1 patch;或上游 `gemini@google-gemini` repatch 脚本)→ 用 `Agent(subagent_type="codex:codex-rescue")` + `Agent(subagent_type="gemini:gemini-rescue")` 并行,主线接 ≤200 token 摘要。
43
43
  2. **降级 codeagent-wrapper**(BC fallback):plugin 未装 → fallback 到 Bash 调用,行为与 plugin 路径等价。
44
44
 
45
45
  **判定**:preflight `Bash` 跑 `ls ~/.claude/plugins/` 看有无 `codex@*` / `gemini@*` 子目录。helper 见 `src/utils/plugin-detection.ts`。
@@ -24,7 +24,7 @@ $ARGUMENTS
24
24
 
25
25
  CCG 把 6 核心命令的"双模型并行"通道从 `Bash(codeagent-wrapper)` **默认切换**为 plugin spawn。判定流程:
26
26
 
27
- 1. **优先 plugin spawn 路径**(默认):用户已装 `codex@openai-codex` 和 `gemini@google-gemini` plugin 用 `Agent(subagent_type="codex:codex-rescue")` + `Agent(subagent_type="gemini:gemini-rescue")` 并行 spawn,主线只接 plugin 自家 ≤200 token 摘要。
27
+ 1. **优先 plugin spawn 路径**(默认):用户已装 `codex@openai-codex` 和 gemini plugin(推荐 `gemini@gemini-ccgx` fork,已含全部 patch;或上游 `gemini@google-gemini` repatch)→ 用 `Agent(subagent_type="codex:codex-rescue")` + `Agent(subagent_type="gemini:gemini-rescue")` 并行 spawn,主线只接 plugin 自家 ≤200 token 摘要。
28
28
  2. **降级 codeagent-wrapper 路径**(BC fallback):plugin 未装 → fallback 到 `Bash(~/.claude/bin/codeagent-wrapper ...)`,行为与 plugin 路径等价。
29
29
 
30
30
  **判断方法**:preflight 用 `Bash` 跑 `ls ~/.claude/plugins/ 2>/dev/null | grep -E '^(codex|gemini)@'`;两个 plugin 独立判定。
@@ -44,7 +44,7 @@ argument-hint: "<优化目标> [--role=architect|critic|implementer|tester|write
44
44
 
45
45
  CCG 把双模型并行通道从 `Bash(codeagent-wrapper)` **默认切换**为 plugin spawn:
46
46
 
47
- 1. **优先 plugin spawn**(默认):装了 `codex@openai-codex` + `gemini@google-gemini` plugin 用 `Agent(subagent_type="codex:codex-rescue")` + `Agent(subagent_type="gemini:gemini-rescue")` 并行,主线接 ≤200 token 摘要。
47
+ 1. **优先 plugin spawn**(默认):装了 `codex@openai-codex` + gemini plugin(推荐 `gemini@gemini-ccgx` fork,含 P-1..P-21 + W1/W2/I1 patch;或上游 `gemini@google-gemini` repatch 脚本)→ 用 `Agent(subagent_type="codex:codex-rescue")` + `Agent(subagent_type="gemini:gemini-rescue")` 并行,主线接 ≤200 token 摘要。
48
48
  2. **降级 codeagent-wrapper**(BC fallback):plugin 未装 → fallback 到 Bash 调用,行为与 plugin 路径等价。
49
49
 
50
50
  **判定**:preflight `Bash` 跑 `ls ~/.claude/plugins/` 看有无 `codex@*` / `gemini@*` 子目录。helper 见 `src/utils/plugin-detection.ts`。
@@ -54,7 +54,7 @@ $ARGUMENTS
54
54
 
55
55
  CCG 把 6 核心命令的"双模型并行"通道从 `Bash(codeagent-wrapper)` **默认切换**为 plugin spawn。判定流程:
56
56
 
57
- 1. **优先 plugin spawn 路径**(默认):用户已装 `codex@openai-codex` 和 `gemini@google-gemini` plugin 用 `Agent(subagent_type="codex:codex-rescue")` + `Agent(subagent_type="gemini:gemini-rescue")` 并行 spawn,主线只接 plugin 自家 ≤200 token 摘要协议(`STATUS: ... / FINDINGS: ... / NOTES: ...`)。
57
+ 1. **优先 plugin spawn 路径**(默认):用户已装 `codex@openai-codex` 和 gemini plugin(推荐 `gemini@gemini-ccgx` fork,已含全部 patch;或上游 `gemini@google-gemini` repatch)→ 用 `Agent(subagent_type="codex:codex-rescue")` + `Agent(subagent_type="gemini:gemini-rescue")` 并行 spawn,主线只接 plugin 自家 ≤200 token 摘要协议(`STATUS: ... / FINDINGS: ... / NOTES: ...`)。
58
58
  2. **降级 codeagent-wrapper 路径**(BC fallback):plugin 未装 → fallback 到 `Bash(~/.claude/bin/codeagent-wrapper --backend ... resume ... <<'EOF' ... EOF)`,与 plugin 路径行为等价。
59
59
 
60
60
  **判断方法**:preflight 用 `Bash` 跑 `ls ~/.claude/plugins/ 2>/dev/null | grep -E '^codex@'` 与 `... | grep -E '^gemini@'` 各一次。匹配到对应行 → plugin 已装。两个 plugin 独立判定,可分别 mix-and-match(仅 codex plugin 装了 → backend 走 plugin、frontend 走 codeagent)。
@@ -21,7 +21,7 @@ argument-hint: "[代码或描述] [--adversarial] [--fix [--all] [--auto]] [--ro
21
21
 
22
22
  双模型并行审查,交叉验证综合反馈。无参数时自动审查当前 git 变更。
23
23
 
24
- **双模型并行通道**:默认走 plugin spawn —— 装了 `codex@openai-codex` + `gemini@google-gemini` plugin 用 `Agent(subagent_type="codex:codex-rescue")` + `Agent(subagent_type="gemini:gemini-rescue")` 并行,主线只接 ≤200 token 摘要;plugin 未装 → fallback 到 codeagent-wrapper 路径(BC fallback)。preflight 用 `Bash` 跑 `ls ~/.claude/plugins/` 检测,helper 见 `src/utils/plugin-detection.ts`。
24
+ **双模型并行通道**:默认走 plugin spawn —— 装了 `codex@openai-codex` + gemini plugin(推荐 `gemini@gemini-ccgx` fork,含 P-1..P-21 + W1/W2/I1 patch;或上游 `gemini@google-gemini` repatch 脚本)→ 用 `Agent(subagent_type="codex:codex-rescue")` + `Agent(subagent_type="gemini:gemini-rescue")` 并行,主线只接 ≤200 token 摘要;plugin 未装 → fallback 到 codeagent-wrapper 路径(BC fallback)。preflight 用 `Bash` 跑 `ls ~/.claude/plugins/` 检测,helper 见 `src/utils/plugin-detection.ts`。
25
25
 
26
26
  `--adversarial` 模式下额外触发第三层"敌对视角"审查,由官方 codex plugin 的 `Agent(codex:codex-rescue)` 在 fresh context 中专门挑前两轮意见的漏洞,适合极重要 PR / 安全敏感变更。需用户已装 `codex@openai-codex` plugin,否则降级为双模型审查。
27
27
 
@@ -61,12 +61,37 @@ import { join } from 'node:path'
61
61
  import { fileURLToPath } from 'node:url'
62
62
 
63
63
  // ---------------------------------------------------------------------------
64
- // Vendor → marketplace key (matches plugin-bash-codegen.ts SSoT lookup)
64
+ // Vendor → marketplace keys (ordered preference list).
65
+ //
66
+ // CCG 2.0.0: gemini-ccgx (ccgx-maintained fork shipping P-1..P-21 + W1/W2/I1
67
+ // inline, no repatch needed) is preferred over google-gemini (upstream).
68
+ // Keep this list in sync with src/utils/plugin-bash-codegen.ts.
65
69
  // ---------------------------------------------------------------------------
66
70
 
67
71
  const VENDOR_KEYS = {
68
- codex: 'codex@openai-codex',
69
- gemini: 'gemini@google-gemini',
72
+ codex: ['codex@openai-codex'],
73
+ gemini: ['gemini@gemini-ccgx', 'gemini@google-gemini'],
74
+ }
75
+
76
+ // ---------------------------------------------------------------------------
77
+ // Vendor → preferred entry script (relative to <installPath>/scripts/).
78
+ //
79
+ // gemini: prefer `gemini-batch.mjs` (CCG-only, bypasses ACP broker entirely
80
+ // — direct gemini-cli batch mode via stdin + --output-format json). Falls
81
+ // back to the legacy `gemini-companion.mjs task` (ACP path) only if the
82
+ // batch entry isn't shipped.
83
+ //
84
+ // Why bypass ACP: gemini-cli 0.40+'s session/new RPC runs MCP setup + auth
85
+ // refresh + chat startup synchronously inside the agent. On Windows the
86
+ // gemini-plugin-cc ACP broker + named-pipe transport reliably hangs trivial
87
+ // trips for 5+ minutes even when the direct CLI returns in 10-30s. Batch
88
+ // mode side-steps the entire transport stack (no broker, no detached
89
+ // process, no orphan MCP children).
90
+ // ---------------------------------------------------------------------------
91
+
92
+ const VENDOR_ENTRY_SCRIPTS = {
93
+ codex: ['codex-companion.mjs'],
94
+ gemini: ['gemini-batch.mjs', 'gemini-companion.mjs'],
70
95
  }
71
96
 
72
97
  // ---------------------------------------------------------------------------
@@ -80,6 +105,13 @@ function parseArgs(argv) {
80
105
  json: true,
81
106
  timeoutMs: 600000,
82
107
  maxBudgetUsd: 50,
108
+ // 1.0.5 regression fix: pre-1.0.5 callers always passed --write directly
109
+ // to codex-companion. Default true preserves BC; --no-write opt-out for
110
+ // read-only review flows.
111
+ write: true,
112
+ model: null,
113
+ effort: null,
114
+ cwd: null,
83
115
  }
84
116
 
85
117
  const positional = []
@@ -96,6 +128,11 @@ function parseArgs(argv) {
96
128
  case '--no-json': opts.json = false; break
97
129
  case '--timeout-ms': opts.timeoutMs = Number.parseInt(next(), 10); break
98
130
  case '--max-budget-usd': opts.maxBudgetUsd = Number.parseFloat(next()); break
131
+ case '--write': opts.write = true; break
132
+ case '--no-write': opts.write = false; break
133
+ case '--model': opts.model = next(); break
134
+ case '--effort': opts.effort = next(); break
135
+ case '--cwd': opts.cwd = next(); break
99
136
  case '--help':
100
137
  case '-h':
101
138
  printHelp()
@@ -131,6 +168,11 @@ Optional:
131
168
  --no-json Disable --json (text output)
132
169
  --timeout-ms <N> Kill companion after N ms (default: 600000)
133
170
  --max-budget-usd <N> Per-call cost cap (default: 50)
171
+ --write Enable workspace-write sandbox (default: true; codex needs this to spawn read commands too under default approval policy)
172
+ --no-write Force read-only sandbox (companion approval-policy will decline most commands)
173
+ --model <name> Override model (codex only; gemini ignores)
174
+ --effort <level> Reasoning effort: none|minimal|low|medium|high|xhigh (codex only)
175
+ --cwd <path> Override working directory for the companion call. codex sandbox is cwd-bound; set this when auditing a repo other than the caller's cwd.
134
176
 
135
177
  Outputs JSON to stdout: {status, vendor, version, durationMs, exitCode, stdout, stderr, error?}
136
178
  `)
@@ -154,21 +196,41 @@ function discoverCompanion(vendor, homeDir = homedir()) {
154
196
  return { error: `installed_plugins.json parse failed: ${e.message}` }
155
197
  }
156
198
 
157
- const key = VENDOR_KEYS[vendor]
158
- const instances = raw?.plugins?.[key]
159
- if (!Array.isArray(instances) || instances.length === 0) {
160
- return { error: `plugin ${key} not installed (no entry in installed_plugins.json)` }
199
+ // Try preferred keys in order — fork first, upstream fallback.
200
+ const keys = VENDOR_KEYS[vendor]
201
+ let instances = null
202
+ let matchedKey = null
203
+ for (const key of keys) {
204
+ const candidate = raw?.plugins?.[key]
205
+ if (Array.isArray(candidate) && candidate.length > 0) {
206
+ instances = candidate
207
+ matchedKey = key
208
+ break
209
+ }
210
+ }
211
+ if (!instances || instances.length === 0) {
212
+ return { error: `${vendor} plugin not installed (tried ${keys.join(', ')})` }
161
213
  }
162
214
 
163
215
  const inst = instances[0]
164
216
  const installPath = inst?.installPath
165
217
  if (typeof installPath !== 'string' || !installPath) {
166
- return { error: `plugin ${key} has no installPath in installed_plugins.json` }
218
+ return { error: `plugin ${matchedKey} has no installPath in installed_plugins.json` }
167
219
  }
168
220
 
169
- const companionPath = join(installPath, 'scripts', `${vendor}-companion.mjs`)
170
- if (!existsSync(companionPath)) {
171
- return { error: `companion script missing: ${companionPath}` }
221
+ // Walk the preferred entry-script list. First existing file wins.
222
+ // BC: vendors with only the legacy companion still resolve correctly.
223
+ const entryCandidates = VENDOR_ENTRY_SCRIPTS[vendor] ?? [`${vendor}-companion.mjs`]
224
+ let companionPath = null
225
+ for (const name of entryCandidates) {
226
+ const candidate = join(installPath, 'scripts', name)
227
+ if (existsSync(candidate)) {
228
+ companionPath = candidate
229
+ break
230
+ }
231
+ }
232
+ if (!companionPath) {
233
+ return { error: `no entry script found for ${vendor} (tried: ${entryCandidates.join(', ')}) in ${join(installPath, 'scripts')}` }
172
234
  }
173
235
 
174
236
  return {
@@ -238,6 +300,15 @@ async function main(argv) {
238
300
  promptBody,
239
301
  ]
240
302
  if (opts.json) companionArgs.push('--json')
303
+ // codex-companion flag pass-through (1.0.5 regression fix). gemini-companion
304
+ // ignores unknown flags safely; both vendors accept this surface.
305
+ if (opts.write) companionArgs.push('--write')
306
+ if (opts.model) companionArgs.push('--model', opts.model)
307
+ if (opts.effort) companionArgs.push('--effort', opts.effort)
308
+ // --cwd pass-through: codex sandbox is cwd-bound. For cross-repo audits
309
+ // the caller must set this to the repo being audited (codex-companion
310
+ // does not expose --add-dir; multi-workspace requires deeper changes).
311
+ if (opts.cwd) companionArgs.push('--cwd', opts.cwd)
241
312
 
242
313
  const startedAt = Date.now()
243
314
 
@@ -217,7 +217,7 @@ function exitMissingBackend(backend) {
217
217
  const pluginInstall = backend === 'codex'
218
218
  ? '/plugins install codex@openai-codex'
219
219
  : backend === 'gemini'
220
- ? '/plugins install gemini@google-gemini'
220
+ ? '/plugins install gemini@gemini-ccgx (ccgx 2.0.0 fork, recommended) — or /plugins install gemini@google-gemini (upstream)'
221
221
  : null;
222
222
  const npmInstall = backend === 'codex'
223
223
  ? 'npm i -g @openai/codex'
@@ -51,8 +51,23 @@ function findPluginVersion() {
51
51
  return versions[versions.length - 1];
52
52
  }
53
53
 
54
- const VERSION = findPluginVersion();
55
- const SCRIPTS = join(PLUGIN_BASE, VERSION, "scripts");
54
+ // Allow redirecting the patch target to a custom location, e.g. a local fork.
55
+ // When GEMINI_PLUGIN_SCRIPTS is set, it must point directly to the `scripts/`
56
+ // directory (containing acp-broker.mjs, gemini-companion.mjs, lib/...).
57
+ // Falls back to ~/.claude/plugins/cache/.../gemini/<latest>/scripts/.
58
+ let SCRIPTS;
59
+ let VERSION;
60
+ if (process.env.GEMINI_PLUGIN_SCRIPTS) {
61
+ SCRIPTS = process.env.GEMINI_PLUGIN_SCRIPTS;
62
+ if (!existsSync(SCRIPTS)) {
63
+ throw new Error(`GEMINI_PLUGIN_SCRIPTS path does not exist: ${SCRIPTS}`);
64
+ }
65
+ VERSION = `<custom: ${SCRIPTS}>`;
66
+ }
67
+ else {
68
+ VERSION = findPluginVersion();
69
+ SCRIPTS = join(PLUGIN_BASE, VERSION, "scripts");
70
+ }
56
71
 
57
72
  console.log(`Patching gemini plugin ${VERSION} at:\n ${SCRIPTS}\n`);
58
73