@ask-llm/plugin 0.16.3 → 0.17.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ask-llm",
3
- "version": "0.16.3",
3
+ "version": "0.17.0",
4
4
  "description": "AI-to-AI collaboration — review code and debate plans across Codex, Grok, Antigravity, Ollama, and Gemini",
5
5
  "author": {
6
6
  "name": "Anton Lykhoyda",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ask-llm",
3
3
  "displayName": "Ask LLM",
4
- "version": "0.16.3",
4
+ "version": "0.17.0",
5
5
  "description": "Portable AI-to-AI review and pairing workflows for Cursor Agent with explicit provider transports.",
6
6
  "author": {
7
7
  "name": "Anton Lykhoyda"
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @ask-llm/plugin
2
2
 
3
+ ## 0.17.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#327](https://github.com/Lykhoyda/ask-llm/pull/327) [`4e0e436`](https://github.com/Lykhoyda/ask-llm/commit/4e0e436b43f7a1bff897bd690c71765420ff32ae) Thanks [@Lykhoyda](https://github.com/Lykhoyda)! - Add Codex `preferred` and `sandbox` to unified `ask-llm` (fail closed on other providers) and let plugin Codex workflows use fully pinned unified MCP when no `ask-codex` leaf is present, without stripping provider options ([#266](https://github.com/Lykhoyda/ask-llm/issues/266)).
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [[`124d93d`](https://github.com/Lykhoyda/ask-llm/commit/124d93dd6dae941ffac37d6583be7cbf0eb0c30f), [`4e0e436`](https://github.com/Lykhoyda/ask-llm/commit/4e0e436b43f7a1bff897bd690c71765420ff32ae)]:
12
+ - @ask-llm/antigravity-mcp@0.7.6
13
+ - @ask-llm/mcp@0.10.0
14
+
3
15
  ## 0.16.3
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -23,16 +23,20 @@ Part of the [Ask LLM](https://github.com/Lykhoyda/ask-llm) monorepo.
23
23
 
24
24
  ### MCP Servers
25
25
 
26
- The plugin bundles only the Codex MCP registration under Claude Code's plugin namespace. After installation or upgrade, fully restart Claude Code and run `/mcp`; `plugin:ask-llm:codex` should be connected. `/sol-review` selects `ask-codex`.
26
+ The recommended cross-provider server is `@ask-llm/mcp`. Keep `npx -y @ask-llm/mcp` as the primary registration; `npm install -g @ask-llm/mcp` is a first-class alternative when you want a pinned global binary. Split provider packages (`@ask-llm/codex-mcp`, `@ask-llm/grok-mcp`, and the others) remain an advanced optimization for a richer per-provider tool surface.
27
+
28
+ The plugin bundles only the Codex MCP registration under Claude Code's plugin namespace. After installation or upgrade, fully restart Claude Code and run `/mcp`; `plugin:ask-llm:codex` should be connected. Codex-facing workflows prefer that bundled `ask-codex` leaf (or a user-scoped `ask-codex`), then a fully pinned unified `ask-llm` call (`provider: "codex"` plus model and Codex options), then the disclosed `codex exec` fallback. An older unified schema that cannot honor those options is reported rather than stripped.
27
29
 
28
30
  `/grok-pair` does not add servers to the plugin. Register the unified Ask LLM server at user scope (the recommended install: it exposes `ask-cursor-agent` for the Cursor Agent route plus the unified `ask-llm` tool, which pair skills call only fully pinned) and, optionally, the split Grok server for the `ask-grok` leaf:
29
31
 
30
32
  ```bash
31
33
  claude mcp add --scope user ask-llm -- npx -y @ask-llm/mcp
34
+ # first-class alternative after `npm install -g @ask-llm/mcp`:
35
+ # claude mcp add --scope user ask-llm -- ask-llm-mcp
32
36
  claude mcp add --scope user grok -- npx -y @ask-llm/grok-mcp
33
37
  ```
34
38
 
35
- Existing user-scoped Codex registrations remain compatible and keep their shorter names. Other providers are registered explicitly at user scope:
39
+ Existing user-scoped Codex registrations remain compatible and keep their shorter names. Other providers are registered explicitly at user scope only when you want their split leaves:
36
40
 
37
41
  ```bash
38
42
  claude mcp add --scope user gemini -- npx -y @ask-llm/gemini-mcp
@@ -40,6 +44,8 @@ claude mcp add --scope user ollama -- npx -y @ask-llm/ollama-mcp
40
44
  claude mcp add --scope user antigravity -- npx -y @ask-llm/antigravity-mcp
41
45
  ```
42
46
 
47
+ If Codex is missing entirely, provision the unified server first (`claude mcp add --scope user ask-llm -- npx -y @ask-llm/mcp`) or the split Codex leaf with `claude mcp add --scope user codex -- npx -y @ask-llm/codex-mcp`. If `/mcp` shows the bundled registration but it is disconnected, run `npx -y @ask-llm/mcp doctor` and restart Claude Code. `/sol-review` preserves source-plugin and session-local MCP/settings context when reading the active `claude mcp list` inventory, reports missing, unavailable, and unsupported-schema states separately, and discloses the explicit `codex exec` fallback after failed health, an incomplete unified schema, or MCP transport failure.
48
+
43
49
  ### Cursor Agent
44
50
 
45
51
  Cursor's supported Agent Skills surface exposes exactly `/codex-pair` and `/grok-pair` (the manifest sets `agents: []`, `commands: []`, and `hooks: {}` so Claude agents and the Claude-format hooks file are never auto-discovered, and the other skills stay Claude/Pi-only until they get Cursor adapters); its MCP surface is `mcp.json`, which bundles only the unified `ask-llm` server. For a source checkout:
@@ -48,9 +54,7 @@ Cursor's supported Agent Skills surface exposes exactly `/codex-pair` and `/grok
48
54
  agent --plugin-dir ./packages/claude-plugin
49
55
  ```
50
56
 
51
- `/codex-pair` requires explicit `model=` and `effort=` values before consent, then uses a separately user-installed `ask-codex` leaf when exposed, otherwise the bundled unified `ask-llm` fully pinned (`provider: "codex"`, model, effort, include directories, session), with resumable Thread ID, cancellation, and result relay. It never guesses MCP-process environment defaults and does not pretend Claude-only hooks are active. `/grok-pair` gives Cursor-native `.cursor/mcp.json` and Tools & MCP reload guidance; it never sends Cursor users to `claude mcp add`. If installing only MCP configuration, the recommended minimal entry is `ask-llm` → `npx -y @ask-llm/mcp` in project `.cursor/mcp.json` or user `~/.cursor/mcp.json` (keep one registration per server — do not duplicate it when the plugin is loaded); add `codex` → `@ask-llm/codex-mcp` or `grok` → `@ask-llm/grok-mcp` only when you specifically want their `ask-codex`/`ask-grok` leaves, then reload MCP/restart Cursor Agent. When Cursor hosts `/grok-pair`, it never recursively invokes Cursor Agent.
52
-
53
- If Codex is missing entirely, register it explicitly with `claude mcp add --scope user codex -- npx -y @ask-llm/codex-mcp`. If `/mcp` shows the bundled registration but it is disconnected, run `npx -y @ask-llm/mcp doctor` and restart Claude Code. `/sol-review` preserves source-plugin and session-local MCP/settings context when reading the active `claude mcp list` inventory, reports missing and unavailable states separately, and discloses the explicit `codex exec` fallback after failed health or MCP transport failure.
57
+ `/codex-pair` requires explicit `model=` and `effort=` values before consent, then uses a separately user-installed `ask-codex` leaf when exposed, otherwise the bundled unified `ask-llm` fully pinned (`provider: "codex"`, model, effort, include directories, sandbox, session), with resumable Thread ID, cancellation, and result relay. It never guesses MCP-process environment defaults and does not pretend Claude-only hooks are active. `/grok-pair` gives Cursor-native `.cursor/mcp.json` and Tools & MCP reload guidance; it never sends Cursor users to `claude mcp add`. If installing only MCP configuration, the recommended minimal entry is `ask-llm` → `npx -y @ask-llm/mcp` in project `.cursor/mcp.json` or user `~/.cursor/mcp.json` (keep one registration per server — do not duplicate it when the plugin is loaded); add `codex` → `@ask-llm/codex-mcp` or `grok` → `@ask-llm/grok-mcp` only when you specifically want their `ask-codex`/`ask-grok` leaves, then reload MCP/restart Cursor Agent. When Cursor hosts `/grok-pair`, it never recursively invokes Cursor Agent.
54
58
 
55
59
  ### Pi
56
60
 
@@ -80,7 +84,7 @@ See the [Pi host guide](https://lykhoyda.github.io/ask-llm/plugin/pi) for securi
80
84
  | `/gemini-review` | Gemini-only code review with confidence filtering |
81
85
  | `/codex-review` | Codex-only code review (precision-first, ≥80 confidence — default for routine PR review) |
82
86
  | `/fable-review` | Isolated, read-only review requesting native Fable, with runtime verification limits disclosed |
83
- | `/sol-review` | Model-pinned GPT-5.6 Sol review through the bundled `ask-codex` MCP tool; missing registration and service unavailability are diagnosed separately before the explicit CLI fallback |
87
+ | `/sol-review` | Model-pinned GPT-5.6 Sol review: prefer bundled or user-scoped `ask-codex`, then fully pinned unified `ask-llm`, then the disclosed CLI fallback |
84
88
  | `/ollama-review` | Local review — no data leaves your machine |
85
89
  | `/brainstorm` | Explicit multi-model brainstorm (default external: Antigravity + Codex); supports an exact no-Gemini Grok + GPT-5.6 Sol panel through Cursor Agent |
86
90
  | `/grok-review` | Grok review through explicit xAI API or Grok CLI harness; no fallback |
@@ -10,6 +10,7 @@ tools:
10
10
  - Read
11
11
  - mcp__codex__ask-codex
12
12
  - mcp__plugin_ask-llm_codex__ask-codex
13
+ - mcp__ask-llm__ask-llm
13
14
  ---
14
15
 
15
16
  <!-- PORTABLE-CONTRACT:START -->
@@ -62,7 +63,11 @@ You are a code review coordinator that leverages OpenAI Codex for independent an
62
63
 
63
64
  ### Phase 2: Review Prompt Construction
64
65
 
65
- When calling `ask-codex`, leave `model` and `preferred` unset and set `reasoningEffort: "high"`. This uses GPT-5.6 Sol at high effort and transparently falls back to GPT-5.6 Terra on quota. Structure the prompt to request confidence scoring AND severity classification:
66
+ When calling Codex, leave `model` and `preferred` unset and set `reasoningEffort: "high"` plus `sandbox: "read-only"`. This uses GPT-5.6 Sol at high effort and transparently falls back to GPT-5.6 Terra on quota.
67
+
68
+ Prefer an exact `ask-codex` leaf (`mcp__codex__ask-codex` or `mcp__plugin_ask-llm_codex__ask-codex`). If none is exposed, call `mcp__ask-llm__ask-llm` with `provider: "codex"` and the same options. Inspect the unified input schema first: if it lacks `reasoningEffort`, `includeDirs`, `preferred`, or `sandbox`, stop and tell the user to upgrade `@ask-llm/mcp` (`npx -y @ask-llm/mcp@latest` or `npm install -g @ask-llm/mcp`); do not omit those fields to make the call succeed. Never make an unpinned generic `ask-llm` call.
69
+
70
+ Structure the prompt to request confidence scoring AND severity classification:
66
71
 
67
72
  ```
68
73
  Review the following code changes. For each issue found, rate:
@@ -9,6 +9,8 @@ tools:
9
9
  - Grep
10
10
  - Read
11
11
  - mcp__codex__ask-codex
12
+ - mcp__plugin_ask-llm_codex__ask-codex
13
+ - mcp__ask-llm__ask-llm
12
14
  ---
13
15
 
14
16
  <!-- PORTABLE-CONTRACT:START -->
@@ -35,7 +37,7 @@ If the assistant said "I added retry logic to the executor," `codex-reviewer` lo
35
37
 
36
38
  ## Tool surface (read-only by contract)
37
39
 
38
- Allowed: `Read`, `Grep`, `Glob`, `Bash` (read-only commands only — `cat`, `head`, `tail`, `wc`, `diff`, `git diff|log|show|status|blame`, `jq`, language-native test runners in dry-run/list mode), `mcp__codex__ask-codex`.
40
+ Allowed: `Read`, `Grep`, `Glob`, `Bash` (read-only commands only — `cat`, `head`, `tail`, `wc`, `diff`, `git diff|log|show|status|blame`, `jq`, language-native test runners in dry-run/list mode), `mcp__codex__ask-codex`, `mcp__plugin_ask-llm_codex__ask-codex`, `mcp__ask-llm__ask-llm`.
39
41
 
40
42
  Forbidden: anything that mutates state. Never run `rm`, `mv`, `chmod`, redirections (`>`, `>>`, `tee`), package installs (`npm install`, `pip install`), or DB writes (`INSERT`, `UPDATE`, `DELETE`, `DROP`). No `Write`, no `Edit`, no `NotebookEdit`.
41
43
 
@@ -77,9 +79,9 @@ Evidence sources, in order of cost:
77
79
  - `Bash`: `git log --diff-filter=A -- <path>` — confirm a file was added in this branch.
78
80
  - `Bash`: `git diff <ref> -- <path>` — confirm what specifically changed.
79
81
  - `Bash`: `node -e "..."` (dry-run only, no side effects) — evaluate a small expression to confirm a constant value.
80
- - `mcp__codex__ask-codex` — when the verification needs Codex's broader code-tracing (e.g., "does function X actually call function Y in the new path?"), send a focused single-claim prompt.
82
+ - `mcp__codex__ask-codex` or `mcp__plugin_ask-llm_codex__ask-codex` — when the verification needs Codex's broader code-tracing (e.g., "does function X actually call function Y in the new path?"), send a focused single-claim prompt. Prefer any exact `ask-codex` leaf; if only unified `mcp__ask-llm__ask-llm` is exposed, call it with `provider: "codex"`, `reasoningEffort: "high"`, `sandbox: "read-only"`, and `preferred` unset. If the unified schema lacks those Codex options, stop and report that `@ask-llm/mcp` must be upgraded; do not omit the fields.
81
83
 
82
- When you call `mcp__codex__ask-codex` for verification, scope the prompt narrowly:
84
+ When you call an exact `ask-codex` leaf or the fully pinned unified `ask-llm` equivalent for verification, scope the prompt narrowly:
83
85
 
84
86
  ```
85
87
  Verify this single claim against actual source: <claim>.
@@ -29,19 +29,23 @@ You are a code review coordinator for a model-pinned OpenAI GPT-5.6 Sol review.
29
29
 
30
30
  1. Inspect `git diff` and `git diff --cached`. Read each affected file around the changed lines.
31
31
  2. Apply the nearest `CLAUDE.md` instructions and inspect any ADR explicitly cited by changed code.
32
- 3. Call `mcp__codex__ask-codex` with:
32
+ 3. Call an available Ask LLM Codex transport with:
33
33
  - `model: "gpt-5.6-sol"`
34
34
  - `reasoningEffort: "high"`
35
+ - `sandbox: "read-only"`
35
36
  - `preferred` unset
36
37
  - a prompt containing the scoped conventions, relevant ADR summaries, and the diff
37
38
 
38
- The tool name may be plugin-namespaced in some sessions (for example `mcp__plugin_ask-llm_codex__ask-codex`). Treat a tool as authoritative only when its server identity maps to an active `@ask-llm/codex-mcp` registration. If no authoritative `ask-codex` tool is available in this subagent context, pass the same prompt on stdin to the shipped fallback runner:
39
+ Transport ladder (do not skip rungs, and never strip an option to make a call succeed):
40
+ 1. Prefer any exact `ask-codex` leaf whose server identity maps to an active `@ask-llm/codex-mcp` registration (`mcp__codex__ask-codex` or plugin-namespaced `mcp__plugin_ask-llm_codex__ask-codex`).
41
+ 2. Otherwise call `mcp__ask-llm__ask-llm` (or another namespaced `__ask-llm` leaf whose server maps to `@ask-llm/mcp`) with `provider: "codex"` plus the same model, reasoningEffort, sandbox, preferred, and any `includeDirs`. Inspect the advertised input schema first. If it lacks `reasoningEffort`, `includeDirs`, `preferred`, or `sandbox`, stop: the installed `@ask-llm/mcp` is too old to honor Codex options. Upgrade with `npx -y @ask-llm/mcp@latest` or `npm install -g @ask-llm/mcp`; do not omit those fields.
42
+ 3. If no authoritative MCP tool is available in this subagent context, pass the same prompt on stdin to the shipped fallback runner:
39
43
 
40
44
  ```bash
41
45
  node "${CLAUDE_PLUGIN_ROOT}/scripts/sol-review-transport.mjs" --fallback --plugin-dir "${CLAUDE_PLUGIN_ROOT}" --cli-path "$(command -v codex || true)"
42
46
  ```
43
47
 
44
- Before executing Codex, the runner queries the active Claude MCP inventory again with the active plugin preserved; mirror any session-local `--mcp-config`, `--settings`, `--setting-sources`, and `--strict-mcp-config` flags on the runner command. This makes a parent `preferred` result followed by an absent or disconnected subagent tool a registered-but-unavailable state with the corresponding remediation. If the MCP call itself fails at the transport/service boundary, rerun the same fallback command with `--mcp-failed`; do not use that flag for a provider/model response. The runner then executes `codex exec -m gpt-5.6-sol -c model_reasoning_effort="high" -s read-only --ignore-user-config --ignore-rules --skip-git-repo-check`. The model pin, reasoning-effort override, read-only sandbox, and isolation flags are load-bearing; never drop or substitute them. On a quota or rate-limit failure only, the runner retries once with `${ASK_CODEX_FALLBACK_MODEL:-gpt-5.6-terra}` and identical flags, matching the MCP executor's configurable quota ladder. It writes the review result to stdout unchanged so the validated findings can be relayed without loss. If the `codex` CLI is also unavailable, stop and report that the Sol review could not run. Do not review on another transport, on any model outside the Sol-to-fallback ladder, or in another sandbox mode.
48
+ Before executing Codex, the runner queries the active Claude MCP inventory again with the active plugin preserved; mirror any session-local `--mcp-config`, `--settings`, `--setting-sources`, and `--strict-mcp-config` flags on the runner command. When the resolved tool is unified `ask-llm`, pass `--tool "<resolved tool name>" --tool-schema "<advertised input JSON schema>"` so an older schema is classified as `unsupported-schema` rather than being called. This makes a parent `preferred` or `unified` result followed by an absent or disconnected subagent tool a registered-but-unavailable state with the corresponding remediation. If the MCP call itself fails at the transport/service boundary, rerun the same fallback command with `--mcp-failed`; do not use that flag for a provider/model response. The runner then executes `codex exec -m gpt-5.6-sol -c model_reasoning_effort="high" -s read-only --ignore-user-config --ignore-rules --skip-git-repo-check`. The model pin, reasoning-effort override, read-only sandbox, and isolation flags are load-bearing; never drop or substitute them. On a quota or rate-limit failure only, the runner retries once with `${ASK_CODEX_FALLBACK_MODEL:-gpt-5.6-terra}` and identical flags, matching the MCP executor's configurable quota ladder. It writes the review result to stdout unchanged so the validated findings can be relayed without loss. If the `codex` CLI is also unavailable, stop and report that the Sol review could not run. Do not review on another transport, on any model outside the Sol-to-fallback ladder, or in another sandbox mode.
45
49
  4. Ask Sol for concrete correctness, security, data-loss, concurrency, resource-lifecycle, and compatibility failures with confidence scores and reproduction conditions.
46
50
  5. Read the reported source locations and trace each reproduction path. Drop style preferences, speculative improvements, pre-existing issues, linter/type-checker findings, and behavior documented as intentional.
47
51
  6. Report only validated findings with confidence of at least 80/100. Never invent findings to fill a report.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ask-llm/plugin",
3
- "version": "0.16.3",
3
+ "version": "0.17.0",
4
4
  "description": "Ask LLM review, comparison, brainstorming, image, verification, and pairing workflows for Claude Code, Cursor Agent, and Pi",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -83,11 +83,11 @@
83
83
  ]
84
84
  },
85
85
  "dependencies": {
86
- "@ask-llm/antigravity-mcp": "^0.7.5",
86
+ "@ask-llm/antigravity-mcp": "^0.7.6",
87
87
  "@ask-llm/codex-mcp": "^0.7.8",
88
88
  "@ask-llm/gemini-mcp": "^1.9.1",
89
89
  "@ask-llm/grok-mcp": "^0.1.5",
90
- "@ask-llm/mcp": "^0.9.3",
90
+ "@ask-llm/mcp": "^0.10.0",
91
91
  "@ask-llm/ollama-mcp": "^0.6.2"
92
92
  },
93
93
  "peerDependencies": {
@@ -7,9 +7,18 @@ import { prepareCommandInvocation } from "./lib/process.mjs";
7
7
 
8
8
  export const ASK_CODEX_PACKAGE = "@ask-llm/codex-mcp";
9
9
  export const ASK_CODEX_TOOL = "ask-codex";
10
+ export const ASK_LLM_PACKAGE = "@ask-llm/mcp";
11
+ export const ASK_LLM_TOOL = "ask-llm";
12
+ export const UNIFIED_CODEX_OPTION_KEYS = ["reasoningEffort", "includeDirs", "preferred", "sandbox"];
10
13
  export const SOL_MODEL = "gpt-5.6-sol";
11
14
  export const TERRA_MODEL = "gpt-5.6-terra";
12
15
 
16
+ const MISSING_REGISTRATION_REMEDIATION =
17
+ "Run `claude mcp add --scope user ask-llm -- npx -y @ask-llm/mcp`, fully restart Claude Code, then verify with `/mcp`. Split Codex remains an advanced optimization: `claude mcp add --scope user codex -- npx -y @ask-llm/codex-mcp`.";
18
+ const SERVICE_REMEDIATION = "Run `npx -y @ask-llm/mcp doctor`, inspect `/mcp`, then fully restart Claude Code.";
19
+ const UPGRADE_UNIFIED_REMEDIATION =
20
+ "Upgrade `@ask-llm/mcp` with `npx -y @ask-llm/mcp@latest` or `npm install -g @ask-llm/mcp`, fully restart Claude Code, then verify with `/mcp`.";
21
+
13
22
  const scriptPath = fileURLToPath(import.meta.url);
14
23
  const quotaSignals = [
15
24
  "rate_limit_exceeded",
@@ -38,6 +47,29 @@ export function isAskCodexRegistration(server) {
38
47
  );
39
48
  }
40
49
 
50
+ export function isAskLlmToolName(name) {
51
+ return name === ASK_LLM_TOOL || /^mcp__.+__ask-llm$/.test(name);
52
+ }
53
+
54
+ export function isAskLlmRegistration(server) {
55
+ if (!server || typeof server !== "object") return false;
56
+ const command = typeof server.command === "string" ? server.command : "";
57
+ const args = Array.isArray(server.args) ? server.args.filter((arg) => typeof arg === "string") : [];
58
+ const commandLine = typeof server.commandLine === "string" ? server.commandLine : "";
59
+ return (
60
+ /(?:^|[/\\])ask-llm-mcp(?:\.cmd|\.exe)?$/.test(command) ||
61
+ args.includes(ASK_LLM_PACKAGE) ||
62
+ /(?:^|\s)@ask-llm\/mcp(?:@[^\s]+)?(?:\s|$)/.test(commandLine) ||
63
+ /(?:^|[/\\])ask-llm-mcp(?:\.cmd|\.exe)?(?:\s|$)/.test(commandLine)
64
+ );
65
+ }
66
+
67
+ export function unifiedSchemaHonorsCodexOptions(schema) {
68
+ const properties = schema?.properties;
69
+ if (!properties || typeof properties !== "object") return false;
70
+ return UNIFIED_CODEX_OPTION_KEYS.every((key) => Object.hasOwn(properties, key));
71
+ }
72
+
41
73
  export function parseClaudeMcpList(output) {
42
74
  const servers = {};
43
75
  for (const line of output.split(/\r?\n/)) {
@@ -77,8 +109,8 @@ export function readActiveMcpServers({
77
109
  return parseClaudeMcpList(result.stdout || "");
78
110
  }
79
111
 
80
- function expectedToolName(serverName) {
81
- return `mcp__${serverName.replaceAll(":", "_")}__${ASK_CODEX_TOOL}`;
112
+ function expectedToolName(serverName, tool = ASK_CODEX_TOOL) {
113
+ return `mcp__${serverName.replaceAll(":", "_")}__${tool}`;
82
114
  }
83
115
 
84
116
  function isAvailableMcpServer(server) {
@@ -86,12 +118,34 @@ function isAvailableMcpServer(server) {
86
118
  return !status || /^✔\s*Connected\b/i.test(status) || /^cached\b.*\bconnects on first use\b/i.test(status);
87
119
  }
88
120
 
121
+ function withCliFallback({ state, reason, remediation, cliPath }) {
122
+ if (!cliPath) {
123
+ return {
124
+ state,
125
+ transport: null,
126
+ toolName: null,
127
+ diagnostic: `${reason} The explicit CLI fallback is also unavailable.`,
128
+ remediation: `${remediation} Install the fallback with \`npm install -g @openai/codex\` if needed.`,
129
+ fallbackDisclosure: null,
130
+ };
131
+ }
132
+ return {
133
+ state,
134
+ transport: "cli",
135
+ toolName: null,
136
+ diagnostic: reason,
137
+ remediation,
138
+ fallbackDisclosure: `Transport disclosure: ${reason} Running the review through the explicit \`codex exec\` CLI fallback; validated findings will be relayed unchanged.`,
139
+ };
140
+ }
141
+
89
142
  export function classifySolReviewTransport({
90
143
  availableTools = [],
91
144
  mcpServers = {},
92
145
  cliPath = "",
93
146
  inventoryError = null,
94
147
  mcpFailed = false,
148
+ toolSchemas = {},
95
149
  }) {
96
150
  if (inventoryError) {
97
151
  const reason = `Ask LLM Codex MCP availability could not be determined because the active Claude MCP inventory could not be inspected: ${inventoryError}`;
@@ -116,28 +170,60 @@ export function classifySolReviewTransport({
116
170
  };
117
171
  }
118
172
 
119
- const registrations = Object.entries(mcpServers).filter(([, server]) => isAskCodexRegistration(server));
120
- const availableRegistrations = registrations.filter(([, server]) => isAvailableMcpServer(server));
121
- const registeredToolNames = new Set(availableRegistrations.map(([name]) => expectedToolName(name)));
122
- const toolName = availableTools.find((name) => isAskCodexToolName(name) && registeredToolNames.has(name));
123
- if (toolName && !mcpFailed) {
173
+ const codexRegistrations = Object.entries(mcpServers).filter(([, server]) => isAskCodexRegistration(server));
174
+ const availableCodexRegistrations = codexRegistrations.filter(([, server]) => isAvailableMcpServer(server));
175
+ const registeredCodexToolNames = new Set(
176
+ availableCodexRegistrations.map(([name]) => expectedToolName(name, ASK_CODEX_TOOL)),
177
+ );
178
+ const askCodexToolName = availableTools.find(
179
+ (name) => isAskCodexToolName(name) && registeredCodexToolNames.has(name),
180
+ );
181
+ if (askCodexToolName && !mcpFailed) {
124
182
  return {
125
183
  state: "preferred",
126
184
  transport: "mcp",
127
- toolName,
128
- diagnostic: `Ask LLM Codex transport available as ${toolName}.`,
185
+ toolName: askCodexToolName,
186
+ diagnostic: `Ask LLM Codex transport available as ${askCodexToolName}.`,
187
+ remediation: null,
188
+ fallbackDisclosure: null,
189
+ };
190
+ }
191
+
192
+ const unifiedRegistrations = Object.entries(mcpServers).filter(([, server]) => isAskLlmRegistration(server));
193
+ const availableUnifiedRegistrations = unifiedRegistrations.filter(([, server]) => isAvailableMcpServer(server));
194
+ const registeredUnifiedToolNames = new Set(
195
+ availableUnifiedRegistrations.map(([name]) => expectedToolName(name, ASK_LLM_TOOL)),
196
+ );
197
+ const askLlmToolName = availableTools.find((name) => isAskLlmToolName(name) && registeredUnifiedToolNames.has(name));
198
+ if (askLlmToolName && !mcpFailed) {
199
+ const schema = toolSchemas[askLlmToolName];
200
+ if (!unifiedSchemaHonorsCodexOptions(schema)) {
201
+ const schemaMissing = !schema;
202
+ return withCliFallback({
203
+ state: "unsupported-schema",
204
+ reason: schemaMissing
205
+ ? "Ask LLM unified MCP is registered, but its `ask-llm` schema was not provided so Codex options (reasoningEffort, includeDirs, preferred, sandbox) cannot be verified. Pass `--tool-schema` with the advertised input JSON schema; never omit those fields to make the call succeed."
206
+ : "Ask LLM unified MCP is registered, but its `ask-llm` schema cannot honor Codex options (reasoningEffort, includeDirs, preferred, sandbox). Upgrade `@ask-llm/mcp`; never omit those fields to make the call succeed.",
207
+ remediation: UPGRADE_UNIFIED_REMEDIATION,
208
+ cliPath,
209
+ });
210
+ }
211
+ return {
212
+ state: "unified",
213
+ transport: "mcp",
214
+ toolName: askLlmToolName,
215
+ diagnostic: `Ask LLM unified transport available as ${askLlmToolName}. Call it with provider "codex" and pass reasoningEffort, includeDirs, preferred, and sandbox; do not strip unsupported fields.`,
129
216
  remediation: null,
130
217
  fallbackDisclosure: null,
131
218
  };
132
219
  }
133
220
 
134
- const registered = registrations.length > 0;
221
+ const registered = codexRegistrations.length > 0 || unifiedRegistrations.length > 0;
135
222
  const state = registered || mcpFailed ? "unavailable" : "missing-registration";
136
- const remediation =
137
- registered || mcpFailed
138
- ? "Run `npx -y @ask-llm/mcp doctor`, inspect `/mcp`, then fully restart Claude Code."
139
- : "Run `claude mcp add --scope user codex -- npx -y @ask-llm/codex-mcp`, fully restart Claude Code, then verify with `/mcp`.";
140
- const unavailableRegistration = registrations.find(([, server]) => !isAvailableMcpServer(server));
223
+ const remediation = registered || mcpFailed ? SERVICE_REMEDIATION : MISSING_REGISTRATION_REMEDIATION;
224
+ const unavailableRegistration =
225
+ codexRegistrations.find(([, server]) => !isAvailableMcpServer(server)) ||
226
+ unifiedRegistrations.find(([, server]) => !isAvailableMcpServer(server));
141
227
  const reason = mcpFailed
142
228
  ? "Ask LLM Codex MCP invocation failed in this session, so the preferred transport is unavailable."
143
229
  : unavailableRegistration
@@ -146,25 +232,7 @@ export function classifySolReviewTransport({
146
232
  ? "Ask LLM Codex MCP is registered, but its `ask-codex` tool is unavailable in this session."
147
233
  : "Ask LLM Codex MCP registration is missing from this Claude Code installation.";
148
234
 
149
- if (!cliPath) {
150
- return {
151
- state,
152
- transport: null,
153
- toolName: null,
154
- diagnostic: `${reason} The explicit CLI fallback is also unavailable.`,
155
- remediation: `${remediation} Install the fallback with \`npm install -g @openai/codex\` if needed.`,
156
- fallbackDisclosure: null,
157
- };
158
- }
159
-
160
- return {
161
- state,
162
- transport: "cli",
163
- toolName: null,
164
- diagnostic: reason,
165
- remediation,
166
- fallbackDisclosure: `Transport disclosure: ${reason} Running the review through the explicit \`codex exec\` CLI fallback; validated findings will be relayed unchanged.`,
167
- };
235
+ return withCliFallback({ state, reason, remediation, cliPath });
168
236
  }
169
237
 
170
238
  export function codexFallbackArgs(model) {
@@ -255,6 +323,7 @@ function parseArgs(args) {
255
323
  mcpList: null,
256
324
  fallback: false,
257
325
  mcpFailed: false,
326
+ toolSchema: null,
258
327
  claudeContextArgs: [],
259
328
  };
260
329
  const claudeContextValueFlags = new Set(["--plugin-dir", "--mcp-config", "--settings", "--setting-sources"]);
@@ -265,7 +334,14 @@ function parseArgs(args) {
265
334
  else if (arg === "--tool") parsed.tools.push(args[++index] ?? "");
266
335
  else if (arg === "--cli-path") parsed.cliPath = args[++index] ?? "";
267
336
  else if (arg === "--mcp-list") parsed.mcpList = args[++index] ?? "";
268
- else if (arg === "--strict-mcp-config") parsed.claudeContextArgs.push(arg);
337
+ else if (arg === "--tool-schema") {
338
+ const raw = args[++index] ?? "";
339
+ try {
340
+ parsed.toolSchema = JSON.parse(raw);
341
+ } catch {
342
+ throw new Error("Invalid --tool-schema JSON");
343
+ }
344
+ } else if (arg === "--strict-mcp-config") parsed.claudeContextArgs.push(arg);
269
345
  else if (claudeContextValueFlags.has(arg)) {
270
346
  const value = args[++index];
271
347
  if (value === undefined) throw new Error(`Missing value for ${arg}`);
@@ -295,12 +371,17 @@ function readMcpServers(parsed) {
295
371
  async function main() {
296
372
  const parsed = parseArgs(process.argv.slice(2));
297
373
  const { mcpServers, inventoryError } = readMcpServers(parsed);
374
+ const toolSchemas = {};
375
+ if (parsed.toolSchema) {
376
+ for (const name of parsed.tools) toolSchemas[name] = parsed.toolSchema;
377
+ }
298
378
  const decision = classifySolReviewTransport({
299
379
  availableTools: parsed.tools,
300
380
  mcpServers,
301
381
  cliPath: parsed.cliPath,
302
382
  inventoryError,
303
383
  mcpFailed: parsed.mcpFailed,
384
+ toolSchemas,
304
385
  });
305
386
 
306
387
  if (parsed.fallback) {
@@ -38,7 +38,7 @@ Codex's `image_generation` tool selects the image model server-side; as of 2026-
38
38
 
39
39
  - `codex-cli` >= 0.125.0 installed and authenticated
40
40
  - `image_generation` feature flag enabled (default: stable + true). Verify with `codex features list | grep image_generation`
41
- - The `ask-codex` MCP tool available (from `@ask-llm/codex-mcp` or the `@ask-llm/mcp` orchestrator)
41
+ - The `ask-codex` MCP tool available (from `@ask-llm/codex-mcp`) or the unified `mcp__ask-llm__ask-llm` tool (from `@ask-llm/mcp`) whose schema includes `sandbox`
42
42
 
43
43
  ## Instructions
44
44
 
@@ -65,9 +65,9 @@ Example slug derivation:
65
65
  - "Generate an image of a dark terminal with two reviewers" → `dark-terminal-with-two-reviewers.png`
66
66
  - "Make me a cat picture" → `cat-picture.png`
67
67
 
68
- ### Phase 3: Dispatch to ask-codex
68
+ ### Phase 3: Dispatch to Codex
69
69
 
70
- Call the `ask-codex` MCP tool (NOT raw `codex exec` — that bypasses ADR-044 quota fallback, ADR-042 stdin handling, and ADR-047 PATH resolution). Use this prompt template:
70
+ Call an exact `ask-codex` leaf when exposed (NOT raw `codex exec` — that bypasses ADR-044 quota fallback, ADR-042 stdin handling, and ADR-047 PATH resolution). If only unified `mcp__ask-llm__ask-llm` is exposed, use that instead with `provider: "codex"`. If the unified schema lacks `sandbox`, stop and tell the user to upgrade `@ask-llm/mcp` (`npx -y @ask-llm/mcp@latest` or `npm install -g @ask-llm/mcp`); do not omit `sandbox` to make the call succeed. Use this prompt template:
71
71
 
72
72
  ```
73
73
  Use your image_generation tool to create the following image and save it as a PNG file.
@@ -80,7 +80,7 @@ Save the file to this absolute path: <path from Phase 2>
80
80
  After saving, confirm the absolute path of the created file and its byte size in your reply. If image_generation fails or the file cannot be written, explain what went wrong and do not invent a fake path.
81
81
  ```
82
82
 
83
- **Sandbox:** pass `sandbox: "workspace-write"` on this `ask-codex` call. `ask-codex` defaults to the read-only review sandbox (ADR-136), under which Codex cannot write the PNG to disk; image generation is the sanctioned exception that needs Codex to write the output file itself.
83
+ **Sandbox:** pass `sandbox: "workspace-write"` on this `ask-codex` or fully pinned `ask-llm({ provider: "codex", ... })` call. Codex defaults to the read-only review sandbox (ADR-136), under which it cannot write the PNG to disk; image generation is the sanctioned exception that needs Codex to write the output file itself. The unified server will not silently strip `sandbox`; an older `@ask-llm/mcp` that cannot honor it must fail closed.
84
84
 
85
85
  **Default model:** let `ask-codex` use its default (`gpt-5.6-sol`). The image_generation tool is invoked by the model regardless of which Codex chat model is selected — model selection here is about the orchestrating agent, not the image model itself.
86
86
 
@@ -19,7 +19,7 @@ Pi requires project trust, this repository marker, and explicit user-owned allow
19
19
 
20
20
  Cursor discovers this `SKILL.md` through its supported Agent Skills surface; `/codex-pair` attaches it as an explicit command. Do **not** use Claude Code's `PostToolUse`, `Stop`, `SessionStart`, `SessionEnd`, `CLAUDE_PLUGIN_ROOT`, `AskUserQuestion`, or plugin tool namespaces. This adapter is an on-demand iterative pairing session, not a claim that Claude hooks were registered in Cursor.
21
21
 
22
- 1. Read `../pairing-contract.md`. The Cursor plugin bundle registers only the unified `ask-llm` server; if the user separately installed `@ask-llm/codex-mcp`, prefer that deterministic `ask-codex` leaf. Resolve an exposed MCP tool whose exact leaf is `ask-codex`; do not assume its server prefix. When only the unified `ask-llm` leaf is registered, it may serve as the transport with `provider: "codex"` and every option (model, reasoning effort, include directories, session) pinned explicitly; its schema rejects unsupported combinations instead of stripping them and Codex runs read-only by default. Never make an unpinned generic call. If neither tool is exposed, stop with the recommended user-installed unified server:
22
+ 1. Read `../pairing-contract.md`. The Cursor plugin bundle registers only the unified `ask-llm` server; if the user separately installed `@ask-llm/codex-mcp`, prefer that deterministic `ask-codex` leaf. Resolve an exposed MCP tool whose exact leaf is `ask-codex`; do not assume its server prefix. When only the unified `ask-llm` leaf is registered, it may serve as the transport with `provider: "codex"` and every option (model, reasoning effort, include directories, sandbox, session) pinned explicitly; its schema rejects unsupported combinations instead of stripping them. Inspect the advertised schema before the first call: if `sandbox` or another required Codex option is missing, stop and upgrade `@ask-llm/mcp` rather than omitting the field. Never make an unpinned generic call. If neither tool is exposed, stop with the recommended user-installed unified server:
23
23
  ```json
24
24
  {"mcpServers":{"ask-llm":{"command":"npx","args":["-y","@ask-llm/mcp"]}}}
25
25
  ```
@@ -48,13 +48,14 @@ Cursor discovers this `SKILL.md` through its supported Agent Skills surface; `/c
48
48
  "model": "<required exact ID>",
49
49
  "reasoningEffort": "<required effort>",
50
50
  "includeDirs": ["<validated relative directory>"],
51
- "sessionId": ""
51
+ "sessionId": "",
52
+ "sandbox": "read-only"
52
53
  }
53
54
  }
54
55
  ]
55
56
  ```
56
57
  The prompt assigns Codex the independent reviewer role and requests actionable severity/file/line evidence. Capture the returned structured `sessionId`/Thread ID and actual model. Relay feedback before changing code; verify every finding against source and label it accepted, rejected, or deferred.
57
- 5. At meaningful checkpoints, call the same tool with the captured `sessionId`, same model/effort, and bounded delta. On `ask-codex`, keep `sandbox: "read-only"`; unified `ask-llm` has no sandbox input and is read-only by default, so do not send an unsupported sandbox field. Omit `includeDirs` on resumed calls because `codex exec resume` does not support them (every Codex transport — split `ask-codex`, unified `ask-llm`, and Pi — rejects that combination at the shared executor instead of dropping the directories); never silently strip them from the first call. If no session ID was returned, stop with a session diagnostic instead of pretending continuity.
58
+ 5. At meaningful checkpoints, call the same tool with the captured `sessionId`, same model/effort, and bounded delta. Keep `sandbox: "read-only"` on both `ask-codex` and unified `ask-llm`. If the unified schema lacks `sandbox`, stop and upgrade `@ask-llm/mcp` rather than omitting it. Omit `includeDirs` on resumed calls because `codex exec resume` does not support them (every Codex transport — split `ask-codex`, unified `ask-llm`, and Pi — rejects that combination at the shared executor instead of dropping the directories); never silently strip them from the first call. If no session ID was returned, stop with a session diagnostic instead of pretending continuity.
58
59
  6. A Cursor interrupt cancels the MCP request. Report `cancelled` and never retry another tool/model/provider. Preserve earlier feedback on later failure and report `failed (partial)`. On success report `completed` with host, provider, requested/actual model, effort, session reuse count, context/include directories, accepted/rejected/deferred actions, and any reported Codex quota fallback. Never conceal fallback or rewrite a model.
59
60
 
60
61
  <!-- HOST-ADAPTER:CLAUDE-CODE:START -->
@@ -20,7 +20,7 @@ Additional directories must be explicit relative workspace paths: no absolute pa
20
20
 
21
21
  ## Transport, options, and attribution
22
22
 
23
- Select one route before the first call and keep it immutable for the run. Resolve tools by their exact leaf capability (`ask-codex`, `ask-grok`, or `ask-cursor-agent`), not by assuming a Claude plugin namespace. The unified `ask-llm` tool is an acceptable transport only when the call pins provider, harness where applicable, exact model, reasoning effort, include directories, and session explicitly and its schema rejects unsupported combinations rather than stripping them; it is never an unpinned generic call and never a fallback for a failed split tool. A missing tool is a setup failure, never permission to use a generic call.
23
+ Select one route before the first call and keep it immutable for the run. Resolve tools by their exact leaf capability (`ask-codex`, `ask-grok`, or `ask-cursor-agent`), not by assuming a Claude plugin namespace. The unified `ask-llm` tool is an acceptable transport only when the call pins provider, harness where applicable, exact model, reasoning effort, include directories, sandbox where the route supports it, and session explicitly and its schema rejects unsupported combinations rather than stripping them; it is never an unpinned generic call and never a fallback for a failed split tool. If the advertised schema lacks a required Codex option (`reasoningEffort`, `includeDirs`, `preferred`, `sandbox`), stop and upgrade `@ask-llm/mcp` rather than omitting the field. A missing tool is a setup failure, never permission to use a generic call.
24
24
 
25
25
  Every call and final report must keep these separate:
26
26
 
@@ -33,13 +33,13 @@ Run a read-only, precision-first review explicitly pinned to GPT-5.6 Sol at high
33
33
  3. If the combined diff is empty, report that there are no changes to review.
34
34
  4. Read the root and file-scoped `CLAUDE.md` files plus any ADRs cited by changed code.
35
35
  5. Preflight the transport through the shipped executable contract:
36
- - Search the current tool surface for the exact `ask-codex` leaf tool. The executable correlates its client-assigned server prefix with an active `@ask-llm/codex-mcp` registration; similarly named tools and tools from unrelated servers are not authoritative.
37
- - Run `node "${CLAUDE_PLUGIN_ROOT}/scripts/sol-review-transport.mjs" --plugin-dir "${CLAUDE_PLUGIN_ROOT}" --cli-path "$(command -v codex || true)"`, adding `--tool "<resolved tool name>"` only when the exact `ask-codex` tool resolved. Mirror any session-local `--mcp-config`, `--settings`, `--setting-sources`, and `--strict-mcp-config` flags so the nested inventory sees the same configuration as the active session.
38
- - Preserve the returned `state`, `diagnostic`, `remediation`, and `fallbackDisclosure`. The executable reads `claude mcp list` with that session context: `missing-registration` means that active inventory has no supported registration; `unavailable` means it contains the registration but the current tool surface does not expose a usable tool, reports failed health, or the MCP invocation failed; `inventory-unavailable` means the active inventory could not be inspected, so registration and service health are unknown. Do not collapse those states into a generic "no MCP" message. If `transport` is null, stop and show the executable remediation instead of launching the agent.
36
+ - Search the current tool surface for an exact `ask-codex` leaf first, then an `ask-llm` leaf. The executable correlates each client-assigned server prefix with an active `@ask-llm/codex-mcp` or `@ask-llm/mcp` registration; similarly named tools and tools from unrelated servers are not authoritative.
37
+ - Run `node "${CLAUDE_PLUGIN_ROOT}/scripts/sol-review-transport.mjs" --plugin-dir "${CLAUDE_PLUGIN_ROOT}" --cli-path "$(command -v codex || true)"`, adding `--tool "<resolved tool name>"` when an exact `ask-codex` or `ask-llm` tool resolved. For unified `ask-llm`, also pass `--tool-schema "<advertised input JSON schema>"` so a missing `reasoningEffort`/`includeDirs`/`preferred`/`sandbox` property is `unsupported-schema`, not a silent strip. Mirror any session-local `--mcp-config`, `--settings`, `--setting-sources`, and `--strict-mcp-config` flags so the nested inventory sees the same configuration as the active session.
38
+ - Preserve the returned `state`, `diagnostic`, `remediation`, and `fallbackDisclosure`. The executable reads `claude mcp list` with that session context: `preferred` is a usable `ask-codex` leaf; `unified` is a usable `mcp__ask-llm__ask-llm` (or namespaced) leaf whose advertised schema honors Codex options and that must be called with `provider: "codex"` and the complete Codex option set; `missing-registration` means that active inventory has no supported registration; `unavailable` means it contains the registration but the current tool surface does not expose a usable tool, reports failed health, or the MCP invocation failed; `unsupported-schema` means unified MCP is present but its schema was not supplied or cannot honor Codex options — pass `--tool-schema` or upgrade `@ask-llm/mcp` rather than omitting fields; `inventory-unavailable` means the active inventory could not be inspected, so registration and service health are unknown. Do not collapse those states into a generic "no MCP" message. If `transport` is null, stop and show the executable remediation instead of launching the agent.
39
39
  - Parent availability remains advisory because subagents do not always inherit the session's MCP servers. The reviewer's fallback runner re-reads the active inventory and reclassifies the absent subagent tool before executing the CLI fallback.
40
40
  6. Launch the `sol-reviewer` agent with the diff and a compact context brief containing the changed files, applicable conventions, referenced ADRs, the user's requested review focus, and the complete preflight result.
41
41
  7. Return the agent's validated findings without adding unverified issues.
42
42
 
43
- The reviewer must call `ask-codex` with `model: "gpt-5.6-sol"` and `reasoningEffort: "high"`, or use the shipped CLI fallback runner when no usable `ask-codex` MCP tool is available or its invocation fails at the transport/service boundary. That runner executes the sanctioned `codex exec -m gpt-5.6-sol -c model_reasoning_effort="high" -s read-only --ignore-user-config --ignore-rules --skip-git-repo-check` contract and relays its result unchanged. This explicit pin distinguishes `/sol-review` from `/codex-review`, which follows the configured Codex default. Both fallback kinds must be disclosed in the report: a Terra quota fallback means the requested Sol review did not complete on Sol, and a CLI transport fallback must report missing registration, registered-service unavailability, or an unreadable inventory without claiming a state that could not be determined.
43
+ The reviewer must call `ask-codex` with `model: "gpt-5.6-sol"`, `reasoningEffort: "high"`, and `sandbox: "read-only"`, or the unified `ask-llm` equivalent with `provider: "codex"` and those same fields, or use the shipped CLI fallback runner when no usable MCP tool is available, its schema cannot honor those options, or its invocation fails at the transport/service boundary. That runner executes the sanctioned `codex exec -m gpt-5.6-sol -c model_reasoning_effort="high" -s read-only --ignore-user-config --ignore-rules --skip-git-repo-check` contract and relays its result unchanged. This explicit pin distinguishes `/sol-review` from `/codex-review`, which follows the configured Codex default. Both fallback kinds must be disclosed in the report: a Terra quota fallback means the requested Sol review did not complete on Sol, and a CLI transport fallback must report missing registration, registered-service unavailability, an unsupported unified schema, or an unreadable inventory without claiming a state that could not be determined.
44
44
 
45
45
  <!-- HOST-ADAPTER:CLAUDE-CODE:END -->