vibe-gate-mcp 0.1.7 → 0.1.8

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 CHANGED
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.8] - 2026-09-24
11
+
12
+ ### Fixed
13
+
14
+ - Resolve explicitly selected CLI names through Windows `PATH` and `PATHEXT` before spawning, including Cursor's `agent.cmd` and legacy `cursor-agent.cmd` shims.
15
+ - Launch Windows `.cmd` and `.bat` shims through `cmd.exe` while keeping native executables on the direct spawn path.
16
+
17
+ ### Tests
18
+
19
+ - Cover Windows `PATHEXT` resolution for explicit `agent` and `cursor-agent` commands and safely quoted batch-shim invocation.
20
+
10
21
  ## [0.1.7] - 2026-09-24
11
22
 
12
23
  ### Fixed
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { basename, delimiter, dirname, extname, isAbsolute, join, normalize, relative, resolve } from "node:path";
2
+ import * as path from "node:path";
3
+ import { basename, dirname, extname, isAbsolute, join, normalize, relative, resolve } from "node:path";
3
4
  import { fileURLToPath } from "node:url";
4
5
  import { config } from "dotenv";
5
6
  import { accessSync, constants, existsSync, readFileSync } from "node:fs";
@@ -2487,9 +2488,32 @@ const WINDOWS_EXECUTABLE_EXTENSIONS = /* @__PURE__ */ new Set([
2487
2488
  ]);
2488
2489
  const WINDOWS_BATCH_EXTENSIONS = /* @__PURE__ */ new Set([".BAT", ".CMD"]);
2489
2490
  function executableExtensions(command, isWindows, pathExt = process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD") {
2490
- if (!isWindows || extname(command)) return [""];
2491
+ const commandExtension = isWindows ? path.win32.extname(command) : extname(command);
2492
+ if (!isWindows || commandExtension) return [""];
2491
2493
  return pathExt.split(";").filter((extension) => WINDOWS_EXECUTABLE_EXTENSIONS.has(extension.toUpperCase()));
2492
2494
  }
2495
+ function executableCandidates(command, options = {}) {
2496
+ const isWindows = (options.platform ?? process.platform) === "win32";
2497
+ const pathApi = isWindows ? path.win32 : path;
2498
+ const hasDirectory = pathApi.isAbsolute(command) || command.includes("/") || command.includes("\\");
2499
+ const pathValue = options.pathValue ?? process.env.PATH ?? process.env.Path ?? "";
2500
+ const pathDelimiter = isWindows ? ";" : path.delimiter;
2501
+ const bases = hasDirectory ? [pathApi.resolve(command)] : pathValue.split(pathDelimiter).filter(Boolean).map((directory) => pathApi.join(directory, command));
2502
+ const extensions = executableExtensions(command, isWindows, options.pathExt);
2503
+ return bases.flatMap((base) => extensions.map((extension) => extension ? `${base}${extension}` : base));
2504
+ }
2505
+ function findExecutable(command, options = {}) {
2506
+ const isWindows = (options.platform ?? process.platform) === "win32";
2507
+ const canAccess = options.canAccess ?? ((candidate, windows) => {
2508
+ try {
2509
+ accessSync(candidate, windows ? constants.F_OK : constants.X_OK);
2510
+ return true;
2511
+ } catch {
2512
+ return false;
2513
+ }
2514
+ });
2515
+ for (const candidate of executableCandidates(command, options)) if (canAccess(candidate, isWindows)) return candidate;
2516
+ }
2493
2517
  function quoteCmdToken(value) {
2494
2518
  if (/[%"\0\r\n]/.test(value)) throw new Error("Windows CLI arguments cannot contain percent signs, quotes, or line breaks.");
2495
2519
  return `"${value}"`;
@@ -2556,10 +2580,11 @@ function sanitizeStderr(stderr) {
2556
2580
  }
2557
2581
  function runCli({ command, args, cwd, input, timeoutMs, label, env, keepCredentialEnvKeys }) {
2558
2582
  return new Promise((resolve, reject) => {
2559
- const target = buildCliSpawnTarget(command, args);
2583
+ const childEnv = createChildEnv(env, keepCredentialEnvKeys);
2584
+ const target = buildCliSpawnTarget(findExecutable(command, { pathValue: childEnv.PATH ?? childEnv.Path }) ?? command, args);
2560
2585
  const child = spawn(target.command, target.args, {
2561
2586
  cwd,
2562
- env: createChildEnv(env, keepCredentialEnvKeys),
2587
+ env: childEnv,
2563
2588
  shell: false,
2564
2589
  windowsHide: true,
2565
2590
  ...target.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {},
@@ -3041,9 +3066,6 @@ function createOpenCodeCliProvider(command, model, timeoutMs) {
3041
3066
  }
3042
3067
  //#endregion
3043
3068
  //#region src/llm/index.ts
3044
- /**
3045
- * LLM provider factory and local CLI auto-detection.
3046
- */
3047
3069
  const AUTO_DETECT_CLI_ORDER = [
3048
3070
  PROVIDERS.CODEX_CLI,
3049
3071
  PROVIDERS.CLAUDE_CODE,
@@ -3064,27 +3086,6 @@ function commandCandidates(id, config) {
3064
3086
  if (id === PROVIDERS.CURSOR_AGENT) return [CLI_DEFAULT_COMMANDS[id], CURSOR_AGENT_LEGACY_COMMAND];
3065
3087
  return [CLI_DEFAULT_COMMANDS[id]];
3066
3088
  }
3067
- function commandSearchBases(command) {
3068
- if (isAbsolute(command) || command.includes("/") || command.includes("\\")) return [resolve(command)];
3069
- return (process.env.PATH ?? process.env.Path ?? "").split(delimiter).filter(Boolean).map((dir) => join(dir, command));
3070
- }
3071
- function canExecute(path, isWindows) {
3072
- try {
3073
- accessSync(path, isWindows ? constants.F_OK : constants.X_OK);
3074
- return true;
3075
- } catch {
3076
- return false;
3077
- }
3078
- }
3079
- function findExecutable(command) {
3080
- const isWindows = process.platform === "win32";
3081
- const bases = commandSearchBases(command);
3082
- const extensions = executableExtensions(command, isWindows);
3083
- for (const base of bases) for (const extension of extensions) {
3084
- const candidate = extension ? `${base}${extension}` : base;
3085
- if (canExecute(candidate, isWindows)) return candidate;
3086
- }
3087
- }
3088
3089
  function hasOpenCodeModel(config) {
3089
3090
  const model = config.criticModel;
3090
3091
  const separator = model?.indexOf("/") ?? -1;
@@ -8,7 +8,7 @@ The CLI is installed locally, but its model request still goes to the provider.
8
8
 
9
9
  When `CRITIC_PROVIDER` is empty or unset, Vibe-Gate checks local CLI executables in this order: Codex (`codex`), Claude Code (`claude`), Cursor Agent (`agent`, then legacy `cursor-agent`), and OpenCode CLI (`opencode`). OpenCode CLI is considered only with a `provider/model` `CRITIC_MODEL`, and that model format moves it to the front of the default order. Configured `*_CLI_PATH` values are checked before default PATH names, using the same provider order. The first tool response reports the selected CLI in `providerNotice`. Detection checks whether the executable is available; it does not verify that the CLI is signed in. If the selected CLI needs login, Vibe-Gate returns its error and does not switch to another account or service. If no local CLI is found, Vibe-Gate retains its OpenAI default and requires `OPENAI_API_KEY`. Set `CRITIC_PROVIDER` to select a provider explicitly.
10
10
 
11
- On Windows, discovery follows `PATHEXT` for `.COM`, `.EXE`, `.BAT`, and `.CMD` files. Batch shims (`.bat` and `.cmd`) run through `cmd.exe`; native executables run directly.
11
+ On Windows, command names are resolved through `PATH` and `PATHEXT` for `.COM`, `.EXE`, `.BAT`, and `.CMD` files whether `CRITIC_PROVIDER` is automatic or explicitly set. Batch shims (`.bat` and `.cmd`) run through `cmd.exe`; native executables run directly.
12
12
 
13
13
  | `CRITIC_PROVIDER` | Command | Sign in | Model selection |
14
14
  | ----------------- | ----------------------------------------- | ---------------------------------------------- | -------------------------------------------------- |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-gate-mcp",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "description": "Adversarial Quality Gate MCP for vibe-coding: IDE AI vs Critic AI, human decides on deadlock",
6
6
  "license": "MIT",