llm-cli-gateway 1.17.3 → 1.17.5

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +1 -1
  3. package/dist/approval-manager.js +0 -8
  4. package/dist/async-job-manager.d.ts +0 -113
  5. package/dist/async-job-manager.js +6 -124
  6. package/dist/cache-stats.d.ts +0 -89
  7. package/dist/cache-stats.js +0 -62
  8. package/dist/claude-mcp-config.js +0 -1
  9. package/dist/cli-updater.d.ts +0 -8
  10. package/dist/cli-updater.js +0 -12
  11. package/dist/codex-json-parser.d.ts +0 -20
  12. package/dist/codex-json-parser.js +0 -21
  13. package/dist/config.d.ts +0 -31
  14. package/dist/config.js +2 -72
  15. package/dist/db.d.ts +0 -18
  16. package/dist/db.js +0 -22
  17. package/dist/doctor.d.ts +0 -49
  18. package/dist/doctor.js +0 -47
  19. package/dist/endpoint-exposure.js +0 -1
  20. package/dist/executor.d.ts +0 -19
  21. package/dist/executor.js +3 -38
  22. package/dist/flight-recorder.d.ts +0 -26
  23. package/dist/flight-recorder.js +1 -70
  24. package/dist/gemini-json-parser.d.ts +0 -25
  25. package/dist/gemini-json-parser.js +0 -28
  26. package/dist/health.d.ts +0 -3
  27. package/dist/health.js +0 -3
  28. package/dist/index.d.ts +12 -208
  29. package/dist/index.js +116 -588
  30. package/dist/job-store.d.ts +0 -74
  31. package/dist/job-store.js +1 -73
  32. package/dist/logger.d.ts +0 -7
  33. package/dist/logger.js +0 -6
  34. package/dist/migrate-sessions.d.ts +0 -3
  35. package/dist/migrate-sessions.js +0 -16
  36. package/dist/migrate.js +1 -18
  37. package/dist/mistral-meta-json-parser.js +0 -67
  38. package/dist/model-registry.js +0 -13
  39. package/dist/pricing.d.ts +0 -46
  40. package/dist/pricing.js +0 -47
  41. package/dist/process-monitor.d.ts +0 -15
  42. package/dist/process-monitor.js +2 -31
  43. package/dist/prompt-parts.d.ts +6 -31
  44. package/dist/prompt-parts.js +0 -11
  45. package/dist/provider-status.d.ts +0 -8
  46. package/dist/provider-status.js +0 -11
  47. package/dist/request-helpers.d.ts +4 -316
  48. package/dist/request-helpers.js +13 -231
  49. package/dist/resources.d.ts +0 -20
  50. package/dist/resources.js +1 -34
  51. package/dist/retry.d.ts +0 -45
  52. package/dist/retry.js +3 -40
  53. package/dist/session-manager-pg.d.ts +0 -32
  54. package/dist/session-manager-pg.js +0 -32
  55. package/dist/session-manager.d.ts +0 -21
  56. package/dist/session-manager.js +1 -15
  57. package/dist/stream-json-parser.d.ts +0 -18
  58. package/dist/stream-json-parser.js +0 -22
  59. package/dist/upstream-contracts.d.ts +0 -55
  60. package/dist/upstream-contracts.js +86 -64
  61. package/dist/validation-orchestrator.js +0 -3
  62. package/dist/worktree-manager.d.ts +0 -9
  63. package/dist/worktree-manager.js +0 -21
  64. package/package.json +1 -1
@@ -47,8 +47,11 @@ export const UPSTREAM_CLI_CONTRACTS = {
47
47
  "excludeDynamicSystemPromptSections",
48
48
  "fallbackModel",
49
49
  "jsonSchema",
50
- // Phase 4 slice ζ
51
50
  "addDir",
51
+ "noSessionPersistence",
52
+ "settingSources",
53
+ "settings",
54
+ "tools",
52
55
  "approvalStrategy",
53
56
  "mcpServers",
54
57
  "strictMcpConfig",
@@ -116,6 +119,22 @@ export const UPSTREAM_CLI_CONTRACTS = {
116
119
  },
117
120
  "--continue": { arity: "none", description: "Continue active session" },
118
121
  "--session-id": { arity: "one", description: "Session id" },
122
+ "--no-session-persistence": {
123
+ arity: "none",
124
+ description: "Do not persist the session to disk (ephemeral; mirrors Codex --ephemeral)",
125
+ },
126
+ "--setting-sources": {
127
+ arity: "one",
128
+ description: "Comma-separated setting sources to load (user|project|local)",
129
+ },
130
+ "--settings": {
131
+ arity: "one",
132
+ description: "Settings JSON file path or literal (can define hooks/permissions/model)",
133
+ },
134
+ "--tools": {
135
+ arity: "variadic",
136
+ description: 'Restrict the available built-in tool set ("" disables all)',
137
+ },
119
138
  },
120
139
  env: {},
121
140
  conformanceFixtures: [
@@ -132,16 +151,12 @@ export const UPSTREAM_CLI_CONTRACTS = {
132
151
  expect: "fail",
133
152
  },
134
153
  {
135
- // Phase 4 slice η: --fallback-model wired through prepareClaudeRequest.
136
154
  id: "claude-fallback-model",
137
155
  description: "Phase 4 slice η: --fallback-model accepted",
138
156
  args: ["-p", "hello", "--fallback-model", "claude-haiku-4-5-20251001"],
139
157
  expect: "pass",
140
158
  },
141
159
  {
142
- // Phase 4 slice η: --json-schema accepts an inline JSON Schema literal
143
- // (per `claude --help` example), not a path. Codex parity for
144
- // structured-output validation in one slice.
145
160
  id: "claude-json-schema",
146
161
  description: "Phase 4 slice η: --json-schema accepts inline JSON literal",
147
162
  args: [
@@ -155,18 +170,29 @@ export const UPSTREAM_CLI_CONTRACTS = {
155
170
  expect: "pass",
156
171
  },
157
172
  {
158
- // Phase 4 slice ζ: --add-dir wired through prepareClaudeHighImpactFlags.
159
- // Repeated once per directory; each instance has arity:"one".
160
173
  id: "claude-add-dir",
161
174
  description: "Phase 4 slice ζ: repeated --add-dir is accepted",
162
175
  args: ["-p", "hello", "--add-dir", "/tmp/a", "--add-dir", "/tmp/b"],
163
176
  expect: "pass",
164
177
  },
165
178
  {
166
- // Claude CLI 2.x: stream-json requires --verbose alongside --print.
167
- // The gateway emits all three together; this fixture pins the combo
168
- // so a future removal of --verbose breaks loudly here instead of
169
- // silently at runtime against the upstream CLI.
179
+ id: "claude-session-settings-tools",
180
+ description: "Claude 2.x: --no-session-persistence, --setting-sources, --settings, and --tools (variadic) are accepted",
181
+ args: [
182
+ "-p",
183
+ "hello",
184
+ "--no-session-persistence",
185
+ "--setting-sources",
186
+ "project,local",
187
+ "--settings",
188
+ "{}",
189
+ "--tools",
190
+ "Read",
191
+ "Edit",
192
+ ],
193
+ expect: "pass",
194
+ },
195
+ {
170
196
  id: "claude-stream-json-requires-verbose",
171
197
  description: "Claude CLI 2.x: --output-format stream-json + --include-partial-messages + --verbose accepted together",
172
198
  args: [
@@ -180,12 +206,6 @@ export const UPSTREAM_CLI_CONTRACTS = {
180
206
  expect: "pass",
181
207
  },
182
208
  {
183
- // Slice κ: when caller marks promptParts with cache_control, the
184
- // gateway emits `-p` as a standalone flag and pipes the JSON
185
- // content-blocks payload over stdin via `--input-format
186
- // stream-json`. The fixture pins the exact argv combination so
187
- // a future regression (re-emitting a positional prompt, dropping
188
- // `--input-format`, etc.) trips loudly here.
189
209
  id: "claude-input-format-stream-json",
190
210
  description: "Slice κ: `-p` standalone + --input-format stream-json + --output-format stream-json + --include-partial-messages + --verbose",
191
211
  args: [
@@ -252,15 +272,10 @@ export const UPSTREAM_CLI_CONTRACTS = {
252
272
  "images",
253
273
  "ignoreUserConfig",
254
274
  "ignoreRules",
255
- // Phase 4 slice ζ
256
275
  "workingDir",
257
276
  "addDir",
258
277
  ],
259
278
  resumeOnlyFlags: ["--last", "--all"],
260
- // Phase 4 slice α (v1.8.0) verified that `codex exec resume` accepts
261
- // `--output-schema` and `-c` (codex-cli 0.133.0 `exec resume --help`),
262
- // so they're no longer forbidden. Current resume help does not accept
263
- // session-profile or working-directory policy flags.
264
279
  resumeForbiddenFlags: ["--sandbox", "-C", "--cd", "--add-dir", "--profile"],
265
280
  flags: {
266
281
  "--last": { arity: "none", description: "Resume latest session" },
@@ -323,8 +338,6 @@ export const UPSTREAM_CLI_CONTRACTS = {
323
338
  arity: "none",
324
339
  description: "Resume picker: show all sessions without cwd filtering",
325
340
  },
326
- // The gateway emits the short form `-C`, and the advisory contract also
327
- // tracks the long `--cd` alias advertised by current Codex exec help.
328
341
  "-C": {
329
342
  arity: "one",
330
343
  description: "Working root for the session (Phase 4 slice ζ; new sessions only)",
@@ -365,9 +378,6 @@ export const UPSTREAM_CLI_CONTRACTS = {
365
378
  expect: "fail",
366
379
  },
367
380
  {
368
- // Phase 4 slice α: --output-schema IS accepted on resume per
369
- // codex-cli 0.133.0; this fixture pins the new behaviour so future
370
- // contract changes can't silently regress.
371
381
  id: "codex-resume-output-schema",
372
382
  description: "Phase 4 slice α: --output-schema accepted on resume (codex-cli 0.133.0)",
373
383
  args: ["exec", "resume", "--output-schema", "/tmp/schema.json", "session-id", "hello"],
@@ -476,8 +486,8 @@ export const UPSTREAM_CLI_CONTRACTS = {
476
486
  "policyFiles",
477
487
  "adminPolicyFiles",
478
488
  "attachments",
479
- // Phase 4 slice γ
480
489
  "skipTrust",
490
+ "yolo",
481
491
  ],
482
492
  flags: {
483
493
  "-p": { arity: "one", description: "Prompt text" },
@@ -503,6 +513,10 @@ export const UPSTREAM_CLI_CONTRACTS = {
503
513
  arity: "none",
504
514
  description: "Trust workspace for this session (Phase 4 slice γ)",
505
515
  },
516
+ "--yolo": {
517
+ arity: "none",
518
+ description: "Auto-approve all actions (gemini -y/--yolo). Functionally equivalent to --approval-mode yolo; the gateway emits at most one of the two.",
519
+ },
506
520
  },
507
521
  env: {},
508
522
  conformanceFixtures: [
@@ -524,6 +538,12 @@ export const UPSTREAM_CLI_CONTRACTS = {
524
538
  args: ["-p", "hello", "--skip-trust"],
525
539
  expect: "pass",
526
540
  },
541
+ {
542
+ id: "gemini-yolo",
543
+ description: "--yolo (auto-approve all) is accepted",
544
+ args: ["-p", "hello", "--yolo"],
545
+ expect: "pass",
546
+ },
527
547
  {
528
548
  id: "gemini-stream-json",
529
549
  description: "Phase 4 slice ε: -o stream-json is accepted",
@@ -566,16 +586,15 @@ export const UPSTREAM_CLI_CONTRACTS = {
566
586
  "mcpServers",
567
587
  "allowedTools",
568
588
  "disallowedTools",
569
- // Phase 4 slice δ
570
589
  "maxTurns",
571
- // Phase 4 slice ζ
572
590
  "workingDir",
573
- // Phase 4 slice θ — Grok HIGH parity
574
591
  "sandbox",
575
592
  "rules",
576
593
  "systemPromptOverride",
577
594
  "allow",
578
595
  "deny",
596
+ "compactionMode",
597
+ "compactionDetail",
579
598
  ],
580
599
  flags: {
581
600
  "-p": { arity: "one", description: "Prompt text" },
@@ -609,10 +628,6 @@ export const UPSTREAM_CLI_CONTRACTS = {
609
628
  arity: "one",
610
629
  description: "Working directory for the invocation (Phase 4 slice ζ)",
611
630
  },
612
- // Phase 4 slice θ — Grok HIGH parity. `--sandbox` is freeform per
613
- // `grok --help` on 0.1.210 (no `[possible values: …]` list, unlike
614
- // --effort / --permission-mode / --output-format), so we register
615
- // it without a `values` constraint.
616
631
  "--sandbox": {
617
632
  arity: "one",
618
633
  description: "Sandbox profile for filesystem + network access (Phase 4 slice θ; freeform passthrough; env: GROK_SANDBOX)",
@@ -665,6 +680,16 @@ export const UPSTREAM_CLI_CONTRACTS = {
665
680
  arity: "optional",
666
681
  description: "Start the session in a new git worktree, optionally named",
667
682
  },
683
+ "--compaction-mode": {
684
+ arity: "one",
685
+ values: ["summary", "transcript", "segments"],
686
+ description: "Compaction mode (default summary; sets GROK_COMPACTION_MODE). `segments` persists per-segment markdown.",
687
+ },
688
+ "--compaction-detail": {
689
+ arity: "one",
690
+ values: ["none", "minimal", "balanced", "verbose"],
691
+ description: "Segment verbatim detail (default verbose; sets GROK_COMPACTION_DETAIL). Only affects `--compaction-mode segments`.",
692
+ },
668
693
  },
669
694
  env: {},
670
695
  conformanceFixtures: [
@@ -762,6 +787,18 @@ export const UPSTREAM_CLI_CONTRACTS = {
762
787
  ],
763
788
  expect: "pass",
764
789
  },
790
+ {
791
+ id: "grok-compaction",
792
+ description: "Grok 0.2.x: --compaction-mode and --compaction-detail accepted with valid enum values",
793
+ args: ["-p", "hello", "--compaction-mode", "segments", "--compaction-detail", "balanced"],
794
+ expect: "pass",
795
+ },
796
+ {
797
+ id: "grok-compaction-mode-invalid",
798
+ description: "Grok --compaction-mode rejects a value outside the contract enum",
799
+ args: ["-p", "hello", "--compaction-mode", "aggressive"],
800
+ expect: "fail",
801
+ },
765
802
  ],
766
803
  },
767
804
  mistral: {
@@ -787,19 +824,14 @@ export const UPSTREAM_CLI_CONTRACTS = {
787
824
  "resumeLatest",
788
825
  "createNewSession",
789
826
  "permissionMode",
790
- "effort",
791
- "reasoningEffort",
792
827
  "approvalStrategy",
793
828
  "mcpServers",
794
829
  "allowedTools",
795
830
  "disallowedTools",
796
- // Phase 4 slice γ
797
831
  "trust",
798
- // Phase 4 slice δ
799
832
  "maxTurns",
800
833
  "maxPrice",
801
834
  "maxTokens",
802
- // Phase 4 slice ζ
803
835
  "workingDir",
804
836
  "addDir",
805
837
  ],
@@ -815,8 +847,6 @@ export const UPSTREAM_CLI_CONTRACTS = {
815
847
  values: ["default", "plan", "accept-edits", "auto-approve", "chat", "explore", "lean"],
816
848
  description: "Agent/permission mode",
817
849
  },
818
- "--effort": { arity: "one", description: "Reasoning effort" },
819
- "--reasoning-effort": { arity: "one", description: "Reasoning effort override" },
820
850
  "--enabled-tools": { arity: "one", description: "Enabled tool" },
821
851
  "--resume": { arity: "one", description: "Resume session" },
822
852
  "--continue": { arity: "none", description: "Continue latest session" },
@@ -831,8 +861,6 @@ export const UPSTREAM_CLI_CONTRACTS = {
831
861
  },
832
862
  "--max-price": {
833
863
  arity: "one",
834
- // Decimal-only: matches the MAX_PRICE_SCHEMA min(1e-6) lower bound
835
- // that keeps String(N) in decimal form (no scientific notation).
836
864
  pattern: /^(0|[1-9][0-9]*)(\.[0-9]+)?$/,
837
865
  description: "Cumulative cost cap in USD (Phase 4 slice δ, programmatic mode only)",
838
866
  },
@@ -932,6 +960,20 @@ export const UPSTREAM_CLI_CONTRACTS = {
932
960
  env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
933
961
  expect: "pass",
934
962
  },
963
+ {
964
+ id: "mistral-effort-rejected",
965
+ description: "vibe 2.x advertises no reasoning-effort surface: a raw --effort arg is rejected by the contract (mirrors the CLI's own 'unrecognized arguments' failure)",
966
+ args: ["-p", "hello", "--agent", "auto-approve", "--effort", "high"],
967
+ env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
968
+ expect: "fail",
969
+ },
970
+ {
971
+ id: "mistral-reasoning-effort-rejected",
972
+ description: "vibe 2.x: a raw --reasoning-effort arg is rejected by the contract",
973
+ args: ["-p", "hello", "--agent", "auto-approve", "--reasoning-effort", "medium"],
974
+ env: { VIBE_ACTIVE_MODEL: "mistral-medium-3.5" },
975
+ expect: "fail",
976
+ },
935
977
  ],
936
978
  },
937
979
  };
@@ -1093,24 +1135,8 @@ function validateFlagValue(cli, arg, flag, value, index, violations) {
1093
1135
  });
1094
1136
  }
1095
1137
  }
1096
- /**
1097
- * Best-effort, advisory-only extraction of long-form flags from raw --help text.
1098
- * Returns a sorted array of unique `--foo-bar` style flags discovered in the output.
1099
- *
1100
- * Heuristics:
1101
- * - Matches common option declaration lines emitted by clap, yargs, commander, custom TUIs, etc.
1102
- * - Lowercases for stable comparison against our contract keys.
1103
- * - Intentionally conservative: ignores obvious noise (URLs, prose in descriptions).
1104
- *
1105
- * This powers the bidirectional drift detector (extra flags the installed binary
1106
- * advertises that our contract does not yet allow). It is NEVER used for argv
1107
- * validation — only for the upstream scanner and `upstream_contracts` probe reports.
1108
- */
1109
1138
  export function extractDiscoveredFlags(helpText) {
1110
1139
  const discovered = new Set();
1111
- // Long flags: --foo, --foo-bar, --foo_bar (some CLIs normalize _ to - in display).
1112
- // Only inspect option declaration lines so prose such as
1113
- // "(Claude Code: --allowedTools)" does not create false drift.
1114
1140
  const longRe = /--([a-z0-9][a-z0-9_-]{1,}[a-z0-9]?)/g;
1115
1141
  for (const line of helpText.split(/\r?\n/)) {
1116
1142
  const trimmed = line.trimStart();
@@ -1175,7 +1201,6 @@ export function probeInstalledCliContract(cli, timeoutMs = 5_000) {
1175
1201
  const discoveredFlags = extractDiscoveredFlags(helpText);
1176
1202
  const contractFlagSet = new Set(Object.keys(contract.flags));
1177
1203
  const extraFlags = discoveredFlags.filter(f => !contractFlagSet.has(f));
1178
- // Cheap version hint: first line that looks like a version banner
1179
1204
  const versionMatch = helpText.match(/^\s*(?:[A-Za-z][\w .-]+)?v?\d+\.\d+\S*/m);
1180
1205
  const versionHint = versionMatch ? versionMatch[0].trim().slice(0, 80) : undefined;
1181
1206
  const helpHash = createHash("sha256").update(helpText).digest("hex");
@@ -1204,9 +1229,6 @@ export function buildUpstreamContractReport(options = {}) {
1204
1229
  {
1205
1230
  executable: contract.executable,
1206
1231
  upstream: contract.upstream,
1207
- // Pure metadata pointers (changelog URLs, package name, watch
1208
- // categories). Enriched from the CliContract — the single source of
1209
- // truth — so report consumers and the scanner read the same values.
1210
1232
  upstreamMetadata: contract.upstreamMetadata
1211
1233
  ? {
1212
1234
  sourceUrls: contract.upstreamMetadata.sourceUrls,
@@ -131,9 +131,6 @@ function plannedJudgeSynthesis(input) {
131
131
  }
132
132
  function buildProviderArgs(provider, prompt) {
133
133
  if (provider === "claude" || provider === "grok" || provider === "mistral") {
134
- // Mistral Vibe mirrors Grok's `-p PROMPT` headless surface. Model selection
135
- // is via VIBE_ACTIVE_MODEL env var (no --model flag); for validation runs we
136
- // let the user's environment pick the active model.
137
134
  return ["-p", prompt];
138
135
  }
139
136
  if (provider === "codex")
@@ -25,15 +25,6 @@ export declare class WorktreeCollisionError extends WorktreeError {
25
25
  }
26
26
  export declare function sanitizeWorktreeName(input: string): string;
27
27
  export declare function createWorktree(opts: CreateWorktreeOptions): Promise<WorktreeHandle>;
28
- /**
29
- * Build a SessionCleanupHook that tears down per-session worktrees. The
30
- * hook reads `session.metadata.worktreePath` (recorded by
31
- * `resolveWorktreeForRequest`) and the optional `session.metadata.worktreeName`,
32
- * derives `repoRoot` from the path layout (`<repoRoot>/.worktrees/<name>`),
33
- * and fires `removeWorktree` asynchronously. Failures are logged by
34
- * `removeWorktree` itself — the hook always resolves so session deletion
35
- * never blocks on git.
36
- */
37
28
  export declare function createWorktreeSessionCleanupHook(logger: Logger): (session: {
38
29
  id: string;
39
30
  metadata?: Record<string, unknown>;
@@ -67,7 +67,6 @@ async function execGit(repoRoot, args, logger) {
67
67
  proc.kill("SIGKILL");
68
68
  }
69
69
  catch {
70
- // ignore — process may already be gone
71
70
  }
72
71
  rejectExec(new WorktreeError(`git ${args.join(" ")} timed out after ${GIT_TIMEOUT_MS}ms`));
73
72
  }, GIT_TIMEOUT_MS);
@@ -116,8 +115,6 @@ export async function createWorktree(opts) {
116
115
  const worktreesDir = join(opts.repoRoot, ".worktrees");
117
116
  const expectedPrefix = worktreesDir + sep;
118
117
  const worktreePath = resolvePath(opts.repoRoot, ".worktrees", name);
119
- // Defense in depth — sanitizeWorktreeName already blocks slashes, but
120
- // double-check that the resolved path is under <repoRoot>/.worktrees/.
121
118
  if (!worktreePath.startsWith(expectedPrefix)) {
122
119
  throw new WorktreeError(`resolved worktree path escapes the expected prefix: ${worktreePath} (expected under ${expectedPrefix})`);
123
120
  }
@@ -134,8 +131,6 @@ export async function createWorktree(opts) {
134
131
  if (!registered) {
135
132
  throw new WorktreeCollisionError(worktreePath);
136
133
  }
137
- // Resume reuse: the worktree already exists and is registered with git.
138
- // Return a handle pointing at it without touching anything.
139
134
  logger.info?.(`reusing existing worktree at ${worktreePath}`);
140
135
  return {
141
136
  name,
@@ -145,9 +140,6 @@ export async function createWorktree(opts) {
145
140
  };
146
141
  }
147
142
  if (registered) {
148
- // Path is registered but the directory is gone — let git prune it
149
- // before we recreate, so `worktree add` doesn't error on the stale
150
- // registration.
151
143
  const prune = await execGit(opts.repoRoot, ["worktree", "prune"], logger);
152
144
  if (prune.code !== 0) {
153
145
  logWarn(logger, `git worktree prune failed before creating ${name}: ${prune.stderr.trim()}`);
@@ -165,15 +157,6 @@ export async function createWorktree(opts) {
165
157
  createdAt: new Date().toISOString(),
166
158
  };
167
159
  }
168
- /**
169
- * Build a SessionCleanupHook that tears down per-session worktrees. The
170
- * hook reads `session.metadata.worktreePath` (recorded by
171
- * `resolveWorktreeForRequest`) and the optional `session.metadata.worktreeName`,
172
- * derives `repoRoot` from the path layout (`<repoRoot>/.worktrees/<name>`),
173
- * and fires `removeWorktree` asynchronously. Failures are logged by
174
- * `removeWorktree` itself — the hook always resolves so session deletion
175
- * never blocks on git.
176
- */
177
160
  export function createWorktreeSessionCleanupHook(logger) {
178
161
  return async (session) => {
179
162
  const meta = session.metadata ?? {};
@@ -181,8 +164,6 @@ export function createWorktreeSessionCleanupHook(logger) {
181
164
  if (!worktreePath)
182
165
  return;
183
166
  const worktreeName = typeof meta.worktreeName === "string" ? meta.worktreeName : undefined;
184
- // Layout invariant from createWorktree: <repoRoot>/.worktrees/<name>.
185
- // Strip the trailing two segments to recover repoRoot.
186
167
  const marker = `${sep}.worktrees${sep}`;
187
168
  const markerIdx = worktreePath.lastIndexOf(marker);
188
169
  if (markerIdx === -1) {
@@ -206,8 +187,6 @@ export async function removeWorktree(opts) {
206
187
  const branch = `gateway/${opts.name}`;
207
188
  const del = await execGit(opts.repoRoot, ["branch", "-D", branch], logger);
208
189
  if (del.code !== 0) {
209
- // Branch may already be gone (user deleted, never existed if add
210
- // half-failed, etc.). Demote to debug — this is best-effort cleanup.
211
190
  logger.debug?.(`git branch -D ${branch} returned code ${del.code}: ${del.stderr.trim()}`);
212
191
  }
213
192
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-cli-gateway",
3
- "version": "1.17.3",
3
+ "version": "1.17.5",
4
4
  "mcpName": "io.github.verivus-oss/llm-cli-gateway",
5
5
  "description": "MCP server providing unified access to Claude Code, Codex, Gemini, Grok, and Mistral Vibe CLIs with session management, retry logic, async job orchestration, durable job results, and cross-LLM validation.",
6
6
  "license": "MIT",