<!-- AUTO-GENERATED by scripts/gen-adapters.js - DO NOT EDIT -->
---
name: consult
description: "Cross-tool AI consultation. Use when user asks to 'consult gemini', 'ask codex', 'get second opinion', 'cross-check with claude', 'consult another AI', 'ask opencode', 'copilot opinion', or wants a second opinion from a different AI tool."
version: 4.2.2
argument-hint: "[question] [--tool] [--effort] [--model] [--context] [--continue]"
---

> **OpenCode Note**: Invoke agents using `@agent-name` syntax.
> Available agents: task-discoverer, exploration-agent, planning-agent,
> implementation-agent, deslop-agent, delivery-validator, sync-docs-agent, consult-agent
> Example: `@exploration-agent analyze the codebase`


# consult

Cross-tool AI consultation: query another AI CLI tool and return the response.

## When to Use

Invoke this skill when:
- User wants a second opinion from a different AI tool
- User asks to consult, ask, or cross-check with gemini/codex/claude/opencode/copilot
- User needs to compare responses across AI tools
- User wants to validate a decision with an external AI

## Arguments

Parse from `$ARGUMENTS`:

| Flag | Values | Default | Description |
|------|--------|---------|-------------|
| `--tool` | gemini, codex, claude, opencode, copilot | (picker) | Target tool |
| `--effort` | low, medium, high, max | medium | Thinking effort level |
| `--model` | any model name | (from effort) | Override model selection |
| `--context` | diff, file=PATH, none | none | Auto-include context |
| `--continue` | (flag) or SESSION_ID | false | Resume previous session |

Question text is everything in `$ARGUMENTS` except the flags above.

## Provider Configurations

### Claude

```
Command: claude -p "QUESTION" --output-format json --model "MODEL" --max-turns TURNS --allowedTools "Read,Glob,Grep"
Session resume: --resume "SESSION_ID"
```

Models: haiku, sonnet, opus

| Effort | Model | Max Turns |
|--------|-------|-----------|
| low | haiku | 1 |
| medium | sonnet | 3 |
| high | opus | 5 |
| max | opus | 10 |

**Parse output**: `JSON.parse(stdout).result`
**Session ID**: `JSON.parse(stdout).session_id`
**Continuable**: Yes

### Gemini

```
Command: gemini -p "QUESTION" --output-format json -m "MODEL"
Session resume: --resume "SESSION_ID"
```

Models: gemini-2.5-flash, gemini-2.5-pro, gemini-3-flash-preview, gemini-3-pro-preview

| Effort | Model |
|--------|-------|
| low | gemini-2.5-flash |
| medium | gemini-2.5-pro |
| high | gemini-3-flash-preview |
| max | gemini-3-pro-preview |

**Parse output**: `JSON.parse(stdout).response`
**Continuable**: Yes (via `--resume`)

### Codex

```
Command: codex -q "QUESTION" --json -m "MODEL" -a suggest -c model_reasoning_effort="LEVEL"
```

Models: gpt-5.1-codex-mini, gpt-5-codex, gpt-5.1-codex, gpt-5.2-codex, gpt-5.3-codex, gpt-5.1-codex-max

| Effort | Model | Reasoning |
|--------|-------|-----------|
| low | gpt-5.1-codex-mini | low |
| medium | gpt-5.2-codex | medium |
| high | gpt-5.3-codex | high |
| max | gpt-5.3-codex | xhigh |

**Parse output**: `JSON.parse(stdout).message` or raw text
**Continuable**: No

### OpenCode

```
Command: opencode run "QUESTION" --format json --model "MODEL" --variant "VARIANT"
With thinking: add --thinking flag
```

Models: 75+ via providers (format: provider/model). Top picks: claude-sonnet-4-5, claude-opus-4-5, gpt-5.2, gpt-5.1-codex, gemini-3-pro, minimax-m2.1

| Effort | Model | Variant |
|--------|-------|---------|
| low | (user-selected or default) | low |
| medium | (user-selected or default) | medium |
| high | (user-selected or default) | high |
| max | (user-selected or default) | high + --thinking |

**Parse output**: Parse JSON events from stdout, extract final text response
**Continuable**: No

### Copilot

```
Command: copilot -p "QUESTION"
```

Models: claude-sonnet-4-5 (default), claude-opus-4-6, claude-haiku-4-5, claude-sonnet-4, gpt-5

| Effort | Notes |
|--------|-------|
| all | No effort control available. Model selectable via --model flag. |

**Parse output**: Raw text from stdout
**Continuable**: No

## Input Validation

Before building commands, validate all user-provided arguments:

- **--tool**: MUST be one of: gemini, codex, claude, opencode, copilot. Reject all other values.
- **--effort**: MUST be one of: low, medium, high, max. Default to medium.
- **--model**: Allow any string, but quote it in the command.
- **--context=file=PATH**: MUST resolve within the project directory. Reject absolute paths outside cwd. Additional checks:
  1. **Block UNC paths** (Windows): Reject paths starting with `\\` or `//` (network shares)
  2. **Resolve canonical path**: Use the Read tool to read the file (do NOT use shell commands). Before reading, resolve the path: join `cwd + PATH`, then normalize (collapse `.`, `..`, resolve symlinks)
  3. **Verify containment**: The resolved canonical path MUST start with the current working directory. If it escapes (via `..`, symlinks, or junction points), reject with: `[ERROR] Path escapes project directory: {PATH}`
  4. **No shell access**: Read file content using the Read tool only. Never pass user-provided paths to shell commands (prevents injection via path values)

## Command Building

Given the parsed arguments, build the complete CLI command. All user-provided values MUST be quoted in the shell command to prevent injection.

### Step 1: Resolve Model

If `--model` is specified, use it directly. Otherwise, use the effort-based model from the provider table above.

### Step 2: Build Command String

Use the command template from the provider's configuration section. Substitute QUESTION, MODEL, TURNS, LEVEL, and VARIANT with resolved values.

If continuing a session (Claude or Gemini): append `--resume SESSION_ID`.
If OpenCode at max effort: append `--thinking`.

### Step 3: Context Packaging

If `--context=diff`: Run `git diff 2>/dev/null` and prepend output to the question.
If `--context=file=PATH`: Read the file using the Read tool and prepend its content to the question.

### Step 4: Safe Question Passing

User-provided question text MUST NOT be interpolated into shell command strings. Shell escaping is insufficient -- `$()`, backticks, and other expansion sequences can execute arbitrary commands even inside double quotes.

**Required approach -- pass question via stdin or temp file:**

1. **Write the question** to a temporary file using the Write tool (e.g., `{AI_STATE_DIR}/consult/question.tmp`)

   Platform state directory:
   - Claude Code: `.opencode/`
   - OpenCode: `.opencode/`
   - Codex CLI: `.codex/`
2. **Build the command** using the temp file as input instead of inline text:

| Provider | Safe command pattern |
|----------|---------------------|
| Claude | `claude -p - --output-format json --model "MODEL" --max-turns TURNS --allowedTools "Read,Glob,Grep" < "{AI_STATE_DIR}/consult/question.tmp"` |
| Gemini | `gemini -p - --output-format json -m "MODEL" < "{AI_STATE_DIR}/consult/question.tmp"` |
| Codex | `codex -q "$(cat "{AI_STATE_DIR}/consult/question.tmp")" --json -m "MODEL" -a suggest` (Codex lacks stdin mode -- cat reads from platform-controlled path, not user input) |
| OpenCode | `opencode run - --format json --model "MODEL" --variant "VARIANT" < "{AI_STATE_DIR}/consult/question.tmp"` |
| Copilot | `copilot -p - < "{AI_STATE_DIR}/consult/question.tmp"` |

3. **Delete the temp file** after the command completes (success or failure). Always clean up to prevent accumulation.

**Model and session ID values** are controlled strings (from pickers or saved state) and safe to quote directly in the command. Only the question contains arbitrary user text and requires the temp file approach. The temp file path (`{AI_STATE_DIR}/consult/question.tmp`) uses a platform-controlled directory and fixed filename -- no user input in the path.

## Provider Detection

Cross-platform tool detection:

- **Windows**: `where.exe TOOL 2>nul` -- returns 0 if found
- **Unix**: `which TOOL 2>/dev/null` -- returns 0 if found

Check each tool (claude, gemini, codex, opencode, copilot) and return only the available ones.

## Session Management

### Save Session

After successful consultation, save to `{AI_STATE_DIR}/consult/last-session.json`:

```json
{
  "tool": "claude",
  "model": "opus",
  "effort": "high",
  "session_id": "abc-123-def-456",
  "timestamp": "2026-02-10T12:00:00Z",
  "question": "original question text",
  "continuable": true
}
```

`AI_STATE_DIR` uses the platform state directory:
- Claude Code: `.opencode/`
- OpenCode: `.opencode/`
- Codex CLI: `.codex/`

### Load Session

For `--continue`, read the session file and restore:
- tool (from saved state)
- session_id (for --resume flag)
- model (reuse same model)

If session file not found, warn and proceed as fresh consultation.

## Output Sanitization

Before returning the response, the invoking agent MUST scan for and redact API keys, tokens, and credentials that may appear in the consulted tool's output. At minimum, redact these patterns:

- `sk-[a-zA-Z0-9_-]{20,}` and `sk-ant-*`, `sk-proj-*` (API keys)
- `AIza[a-zA-Z0-9_-]{30,}` (Google API keys)
- `AKIA*`, `ASIA*` (AWS access/session keys)
- `ghp_*`, `gho_*`, `github_pat_*` (GitHub tokens)
- `ANTHROPIC_API_KEY=*`, `OPENAI_API_KEY=*`, `GOOGLE_API_KEY=*`, `GEMINI_API_KEY=*` (env assignments)
- `Bearer [a-zA-Z0-9_-]{20,}` (auth headers)

See `consult-agent.md` for the complete redaction pattern table with replacements.

## Output Format

Return a plain JSON object to stdout (no markers or wrappers):

```json
{
  "tool": "gemini",
  "model": "gemini-3-pro-preview",
  "effort": "high",
  "duration_ms": 12300,
  "response": "The AI's response text here...",
  "session_id": "abc-123",
  "continuable": true
}
```

## Install Instructions

When a tool is not found, return these install commands:

| Tool | Install |
|------|---------|
| Claude | `npm install -g @anthropic-ai/claude-code` |
| Gemini | See https://gemini.google.com/cli for install instructions |
| Codex | `npm install -g @openai/codex` |
| OpenCode | `npm install -g opencode-ai` or `brew install anomalyco/tap/opencode` |
| Copilot | `gh extension install github/copilot-cli` |

## Error Handling

| Error | Response |
|-------|----------|
| Tool not installed | Return install instructions from table above |
| Tool execution timeout | Return `"response": "Timeout after 120s"` |
| JSON parse error | Return raw text as response |
| Empty output | Return `"response": "No output received"` |
| Session file missing | Proceed without session resume |
| API key missing | Return tool-specific env var instructions |

## Integration

This skill is invoked by:
- `consult-agent` for `/consult` command
- Direct invocation: `Skill('consult', '"question" --tool=gemini --effort=high')`

Example: `Skill('consult', '"Is this approach correct?" --tool=gemini --effort=high --model=gemini-3-pro')`
