Full Code of MadAppGang/claudish for AI

main a9441999085f cached
352 files
4.4 MB
1.2M tokens
1864 symbols
1 requests
Download .txt
Showing preview only (4,729K chars total). Download the full file or copy to clipboard to get everything.
Repository: MadAppGang/claudish
Branch: main
Commit: a9441999085f
Files: 352
Total size: 4.4 MB

Directory structure:
gitextract_7tdmfy1z/

├── .github/
│   ├── ISSUE_TRIAGE.md
│   ├── prompts/
│   │   ├── issue-comment-system.md
│   │   └── issue-triage-system.md
│   ├── release.yml
│   └── workflows/
│       ├── claude-code.yml
│       ├── issue-triage.yml
│       ├── release.yml
│       └── smoke-test.yml
├── .gitignore
├── AI_AGENT_GUIDE.md
├── CHANGELOG.md
├── CLAUDE.md
├── README.md
├── apps/
│   ├── .gitignore
│   └── ClaudishProxy/
│       ├── Package.swift
│       └── Sources/
│           ├── ApiKeyManager.swift
│           ├── BridgeManager.swift
│           ├── CertificateManager.swift
│           ├── ClaudishProxyApp.swift
│           ├── ModelProvider.swift
│           ├── Models.swift
│           ├── ProcessManager.swift
│           ├── ProfileManager.swift
│           ├── ProfilePicker.swift
│           ├── ProfilesSettingsView.swift
│           ├── SettingsView.swift
│           ├── StatsDatabase.swift
│           ├── StatsPanel.swift
│           ├── Theme.swift
│           └── UnifiedModelPicker.swift
├── biome.json
├── cliff.toml
├── design-references/
│   └── stats-panel-style.md
├── docs/
│   ├── advanced/
│   │   ├── automation.md
│   │   ├── cost-tracking.md
│   │   ├── environment.md
│   │   └── mtm-to-magmux-migration.md
│   ├── ai-integration/
│   │   └── for-agents.md
│   ├── api-key-architecture.md
│   ├── api-reference.md
│   ├── getting-started/
│   │   └── quick-start.md
│   ├── index.md
│   ├── models/
│   │   ├── choosing-models.md
│   │   └── model-mapping.md
│   ├── settings-reference.md
│   ├── three-layer-architecture.md
│   ├── troubleshooting.md
│   └── usage/
│       ├── interactive-mode.md
│       ├── magmux.md
│       ├── mcp-server.md
│       ├── monitor-mode.md
│       └── single-shot-mode.md
├── experiments/
│   └── tool-replacement-proxy-2026-04/
│       ├── README.md
│       ├── claudish-patch/
│       │   ├── native-handler-advisor.test.ts
│       │   ├── native-handler-advisor.ts
│       │   └── native-handler.patch
│       ├── evidence/
│       │   ├── evidence-index.ndjson
│       │   ├── evidence-req-advisor-enabled.json
│       │   ├── evidence-resp-advisor-enabled.ndjson
│       │   ├── evidence-stage1-swap.ndjson
│       │   ├── evidence-stage2-rewrite.ndjson
│       │   └── evidence-stage2-ui-transcript.txt
│       ├── journal/
│       │   └── 2026-04-10-to-15-investigation.md
│       ├── poc/
│       │   ├── 01-recording-proxy.ts
│       │   ├── 02-mock-advisor-proxy.ts
│       │   ├── 03-sdk-validation.ts
│       │   ├── 04-multi-turn-validation.ts
│       │   ├── 05-tool-loop-proxy.ts
│       │   ├── 06-sdk-e2e-validation.ts
│       │   └── README.md
│       └── research/
│           ├── 01-advisor-pattern-research.md
│           ├── 01-research-plan.md
│           ├── 02-proxy-replacement-architecture.md
│           ├── 03-how-to-enable-advisor.md
│           ├── 04-real-test-results.md
│           ├── 05-stage1-tool-swap.md
│           └── 06-stage2-tool-result-rewrite.md
├── install.sh
├── landingpage/
│   ├── .firebaserc
│   ├── .gitignore
│   ├── App.tsx
│   ├── README.md
│   ├── components/
│   │   ├── BlockLogo.tsx
│   │   ├── BridgeDiagram.tsx
│   │   ├── Changelog.tsx
│   │   ├── FeatureSection.tsx
│   │   ├── HeroSection.tsx
│   │   ├── MultiModelAnimation.tsx
│   │   ├── SmartRouting.tsx
│   │   ├── SubscriptionSection.tsx
│   │   ├── SupportSection.tsx
│   │   ├── TerminalWindow.tsx
│   │   ├── TypingAnimation.tsx
│   │   └── VisionSection.tsx
│   ├── constants.ts
│   ├── firebase.json
│   ├── firebase.ts
│   ├── index.html
│   ├── index.tsx
│   ├── metadata.json
│   ├── package.json
│   ├── pnpm-workspace.yaml
│   ├── public/
│   │   └── site.webmanifest
│   ├── tsconfig.json
│   ├── types.ts
│   └── vite.config.ts
├── package.json
├── packages/
│   ├── .gitignore
│   ├── cli/
│   │   ├── .gitignore
│   │   ├── AI_AGENT_GUIDE.md
│   │   ├── bin/
│   │   │   └── claudish.cjs
│   │   ├── package.json
│   │   ├── recommended-models.json
│   │   ├── scripts/
│   │   │   ├── generate-version.ts
│   │   │   ├── smoke/
│   │   │   │   ├── probes.ts
│   │   │   │   ├── providers.ts
│   │   │   │   ├── reporter.ts
│   │   │   │   └── types.ts
│   │   │   ├── smoke-test.ts
│   │   │   └── smoke.test.ts
│   │   ├── skills/
│   │   │   └── claudish-usage/
│   │   │       └── SKILL.md
│   │   ├── src/
│   │   │   ├── adapters/
│   │   │   │   ├── anthropic-api-format.ts
│   │   │   │   ├── api-format.ts
│   │   │   │   ├── base-api-format.ts
│   │   │   │   ├── codex-api-format.ts
│   │   │   │   ├── deepseek-model-dialect.ts
│   │   │   │   ├── dialect-manager.ts
│   │   │   │   ├── gemini-api-format.ts
│   │   │   │   ├── glm-model-dialect.ts
│   │   │   │   ├── grok-model-dialect.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── litellm-api-format.ts
│   │   │   │   ├── local-adapter.ts
│   │   │   │   ├── minimax-model-dialect.ts
│   │   │   │   ├── model-catalog.test.ts
│   │   │   │   ├── model-catalog.ts
│   │   │   │   ├── model-dialect.ts
│   │   │   │   ├── ollama-api-format.ts
│   │   │   │   ├── openai-api-format.ts
│   │   │   │   ├── openrouter-api-format.ts
│   │   │   │   ├── qwen-model-dialect.ts
│   │   │   │   ├── tool-name-utils.ts
│   │   │   │   └── xiaomi-model-dialect.ts
│   │   │   ├── auth/
│   │   │   │   ├── auth-commands.ts
│   │   │   │   ├── codex-oauth.ts
│   │   │   │   ├── gemini-oauth.ts
│   │   │   │   ├── kimi-oauth.ts
│   │   │   │   ├── oauth-manager.ts
│   │   │   │   ├── oauth-registry.ts
│   │   │   │   ├── quota-command.ts
│   │   │   │   └── vertex-auth.ts
│   │   │   ├── channel/
│   │   │   │   ├── e2e-channel.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── scrollback-buffer.test.ts
│   │   │   │   ├── scrollback-buffer.ts
│   │   │   │   ├── session-manager.test.ts
│   │   │   │   ├── session-manager.ts
│   │   │   │   ├── signal-watcher.test.ts
│   │   │   │   ├── signal-watcher.ts
│   │   │   │   ├── test-helpers/
│   │   │   │   │   └── fake-claudish.ts
│   │   │   │   └── types.ts
│   │   │   ├── claude-runner.ts
│   │   │   ├── cli-passthrough.test.ts
│   │   │   ├── cli.test.ts
│   │   │   ├── cli.ts
│   │   │   ├── config-command.ts
│   │   │   ├── config-schema.test.ts
│   │   │   ├── config-schema.ts
│   │   │   ├── config.ts
│   │   │   ├── default-provider.test.ts
│   │   │   ├── default-provider.ts
│   │   │   ├── diag-output.ts
│   │   │   ├── format-translation.test.ts
│   │   │   ├── glm-adapter.test.ts
│   │   │   ├── handlers/
│   │   │   │   ├── composed-handler.test.ts
│   │   │   │   ├── composed-handler.ts
│   │   │   │   ├── default-provider-e2e.test.ts
│   │   │   │   ├── fallback-handler.test.ts
│   │   │   │   ├── fallback-handler.ts
│   │   │   │   ├── native-handler-advisor.test.ts
│   │   │   │   ├── native-handler-advisor.ts
│   │   │   │   ├── native-handler.ts
│   │   │   │   ├── shared/
│   │   │   │   │   ├── anthropic-error.test.ts
│   │   │   │   │   ├── anthropic-error.ts
│   │   │   │   │   ├── format/
│   │   │   │   │   │   ├── identity-filter.ts
│   │   │   │   │   │   ├── openai-messages.ts
│   │   │   │   │   │   └── openai-tools.ts
│   │   │   │   │   ├── gemini-queue.ts
│   │   │   │   │   ├── gemini-schema.ts
│   │   │   │   │   ├── local-queue.ts
│   │   │   │   │   ├── openai-compat.ts
│   │   │   │   │   ├── openrouter-queue.ts
│   │   │   │   │   ├── remote-provider-types.ts
│   │   │   │   │   ├── stream-parsers/
│   │   │   │   │   │   ├── anthropic-sse.ts
│   │   │   │   │   │   ├── gemini-sse.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── ollama-jsonl.ts
│   │   │   │   │   │   ├── openai-responses-sse.ts
│   │   │   │   │   │   └── openai-sse.ts
│   │   │   │   │   ├── token-tracker.ts
│   │   │   │   │   ├── tool-call-recovery.ts
│   │   │   │   │   └── web-search-detector.ts
│   │   │   │   └── types.ts
│   │   │   ├── index.ts
│   │   │   ├── logger.ts
│   │   │   ├── mcp-server.ts
│   │   │   ├── middleware/
│   │   │   │   ├── gemini-thought-signature.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── manager.ts
│   │   │   │   └── types.ts
│   │   │   ├── model-catalog.test.ts
│   │   │   ├── model-loader.ts
│   │   │   ├── model-selector.ts
│   │   │   ├── native-anthropic-mapping.test.ts
│   │   │   ├── port-manager.ts
│   │   │   ├── probe/
│   │   │   │   ├── probe-results-printer.ts
│   │   │   │   ├── probe-tui-app.tsx
│   │   │   │   └── probe-tui-runtime.tsx
│   │   │   ├── profile-commands.ts
│   │   │   ├── profile-config.ts
│   │   │   ├── providers/
│   │   │   │   ├── all-models-cache.test.ts
│   │   │   │   ├── all-models-cache.ts
│   │   │   │   ├── api-key-map.ts
│   │   │   │   ├── api-key-provenance.ts
│   │   │   │   ├── auto-route-default-provider.test.ts
│   │   │   │   ├── auto-route.ts
│   │   │   │   ├── catalog-resolvers/
│   │   │   │   │   ├── litellm.ts
│   │   │   │   │   ├── openrouter.test.ts
│   │   │   │   │   ├── openrouter.ts
│   │   │   │   │   └── static-fallback.ts
│   │   │   │   ├── custom-endpoints-loader.test.ts
│   │   │   │   ├── custom-endpoints-loader.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── model-catalog-resolver.ts
│   │   │   │   ├── model-parser.ts
│   │   │   │   ├── probe-live.ts
│   │   │   │   ├── provider-definitions.test.ts
│   │   │   │   ├── provider-definitions.ts
│   │   │   │   ├── provider-profiles.ts
│   │   │   │   ├── provider-registry.ts
│   │   │   │   ├── provider-resolver.ts
│   │   │   │   ├── provider-routing.test.ts
│   │   │   │   ├── remote-provider-registry.ts
│   │   │   │   ├── routing-rules.test.ts
│   │   │   │   ├── routing-rules.ts
│   │   │   │   ├── runtime-providers.test.ts
│   │   │   │   ├── runtime-providers.ts
│   │   │   │   └── transport/
│   │   │   │       ├── anthropic-compat.test.ts
│   │   │   │       ├── anthropic-compat.ts
│   │   │   │       ├── gemini-apikey.ts
│   │   │   │       ├── gemini-codeassist.ts
│   │   │   │       ├── litellm.ts
│   │   │   │       ├── local.ts
│   │   │   │       ├── ollamacloud.ts
│   │   │   │       ├── openai-codex.ts
│   │   │   │       ├── openai.test.ts
│   │   │   │       ├── openai.ts
│   │   │   │       ├── openrouter.ts
│   │   │   │       ├── poe.ts
│   │   │   │       ├── types.ts
│   │   │   │       └── vertex-oauth.ts
│   │   │   ├── proxy-server.ts
│   │   │   ├── services/
│   │   │   │   ├── pricing-cache.ts
│   │   │   │   └── vision-proxy.ts
│   │   │   ├── stats-buffer.test.ts
│   │   │   ├── stats-buffer.ts
│   │   │   ├── stats-otlp.test.ts
│   │   │   ├── stats-otlp.ts
│   │   │   ├── stats.test.ts
│   │   │   ├── stats.ts
│   │   │   ├── team-cli.ts
│   │   │   ├── team-grid.e2e-helpers.ts
│   │   │   ├── team-grid.e2e.test.ts
│   │   │   ├── team-grid.ts
│   │   │   ├── team-orchestrator.test.ts
│   │   │   ├── team-orchestrator.ts
│   │   │   ├── team-timeout-repro.test.ts
│   │   │   ├── telemetry.test.ts
│   │   │   ├── telemetry.ts
│   │   │   ├── test-fixtures/
│   │   │   │   ├── extract-sse-from-log.ts
│   │   │   │   └── sse-responses/
│   │   │   │       ├── SEED-anthropic-text-only.sse
│   │   │   │       ├── SEED-anthropic-thinking.sse
│   │   │   │       ├── SEED-openai-text-only.sse
│   │   │   │       ├── SEED-openai-tool-call.sse
│   │   │   │       ├── minimax-m25-turn1-thinking-text-tool.sse
│   │   │   │       ├── minimax-m25-turn2-thinking-tool-only.sse
│   │   │   │       ├── minimax-m25-turn3-thinking-multichunk.sse
│   │   │   │       ├── regression-zai-glm5-instream-error.sse
│   │   │   │       └── regression-zai-glm5-usage.sse
│   │   │   ├── transform.ts
│   │   │   ├── tui/
│   │   │   │   ├── App.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   ├── panels/
│   │   │   │   │   ├── ApiKeysPanel.tsx
│   │   │   │   │   ├── ConfigViewPanel.tsx
│   │   │   │   │   ├── ProfilesPanel.tsx
│   │   │   │   │   ├── ProvidersPanel.tsx
│   │   │   │   │   ├── RoutingPanel.tsx
│   │   │   │   │   ├── StatsPanel.tsx
│   │   │   │   │   └── TelemetryPanel.tsx
│   │   │   │   ├── providers.ts
│   │   │   │   ├── test-provider.ts
│   │   │   │   └── theme.ts
│   │   │   ├── types.ts
│   │   │   ├── update-checker.ts
│   │   │   ├── update-command.ts
│   │   │   ├── utils.ts
│   │   │   ├── version.ts
│   │   │   └── zai-glm.e2e.test.ts
│   │   ├── tsconfig.json
│   │   └── tsconfig.tui.json
│   ├── macos-bridge/
│   │   ├── docs/
│   │   │   └── PROXY_TRAFFIC_FLOW.md
│   │   ├── package.json
│   │   ├── scripts/
│   │   │   ├── full-test.js
│   │   │   ├── simple-test.js
│   │   │   ├── test-claude-desktop.sh
│   │   │   ├── test-cycletls.ts
│   │   │   ├── test-full-interception.sh
│   │   │   └── test-proxy.sh
│   │   ├── src/
│   │   │   ├── auth.ts
│   │   │   ├── bridge.test.ts
│   │   │   ├── certificate-manager.ts
│   │   │   ├── config-manager.ts
│   │   │   ├── connect-handler.ts
│   │   │   ├── cycletls-manager.ts
│   │   │   ├── detection.ts
│   │   │   ├── http-parser.ts
│   │   │   ├── https-proxy-server.ts
│   │   │   ├── index.ts
│   │   │   ├── process-manager.ts
│   │   │   ├── routing-middleware.ts
│   │   │   ├── server.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   ├── magmux-darwin-arm64/
│   │   ├── .gitignore
│   │   ├── bin/
│   │   │   └── .gitkeep
│   │   └── package.json
│   ├── magmux-darwin-x64/
│   │   ├── .gitignore
│   │   ├── bin/
│   │   │   └── .gitkeep
│   │   └── package.json
│   ├── magmux-linux-arm64/
│   │   ├── .gitignore
│   │   ├── bin/
│   │   │   └── .gitkeep
│   │   └── package.json
│   └── magmux-linux-x64/
│       ├── .gitignore
│       ├── bin/
│       │   └── .gitkeep
│       └── package.json
├── recommended-models.json
├── scripts/
│   ├── generate-manifest.ts
│   ├── postinstall.cjs
│   └── update-models.ts
├── skills/
│   └── claudish-usage/
│       └── SKILL.md
├── test-mcp-e2e.ts
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TRIAGE.md
================================================
# Issue Triage Bot Setup

The Claudish project uses an automated issue triage bot powered by [Claude Code](https://github.com/anthropics/claude-code) (Opus 4.6) to categorize and respond to new GitHub issues.

## How It Works

When a new issue is opened:

1. **Checkout**: Full repository is checked out
2. **Claude Code Agent**: Runs with full codebase access via claudish
3. **Exploration**: Agent reads `README.md`, checks `src/` implementations, looks at `docs/`
4. **Analysis**: Determines if feature exists, is planned, or is new
5. **Response**: Posts a conversational reply with specific file references

## Key Difference: Full Codebase Access

Unlike simple API-based bots, this triage bot runs Claude Code with full access to:
- All source code in `src/`
- Documentation in `docs/` and `ai_docs/`
- Working examples in `README.md`
- Protocol documentation in `*.md` files

This means it can give accurate answers like "that's already implemented in `src/transform.ts`" or "see the Extended Thinking section in `README.md` for usage."

## Labels Used

| Label | Description |
|-------|-------------|
| `bug` | Something broken in existing feature |
| `enhancement` | New feature or improvement |
| `question` | User needs help/clarification |
| `discussion` | Open-ended topic for feedback |
| `duplicate` | Already exists as issue/feature |
| `P0-critical` | Critical - blocking users |
| `P1-high` | High - significant impact |
| `P2-medium` | Medium - quality of life |
| `P3-low` | Low - nice to have |
| `already-implemented` | Feature already exists |
| `planned` | Feature is on the roadmap |
| `provider-specific` | Related to specific provider (OpenRouter, Poe) |
| `protocol` | Related to Anthropic/OpenAI protocol translation |

## Setup Requirements

Add these secrets to your repository:

| Secret | Required | Description |
|--------|----------|-------------|
| `ANTHROPIC_API_KEY` | Yes | Anthropic API key for Claude Code (Opus 4.6) |
| `CLAUDISH_BOT_APP_ID` | Yes | GitHub App ID for the triage bot |
| `CLAUDISH_BOT_PRIVATE_KEY` | Yes | GitHub App private key |

## Response Style

The bot uses a conversational, specific response style:
- 2-4 sentences max
- References specific files/examples from the codebase
- No generic phrases like "Thanks for sharing!"
- Points to documentation for planned features
- Willing to push back respectfully when needed

## Example Responses

**Already implemented:**
> The token scaling you're asking about is already in place - check out `src/transform.ts` and the Context Scaling section in `README.md`. The implementation handles any context window from 128k to 2M+.

**Configuration question:**
> You can set the model via `CLAUDISH_MODEL` env var or `--model` flag. See the Environment Variables table in README.md - if you're hitting rate limits, try `x-ai/grok-code-fast-1` which has generous limits.

**New idea:**
> Interesting angle on supporting local LLMs. We'd need to add a new provider handler in `src/proxy-server.ts`. Converting this to a discussion to gather more input on which local LLM APIs to prioritize.

**Bug Report:**
> I can reproduce this streaming issue. Looks like it's in the SSE handling in `src/transform.ts:245`. The `content_block_start` needs to fire before `ping` - that's documented in `STREAMING_PROTOCOL.md`.


================================================
FILE: .github/prompts/issue-comment-system.md
================================================
# Claudish Issue Comment Reply Agent

You are responding to a follow-up comment on a GitHub issue where you (claudish-bot) previously participated.

## Your Task

1. Read the full conversation from `.triage/conversation.md`
2. Determine if you should reply (see criteria below)
3. If yes, write your response to `.triage/result.json`
4. If no, write `{"should_reply": false}` to `.triage/result.json`

## Should You Reply?

**Reply ONLY if ALL of these are true:**
- You (claudish-bot) have previously commented on this issue
- The latest comment is NOT from claudish-bot (don't reply to yourself)
- The comment is directed at you OR continues a thread you started OR asks a follow-up question

**Do NOT reply if:**
- You haven't commented on this issue before (you're not part of this conversation)
- The comment is between other users discussing amongst themselves
- The comment is just "thanks" or a simple acknowledgment
- The issue has been resolved/closed
- Someone else (a human maintainer) has already answered the follow-up

## Response Style

Same rules as initial triage - conversational, specific, brief:
- 2-4 sentences MAX
- Reference specific files/examples when helpful
- Use markdown formatting (bullets, headers) for readability
- No corporate-speak ("Great follow-up question!")

### Markdown Formatting

Structure responses for **readability**:

```markdown
@username Good question about [specific thing].

**Short answer:** [direct answer]

If you want more detail, check `src/[file].ts` - it shows [specific pattern].
```

## Output Format

Write to `.triage/result.json`:

```json
{
  "should_reply": true,
  "reason": "User asked follow-up question about streaming",
  "response": "Your response here with proper markdown formatting"
}
```

Or if you shouldn't reply:

```json
{
  "should_reply": false,
  "reason": "Comment is between other users, not directed at bot"
}
```

## Context Awareness

You have the full conversation history. Use it to:
- Avoid repeating information you already gave
- Build on previous answers
- Notice if the user tried your suggestion and it didn't work
- Recognize when to escalate to a human (@jackrudenko / Jack)

## When to Escalate

If the question requires:
- A decision about Claudish's design direction
- Access to private/internal information
- Judgment calls about priorities
- Complex debugging that needs maintainer attention

Then reply with something like:
```markdown
@username That's a design decision I'd want @jackrudenko to weigh in on - [brief context of the tradeoff].
```

## Key Files to Reference

When answering technical questions, reference these:

- `src/proxy-server.ts` - Main proxy, request handling
- `src/transform.ts` - API translation layer
- `src/cli.ts` - CLI flags and argument parsing
- `src/config.ts` - Defaults and constants
- `README.md` - User documentation
- `STREAMING_PROTOCOL.md` - SSE protocol details


================================================
FILE: .github/prompts/issue-triage-system.md
================================================
# Claudish Issue Triage Agent

You are triaging GitHub issues for the Claudish CLI tool.

## Project Context

Claudish (Claude-ish) is a CLI tool that allows you to run Claude Code with any OpenRouter model by proxying requests through a local Anthropic API-compatible server. Key features:
- Multi-provider support (OpenRouter, Poe)
- Extended thinking/reasoning support
- Token scaling for any context window size
- Full Anthropic Messages API protocol compliance
- Agent support (`--agent` flag)
- Monitor mode for debugging

## Your Task

1. Read the issue from `.triage/issue.md`
2. Explore the codebase:
   - `README.md` - Main documentation and feature list
   - `src/` - Implementation code
   - `docs/` - Additional documentation
   - `ai_docs/` - AI-specific documentation
   - `STREAMING_PROTOCOL.md` - SSE protocol spec
   - `CHANGELOG.md` - Recent changes
3. Determine if the feature/fix already exists or is planned
4. Write your triage result to `.triage/result.json`

## Triage Categories

- `bug` - Something broken in existing feature
- `enhancement` - New feature or improvement request
- `question` - User needs help/clarification
- `duplicate` - Already exists as implemented feature
- `discussion` - Open-ended topic needing community input

## Available Labels

Priority: `P0-critical`, `P1-high`, `P2-medium`, `P3-low`
Type: `bug`, `enhancement`, `question`, `discussion`, `duplicate`
Status: `already-implemented`, `planned`, `good first issue`, `help wanted`, `documentation`
Area: `provider-specific`, `protocol`, `streaming`, `thinking`, `agent-support`

## Response Style (CRITICAL)

You're a peer responding to a GitHub issue. You actually read it. You have something worth adding.

### Core Principle
Prove you explored the codebase. Reference ONE specific file or example. Add value or ask a real question. Get out.

### Voice
- Conversational, not performative
- Brief and specific (2-4 sentences MAX)
- Adds perspective, doesn't just validate
- Willing to respectfully push back
- Uses author's username naturally

### Format Rules
- Start mid-thought. Cut setup. Lead with your actual point.
- One exclamation point max (preferably zero)
- Use contractions: "I've" not "I have", "didn't" not "did not"

### Markdown Formatting (IMPORTANT)

Structure responses for **readability**. Use blank lines and visual hierarchy:

**When listing multiple items** (files, features, steps):
```markdown
@username Here's what I found:

- Feature X is in `src/feature.ts`
- Related docs at `docs/feature.md`
- Config options in `src/config.ts`

The tricky part is [specific detail].
```

**When explaining with context**:
```markdown
@username The token scaling you're asking about works differently than you might expect.

**How it works:**
- Scales reported usage so Claude sees 200k regardless of actual limit
- Status line shows real usage
- See `src/transform.ts:handleUsage()` for implementation

What model are you using? Knowing that helps me point you to the right config.
```

**When referencing code**:
- Use inline backticks for files: `src/proxy-server.ts`
- Use inline backticks for flags: `--model`, `--agent`
- Use code blocks for multi-line examples only

**Spacing rules**:
- Blank line before bullet lists
- Blank line after section headers
- Keep paragraphs short (2-3 sentences max per paragraph)
- Separate distinct thoughts with blank lines

### NEVER Use These Phrases
- "Great question!"
- "Thanks for opening this issue!"
- "I appreciate you bringing this up!"
- "This is a valuable suggestion!"
- "Thanks for your interest in Claudish!"
- Any sentence that could apply to literally any issue

### Response Formulas

**Already Implemented:**
```markdown
@username The [feature] you're describing already exists.

**Where to find it:**
- Implementation: `src/[file].ts`
- Docs: `README.md` section "[X]"

[Brief note on how it works or any limitations]
```

**Configuration Help:**
```markdown
@username You can configure this with [flag/env var].

**Options:**
- Flag: `--[flag]`
- Env: `[ENV_VAR]`
- Default: [value]

[Brief note on common gotchas]
```

**Bug Report:**
```markdown
@username I can reproduce this.

**What I found:**
- Trigger: [specific scenario]
- Cause: [brief diagnosis]
- Location: `src/[file].ts:[line]`

[Next step: will fix / need more info / workaround]
```

**New Idea:**
```markdown
@username Interesting angle on [specific point from their issue].

We've got [related thing] in `src/[file].ts`, but hadn't considered [their specific twist].

[Suggest discussion or ask clarifying question]
```

**Gentle Pushback:**
```markdown
@username I see where you're coming from, but [alternative perspective].

Have you tried [existing solution]? It's documented in [location].

If that doesn't work for your case, what specifically are you trying to achieve?
```

## Output Format

Write to `.triage/result.json`:

```json
{
  "category": "bug|enhancement|question|duplicate|discussion",
  "labels": ["label1", "label2"],
  "priority": "P0-critical|P1-high|P2-medium|P3-low|null",
  "assign_to_jack": true|false,
  "already_implemented": true|false,
  "related_files": ["src/feature.ts", "docs/feature.md"],
  "convert_to_discussion": true|false,
  "response": "Your 2-4 sentence response here"
}
```

## Decision Guidelines

- **assign_to_jack**: true for bugs, high-priority enhancements, or items needing owner decision
- **convert_to_discussion**: true for open-ended topics, feature debates, or "what do people think about X"
- **already_implemented**: true if the core functionality exists (even if partial)
- **priority**: Only set for bugs and concrete enhancements, not questions/discussions

## Key Files to Reference

- `src/proxy-server.ts` - Main proxy server, request handling
- `src/transform.ts` - Anthropic <-> OpenAI API translation
- `src/cli.ts` - CLI argument parsing, flags
- `src/config.ts` - Constants, model defaults
- `src/claude-runner.ts` - Claude Code spawning, settings
- `README.md` - User-facing documentation
- `STREAMING_PROTOCOL.md` - SSE protocol specification
- `CHANGELOG.md` - Recent changes and versions

## Red Flags to Self-Check

Before writing response:
- [ ] Did I reference something SPECIFIC from the codebase?
- [ ] Could this response apply to any random issue? (If yes, rewrite)
- [ ] Is it scannable? (Use bullets/headers if 3+ items)
- [ ] Are there blank lines separating distinct thoughts?
- [ ] Would I actually say this to someone's face?
- [ ] Am I adding value or just seeking to appear helpful?


================================================
FILE: .github/release.yml
================================================
changelog:
  exclude:
    labels:
      - skip-changelog
    authors:
      - github-actions[bot]
  categories:
    - title: "🚀 New Features"
      labels:
        - enhancement
        - feature
    - title: "🐛 Bug Fixes"
      labels:
        - bug
        - fix
    - title: "📖 Documentation"
      labels:
        - documentation
    - title: "🔧 Maintenance"
      labels:
        - chore
        - maintenance
    - title: "Other Changes"
      labels:
        - "*"


================================================
FILE: .github/workflows/claude-code.yml
================================================
name: Claude Code PR Assistant

on:
  pull_request:
    types: [opened, synchronize, reopened]
  pull_request_review_comment:
    types: [created]
  issue_comment:
    types: [created]

permissions:
  contents: read
  pull-requests: write
  issues: write

jobs:
  claude-code:
    runs-on: ubuntu-latest
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
    # Skip if comment is from bot (avoid loops)
    # For issue_comment, only process if it's on a PR
    if: |
      (github.event_name != 'issue_comment' && github.event_name != 'pull_request_review_comment') ||
      (github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.user.login != 'github-actions[bot]') ||
      (github.event_name == 'pull_request_review_comment' && github.event.comment.user.login != 'github-actions[bot]')

    steps:
      - name: Checkout code
        uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - name: Claude Code Action
        uses: anthropics/claude-code-action@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}


================================================
FILE: .github/workflows/issue-triage.yml
================================================
name: Issue Triage

on:
  issues:
    types: [opened]
  issue_comment:
    types: [created]
  workflow_dispatch:
    inputs:
      issue_number:
        description: 'Issue number to triage'
        required: true
        type: number

permissions:
  issues: write
  contents: read

jobs:
  triage:
    runs-on: ubuntu-latest
    # Skip if comment is from the bot itself (claudish-bot app)
    if: github.event_name != 'issue_comment' || github.event.comment.user.login != 'claudish-bot[bot]'
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
    steps:
      - name: Checkout repository
        uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v5
        with:
          node-version: '22'

      - name: Install Claude Code
        run: npm install -g @anthropic-ai/claude-code@latest

      - name: Generate Claudish Bot token
        id: claudish-bot
        uses: tibdex/github-app-token@v2
        with:
          app_id: ${{ secrets.CLAUDISH_BOT_APP_ID }}
          private_key: ${{ secrets.CLAUDISH_BOT_PRIVATE_KEY }}

      - name: Determine trigger type
        id: trigger
        run: |
          if [ "${{ github.event_name }}" = "issue_comment" ]; then
            echo "type=comment" >> $GITHUB_OUTPUT
            echo "issue_number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
          elif [ -n "${{ github.event.issue.number }}" ]; then
            echo "type=new_issue" >> $GITHUB_OUTPUT
            echo "issue_number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
          else
            echo "type=manual" >> $GITHUB_OUTPUT
            echo "issue_number=${{ inputs.issue_number }}" >> $GITHUB_OUTPUT
          fi

      - name: Get issue details
        id: issue
        env:
          GH_TOKEN: ${{ steps.claudish-bot.outputs.token }}
        run: |
          mkdir -p .triage
          ISSUE_NUM="${{ steps.trigger.outputs.issue_number }}"
          echo "number=$ISSUE_NUM" >> $GITHUB_OUTPUT

          # Fetch issue details
          gh api repos/${{ github.repository }}/issues/$ISSUE_NUM > .triage/issue_data.json
          echo "title=$(jq -r '.title' .triage/issue_data.json)" >> $GITHUB_OUTPUT
          echo "author=$(jq -r '.user.login' .triage/issue_data.json)" >> $GITHUB_OUTPUT

          # Fetch all comments
          gh api repos/${{ github.repository }}/issues/$ISSUE_NUM/comments > .triage/comments.json

          # Check if bot has participated in this conversation
          BOT_PARTICIPATED=$(jq '[.[] | select(.user.login == "claudish-bot[bot]")] | length > 0' .triage/comments.json)
          echo "bot_participated=$BOT_PARTICIPATED" >> $GITHUB_OUTPUT

      - name: Write issue to file
        if: steps.trigger.outputs.type == 'new_issue' || steps.trigger.outputs.type == 'manual'
        run: |
          BODY=$(jq -r '.body // "No description provided"' .triage/issue_data.json)
          cat > .triage/issue.md << ISSUE_EOF
          # Issue #${{ steps.issue.outputs.number }}

          **Title:** ${{ steps.issue.outputs.title }}

          **Author:** @${{ steps.issue.outputs.author }}

          **Body:**
          $BODY
          ISSUE_EOF

      - name: Write conversation to file
        if: steps.trigger.outputs.type == 'comment'
        run: |
          # Build full conversation markdown
          ISSUE_BODY=$(jq -r '.body // "No description provided"' .triage/issue_data.json)
          ISSUE_AUTHOR=$(jq -r '.user.login' .triage/issue_data.json)

          cat > .triage/conversation.md << 'CONV_HEADER'
          # Issue Conversation

          CONV_HEADER

          echo "## Original Issue" >> .triage/conversation.md
          echo "**Author:** @$ISSUE_AUTHOR" >> .triage/conversation.md
          echo "**Title:** ${{ steps.issue.outputs.title }}" >> .triage/conversation.md
          echo "" >> .triage/conversation.md
          echo "$ISSUE_BODY" >> .triage/conversation.md
          echo "" >> .triage/conversation.md
          echo "---" >> .triage/conversation.md
          echo "" >> .triage/conversation.md
          echo "## Comments" >> .triage/conversation.md
          echo "" >> .triage/conversation.md

          # Add each comment
          jq -r '.[] | "### @\(.user.login)\n\(.body)\n\n---\n"' .triage/comments.json >> .triage/conversation.md

          echo "" >> .triage/conversation.md
          echo "## Latest Comment (trigger)" >> .triage/conversation.md
          echo "**From:** @${{ github.event.comment.user.login }}" >> .triage/conversation.md
          echo "" >> .triage/conversation.md

      - name: Skip comment if bot not in conversation
        id: should_process
        if: steps.trigger.outputs.type == 'comment'
        run: |
          if [ "${{ steps.issue.outputs.bot_participated }}" = "false" ]; then
            echo "skip=true" >> $GITHUB_OUTPUT
            echo "Bot has not participated in this conversation, skipping..."
          else
            echo "skip=false" >> $GITHUB_OUTPUT
            echo "Bot previously commented, will analyze for reply..."
          fi

      - name: Triage new issue with Claude Code
        id: triage
        if: steps.trigger.outputs.type == 'new_issue' || steps.trigger.outputs.type == 'manual'
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          # Run Claude Code in print mode with Opus 4.6
          claude --model opus -p --dangerously-skip-permissions \
            --system-prompt "$(cat .github/prompts/issue-triage-system.md)" \
            "Triage the GitHub issue in .triage/issue.md. Read it, explore the codebase for context, then write your triage result to .triage/result.json"

          echo "Claude Code completed"

          # Read the result file
          if [ -f .triage/result.json ]; then
            CLEAN_JSON=$(cat .triage/result.json)
          else
            echo "Error: result.json not created"
            exit 1
          fi

          # Extract fields
          echo "category=$(echo "$CLEAN_JSON" | jq -r '.category // "question"')" >> $GITHUB_OUTPUT
          echo "labels=$(echo "$CLEAN_JSON" | jq -r '.labels | join(",")')" >> $GITHUB_OUTPUT
          echo "priority=$(echo "$CLEAN_JSON" | jq -r '.priority // empty')" >> $GITHUB_OUTPUT
          echo "assign_jack=$(echo "$CLEAN_JSON" | jq -r '.assign_to_jack // false')" >> $GITHUB_OUTPUT
          echo "convert_discussion=$(echo "$CLEAN_JSON" | jq -r '.convert_to_discussion // false')" >> $GITHUB_OUTPUT

          RESPONSE_TEXT=$(echo "$CLEAN_JSON" | jq -r '.response // empty')
          echo "response<<EOF" >> $GITHUB_OUTPUT
          echo "$RESPONSE_TEXT" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

          # Show related files for debugging
          echo "Related files:"
          echo "$CLEAN_JSON" | jq -r '.related_files[]?' || true

      - name: Reply to comment with Claude Code
        id: reply
        if: steps.trigger.outputs.type == 'comment' && steps.should_process.outputs.skip != 'true'
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          # Run Claude Code to analyze conversation and decide if reply needed
          claude --model opus -p --dangerously-skip-permissions \
            --system-prompt "$(cat .github/prompts/issue-comment-system.md)" \
            "Analyze the conversation in .triage/conversation.md. Decide if you should reply. Write result to .triage/result.json"

          echo "Claude Code completed"

          if [ -f .triage/result.json ]; then
            CLEAN_JSON=$(cat .triage/result.json)
          else
            echo "Error: result.json not created"
            exit 1
          fi

          # Extract fields
          SHOULD_REPLY=$(echo "$CLEAN_JSON" | jq -r '.should_reply // false')
          echo "should_reply=$SHOULD_REPLY" >> $GITHUB_OUTPUT

          RESPONSE_TEXT=$(echo "$CLEAN_JSON" | jq -r '.response // empty')
          echo "response<<EOF" >> $GITHUB_OUTPUT
          echo "$RESPONSE_TEXT" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

          REASON=$(echo "$CLEAN_JSON" | jq -r '.reason // empty')
          echo "Reason: $REASON"

      - name: Add labels
        if: steps.triage.outputs.labels != ''
        env:
          GH_TOKEN: ${{ steps.claudish-bot.outputs.token }}
        run: |
          IFS=',' read -ra LABEL_ARRAY <<< "${{ steps.triage.outputs.labels }}"
          for label in "${LABEL_ARRAY[@]}"; do
            # Only add if label exists
            if gh label list | grep -q "^$label"; then
              gh issue edit ${{ steps.issue.outputs.number }} --add-label "$label" || true
            fi
          done

      - name: Assign to Jack
        if: steps.triage.outputs.assign_jack == 'true'
        env:
          GH_TOKEN: ${{ steps.claudish-bot.outputs.token }}
        run: |
          gh issue edit ${{ steps.issue.outputs.number }} --add-assignee jackrudenko || true

      - name: Post triage response
        if: steps.triage.outputs.response != ''
        env:
          GH_TOKEN: ${{ steps.claudish-bot.outputs.token }}
          RESPONSE_TEXT: ${{ steps.triage.outputs.response }}
        run: |
          echo "$RESPONSE_TEXT" > .triage/comment.md
          gh issue comment ${{ steps.issue.outputs.number }} --body-file .triage/comment.md

      - name: Post comment reply
        if: steps.reply.outputs.should_reply == 'true' && steps.reply.outputs.response != ''
        env:
          GH_TOKEN: ${{ steps.claudish-bot.outputs.token }}
          RESPONSE_TEXT: ${{ steps.reply.outputs.response }}
        run: |
          echo "$RESPONSE_TEXT" > .triage/comment.md
          gh issue comment ${{ steps.issue.outputs.number }} --body-file .triage/comment.md

      - name: Convert to discussion (if needed)
        if: steps.triage.outputs.convert_discussion == 'true'
        env:
          GH_TOKEN: ${{ steps.claudish-bot.outputs.token }}
        run: |
          echo "Note: Issue marked for discussion conversion."
          gh issue edit ${{ steps.issue.outputs.number }} --add-label "discussion" || true

      - name: Cleanup
        if: always()
        run: rm -rf .triage


================================================
FILE: .github/workflows/release.yml
================================================
name: Release

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: write
  id-token: write  # Required for npm OIDC trusted publishing

jobs:
  build:
    strategy:
      matrix:
        include:
          - os: macos-latest
            target: bun-darwin-arm64
            artifact: claudish-darwin-arm64
            goos: darwin
            goarch: arm64
          - os: macos-15-intel
            target: bun-darwin-x64
            artifact: claudish-darwin-x64
            goos: darwin
            goarch: amd64
          - os: ubuntu-latest
            target: bun-linux-x64
            artifact: claudish-linux-x64
            goos: linux
            goarch: amd64
          - os: ubuntu-24.04-arm
            target: bun-linux-arm64
            artifact: claudish-linux-arm64
            goos: linux
            goarch: arm64

    runs-on: ${{ matrix.os }}
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

    steps:
      - uses: actions/checkout@v5

      - name: Setup Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - name: Download magmux from latest release
        run: |
          # Fetch latest magmux release from MadAppGang/magmux
          MAGMUX_TAG=$(gh release view --repo MadAppGang/magmux --json tagName -q .tagName)
          echo "Using magmux ${MAGMUX_TAG}"
          ASSET="magmux_${{ matrix.goos }}_${{ matrix.goarch }}.tar.gz"
          gh release download "${MAGMUX_TAG}" --repo MadAppGang/magmux --pattern "${ASSET}" --dir /tmp
          tar xzf "/tmp/${ASSET}" -C /tmp
          # Rename to Node.js platform-arch convention (amd64 → x64)
          NODE_ARCH="${{ matrix.goarch }}"
          if [ "$NODE_ARCH" = "amd64" ]; then NODE_ARCH="x64"; fi
          mkdir -p packages/cli/native
          mv /tmp/magmux "packages/cli/native/magmux-${{ matrix.goos }}-${NODE_ARCH}"
          chmod +x "packages/cli/native/magmux-${{ matrix.goos }}-${NODE_ARCH}"
          ls -la packages/cli/native/magmux-*
        env:
          GH_TOKEN: ${{ github.token }}

      - name: Install dependencies
        run: bun install

      - name: Build CLI
        run: bun run build:cli

      - name: Build binary
        run: |
          # Inject version from tag into fallback (for compiled binaries)
          VERSION="${GITHUB_REF#refs/tags/v}"
          sed -i.bak "s/VERSION = \".*\"/VERSION = \"$VERSION\"/" packages/cli/src/cli.ts
          # Build from root to preserve workspace resolution
          bun build packages/cli/src/index.ts --compile --target=${{ matrix.target }} --outfile ${{ matrix.artifact }}

      - name: Ad-hoc sign binary (macOS Gatekeeper compatibility)
        if: startsWith(matrix.target, 'bun-darwin')
        continue-on-error: true
        run: |
          codesign --force --deep --sign - ${{ matrix.artifact }} && codesign -v ${{ matrix.artifact }} || echo "Warning: codesign failed — Bun binary format may not support ad-hoc signing on this runner. Binary is still functional."

      - name: Upload CLI artifact
        uses: actions/upload-artifact@v5
        with:
          name: ${{ matrix.artifact }}
          path: ${{ matrix.artifact }}

      - name: Upload magmux artifact
        uses: actions/upload-artifact@v5
        with:
          name: magmux-${{ matrix.artifact }}
          path: packages/cli/native/magmux-*

  release:
    needs: build
    runs-on: ubuntu-latest
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0  # Full history for generating release notes from commits

      - name: Setup Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - name: Get version
        id: version
        run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

      - name: Install git-cliff
        uses: kenji-miyake/setup-git-cliff@v2  # no Node 24 version; covered by FORCE_JAVASCRIPT_ACTIONS_TO_NODE24

      - name: Generate release notes
        run: |
          VERSION="${GITHUB_REF#refs/tags/v}"
          CURRENT_TAG="v${VERSION}"
          PREV_TAG=$(git tag --sort=-v:refname | grep '^v' | grep -v "^${CURRENT_TAG}$" | head -1)

          # Generate release notes for this tag only
          if [ -n "$PREV_TAG" ]; then
            git cliff "${PREV_TAG}..${CURRENT_TAG}" --strip header -o release-notes.md
          else
            git cliff --strip header -o release-notes.md
          fi

          # Append install section
          {
            echo ""
            echo "## Install"
            echo ""
            echo '```bash'
            echo "# npm"
            echo "npm install -g claudish"
            echo ""
            echo "# Homebrew"
            echo "brew install MadAppGang/tap/claudish"
            echo ""
            echo "# or download binary from assets below"
            echo '```'
          } >> release-notes.md

          # Add compare link
          if [ -n "$PREV_TAG" ]; then
            echo "" >> release-notes.md
            echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...${CURRENT_TAG}" >> release-notes.md
          fi

          echo "Generated release notes:"
          cat release-notes.md

      - name: Update CHANGELOG.md
        run: |
          git cliff -o CHANGELOG.md
          if git diff --quiet CHANGELOG.md; then
            echo "CHANGELOG.md unchanged"
          else
            git config user.name "github-actions[bot]"
            git config user.email "github-actions[bot]@users.noreply.github.com"
            git add CHANGELOG.md
            git commit -m "docs: update CHANGELOG.md for v${GITHUB_REF#refs/tags/v}"
            git push origin HEAD:main
          fi

      - name: Download all artifacts
        uses: actions/download-artifact@v5
        with:
          path: artifacts

      - name: Prepare release files
        run: |
          mkdir -p release
          for dir in artifacts/*/; do
            # Copy all files from each artifact directory into release/
            # Handles both claudish binaries (file matches dir name) and
            # magmux binaries (file is magmux-*, dir is magmux-claudish-*)
            find "$dir" -type f | while read -r file; do
              cp "$file" "release/$(basename "$file")"
              chmod +x "release/$(basename "$file")"
            done
          done
          ls -la release/

      - name: Generate manifest and checksums
        run: |
          bun scripts/generate-manifest.ts ${{ steps.version.outputs.version }} release
          cat release/manifest.json
          cat release/checksums.txt

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2  # no Node 24 version; covered by FORCE_JAVASCRIPT_ACTIONS_TO_NODE24
        with:
          name: v${{ steps.version.outputs.version }}
          body_path: release-notes.md
          files: |
            release/claudish-*
            release/magmux-*
            release/manifest.json
            release/checksums.txt
          draft: false
          prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') }}

  publish-npm:
    needs: release
    runs-on: ubuntu-latest
    # OIDC trusted publishing - no NPM_TOKEN needed!
    # Configure at: https://www.npmjs.com/package/claudish/access (Trusted Publishers)

    steps:
      - uses: actions/checkout@v5

      - name: Setup Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - name: Setup Node.js
        uses: actions/setup-node@v5
        with:
          node-version: '24'
          registry-url: 'https://registry.npmjs.org'
          always-auth: true

      - name: Install dependencies
        run: bun install

      - name: Download magmux binaries
        uses: actions/download-artifact@v5
        with:
          pattern: magmux-*
          path: magmux-artifacts

      - name: Install magmux binaries
        run: |
          mkdir -p packages/cli/native
          for dir in magmux-artifacts/*/; do
            cp "$dir"/magmux-* packages/cli/native/ 2>/dev/null || true
          done
          chmod +x packages/cli/native/magmux-* 2>/dev/null || true
          echo "Magmux binaries:"
          ls -la packages/cli/native/magmux-*

      - name: Publish magmux platform packages
        run: |
          VERSION="${GITHUB_REF#refs/tags/v}"
          for pkg in packages/magmux-*/; do
            name=$(basename "$pkg")
            platform_arch="${name#magmux-}"

            # Copy the correct binary
            mkdir -p "${pkg}bin"
            cp "packages/cli/native/magmux-${platform_arch}" "${pkg}bin/magmux"
            chmod +x "${pkg}bin/magmux"

            # Update version
            cd "$pkg"
            node -e "const p=require('./package.json'); p.version='${VERSION}'; require('fs').writeFileSync('package.json', JSON.stringify(p,null,2))"

            echo "Publishing @claudish/${name} v${VERSION}..."
            npm publish --access public --provenance || echo "Failed to publish @claudish/${name} (may already exist)"
            cd ../..
          done

      - name: Update recommended models from OpenRouter
        run: |
          echo "Fetching latest model data from OpenRouter..."
          bun scripts/update-models.ts
          echo ""
          echo "Updated recommended-models.json:"
          cat packages/cli/recommended-models.json | head -50

      - name: Build packages
        run: bun run build:cli

      - name: Prepare for npm publish
        run: |
          cd packages/cli
          # Fix files array for npm publish
          VERSION="${GITHUB_REF#refs/tags/v}"
          node -e "
            const pkg = require('./package.json');
            delete pkg.dependencies['@claudish/core'];
            pkg.files = ['dist/', 'AI_AGENT_GUIDE.md', 'recommended-models.json', 'skills/'];
            // Sync optionalDependencies versions to release version
            if (pkg.optionalDependencies) {
              for (const key of Object.keys(pkg.optionalDependencies)) {
                if (key.startsWith('@claudish/magmux-')) {
                  pkg.optionalDependencies[key] = '${VERSION}';
                }
              }
            }
            require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2));
          "
          echo 'Modified package.json:'
          cat package.json

      - name: Publish to npm
        run: cd packages/cli && npm publish --access public --provenance

  deploy-landing-page:
    needs: release
    runs-on: ubuntu-latest
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

    steps:
      - uses: actions/checkout@v5

      - name: Setup Bun
        uses: oven-sh/setup-bun@v2
        with:
          bun-version: latest

      - name: Install dependencies
        run: cd landingpage && bun install --frozen-lockfile

      - name: Build landing page
        run: cd landingpage && bun run build

      - name: Deploy to Firebase Hosting
        uses: FirebaseExtended/action-hosting-deploy@v0  # no Node 24 version; covered by FORCE_JAVASCRIPT_ACTIONS_TO_NODE24
        with:
          repoToken: ${{ secrets.GITHUB_TOKEN }}
          firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
          channelId: live
          projectId: claudish-6da10
          entryPoint: landingpage

  update-homebrew:
    needs: release
    runs-on: ubuntu-latest
    if: ${{ vars.ENABLE_HOMEBREW == 'true' }}
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

    steps:
      - name: Get release info
        id: release
        run: |
          VERSION="${GITHUB_REF#refs/tags/v}"
          echo "version=$VERSION" >> $GITHUB_OUTPUT

          # Wait for release assets
          sleep 10

          # Get checksums
          curl -sL "https://github.com/${{ github.repository }}/releases/download/v${VERSION}/checksums.txt" -o checksums.txt

          ARM64_SHA=$(grep "darwin-arm64" checksums.txt | awk '{print $1}')
          X64_SHA=$(grep "darwin-x64" checksums.txt | awk '{print $1}')

          echo "arm64_sha=$ARM64_SHA" >> $GITHUB_OUTPUT
          echo "x64_sha=$X64_SHA" >> $GITHUB_OUTPUT

      - name: Update Homebrew tap
        uses: actions/checkout@v5
        with:
          repository: MadAppGang/homebrew-tap
          token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
          path: tap

      - name: Update formula
        run: |
          mkdir -p tap/Formula
          cat > tap/Formula/claudish.rb << EOF
          class Claudish < Formula
            desc "Multi-model AI CLI - run Claude Code with any model"
            homepage "https://github.com/MadAppGang/claudish"
            version "${{ steps.release.outputs.version }}"
            license "MIT"

            on_arm do
              url "https://github.com/MadAppGang/claudish/releases/download/v${{ steps.release.outputs.version }}/claudish-darwin-arm64"
              sha256 "${{ steps.release.outputs.arm64_sha }}"
            end

            on_intel do
              url "https://github.com/MadAppGang/claudish/releases/download/v${{ steps.release.outputs.version }}/claudish-darwin-x64"
              sha256 "${{ steps.release.outputs.x64_sha }}"
            end

            def install
              binary = "claudish-darwin-#{Hardware::CPU.arch == :arm64 ? "arm64" : "x64"}"
              bin.install binary => "claudish"
            end

            test do
              assert_match "claudish", shell_output("#{bin}/claudish --version")
            end
          end
          EOF

      - name: Push to tap
        run: |
          cd tap
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add Formula/claudish.rb
          git commit -m "Update claudish to v${{ steps.release.outputs.version }}"
          git push


================================================
FILE: .github/workflows/smoke-test.yml
================================================
name: Smoke Tests

on:
  schedule:
    - cron: "0 6 * * *" # Daily at 06:00 UTC
  workflow_dispatch: # Manual trigger

jobs:
  smoke:
    runs-on: ubuntu-latest
    env:
      FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
    steps:
      - uses: actions/checkout@v5

      - uses: oven-sh/setup-bun@v2

      - name: Install dependencies
        run: bun install --cwd packages/cli

      - name: Run smoke tests
        run: bun run --cwd packages/cli scripts/smoke-test.ts --quiet
        env:
          MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
          MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
          MINIMAX_CODING_API_KEY: ${{ secrets.MINIMAX_CODING_API_KEY }}
          ZHIPU_API_KEY: ${{ secrets.ZHIPU_API_KEY }}
          GLM_CODING_API_KEY: ${{ secrets.GLM_CODING_API_KEY }}
          OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
          OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
          ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
          KIMI_CODING_API_KEY: ${{ secrets.KIMI_CODING_API_KEY }}
          LITELLM_BASE_URL: ${{ secrets.LITELLM_BASE_URL }}

      - name: Upload smoke results
        uses: actions/upload-artifact@v5
        if: always()
        with:
          name: smoke-results-${{ github.run_id }}
          path: packages/cli/results/
          retention-days: 30


================================================
FILE: .gitignore
================================================
# Dependencies
node_modules/

# Build output
dist/
build/

# Environment files
.env
.env.local
.env.*.local

# IDE
.idea/
.vscode/
*.swp
*.swo

# OS files
.DS_Store
Thumbs.db

# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Test coverage
coverage/

# Temporary files
tmp/
temp/
all-models.json

# Claude Code local files
.claude/
.claudemem/

# npm lockfile (we use bun.lock)
package-lock.json

# Dev/test files
__tests__/
*.jinja
logs/

# AI session files
ai-docs/
ai_docs/
ai-sessions/
**/ai-sessions/

# Build artifacts
*.tsbuildinfo

# Temp dev files
claude
claude_desktop.flow

# Debug/analysis artifacts
*.pid
*.mitm
*.offset
analysis_result.txt
content_types.txt
decode_traffic.py
extracted_urls.txt
jetski_service.txt
service_offset.txt
tokens.json
test-results/

# Smoke test results
packages/cli/results/*.json
.worktrees

# Model validation
validation/


================================================
FILE: AI_AGENT_GUIDE.md
================================================
# Claudish AI Agent Usage Guide

**Version:** 2.2.0
**Target Audience:** AI Agents running within Claude Code
**Purpose:** Quick reference for using Claudish CLI and MCP server in agentic workflows

---

## TL;DR - Quick Start

```bash
# 1. Get available models
claudish --models --json

# 2. Auto-detected routing (model name determines provider)
claudish --model gpt-4o "your task here"               # → OpenAI
claudish --model gemini-2.0-flash "your task here"     # → Google
claudish --model llama-3.1-70b "your task here"        # → OllamaCloud

# 3. Explicit provider routing (new @ syntax)
claudish --model google@gemini-2.5-pro "your task here"
claudish --model oai@o1 "deep reasoning task"
claudish --model openrouter@deepseek/deepseek-r1 "analysis"  # Unknown vendors need OR@

# 4. Run with local model (with concurrency control)
claudish --model ollama@llama3.2 "your task here"
claudish --model ollama@llama3.2:3 "parallel task"     # 3 concurrent requests

# 5. For large prompts, use stdin
echo "your task" | claudish --stdin --model gpt-4o
```

## What is Claudish?

Claudish = Claude Code + Any AI Model

- ✅ Run Claude Code with **any AI model** via `provider@model` routing
- ✅ **Native auto-detection** - `gpt-4o` → OpenAI, `gemini-*` → Google, `llama-*` → OllamaCloud
- ✅ Supports direct APIs: Google, OpenAI, MiniMax, Kimi, GLM, Z.AI, OllamaCloud, Poe
- ✅ Supports local models (Ollama, LM Studio, vLLM, MLX) with concurrency control
- ✅ **MCP Server mode** - expose models as tools for Claude Code
- ✅ 100% Claude Code feature compatibility
- ✅ Local proxy server (no data sent to Claudish servers)
- ✅ Cost tracking and model selection

## Model Routing (v4.0+)

### New Syntax: `provider@model[:concurrency]`

| Shortcut | Provider | Example |
|----------|----------|---------|
| `google@`, `g@` | Google Gemini | `g@gemini-2.0-flash` |
| `oai@` | OpenAI Direct | `oai@gpt-4o` |
| `or@`, `openrouter@` | OpenRouter | `or@deepseek/deepseek-r1` |
| `mm@`, `mmax@` | MiniMax Direct | `mm@MiniMax-M2` |
| `kimi@`, `moon@` | Kimi Direct | `kimi@kimi-k2` |
| `glm@`, `zhipu@` | GLM Direct | `glm@glm-4` |
| `llama@`, `oc@` | OllamaCloud | `llama@llama-3.1-70b` |
| `v@`, `vertex@` | Vertex AI | `v@gemini-2.5-flash` |
| `poe@` | Poe | `poe@GPT-4o` |
| `ollama@` | Ollama (local) | `ollama@llama3.2:3` |
| `lmstudio@` | LM Studio | `lmstudio@qwen` |

### Native Model Auto-Detection

| Model Pattern | Routes To |
|---------------|-----------|
| `gemini-*`, `google/*` | Google API |
| `gpt-*`, `o1-*`, `o3-*` | OpenAI API |
| `llama-*`, `meta-llama/*` | OllamaCloud |
| `kimi-*`, `moonshot-*` | Kimi API |
| `glm-*`, `zhipu/*` | GLM API |
| `claude-*` | Native Anthropic |
| **Unknown vendors** | Error (use `openrouter@`) |

### Vertex AI Partner Models

Vertex AI supports Google + partner models (MaaS):

```bash
# Google Gemini on Vertex
claudish --model v/gemini-2.5-flash "task"

# Partner models (MiniMax, Mistral, DeepSeek, Qwen, OpenAI OSS)
claudish --model vertex/minimax/minimax-m2-maas "task"
claudish --model vertex/mistralai/codestral-2 "write code"
claudish --model vertex/deepseek/deepseek-v3-2-maas "analyze"
claudish --model vertex/qwen/qwen3-coder-480b-a35b-instruct-maas "implement"
claudish --model vertex/openai/gpt-oss-120b-maas "reason"
```

## Prerequisites

1. **Install Claudish:**
   ```bash
   npm install -g claudish
   ```

2. **Set API Key (at least one):**
   ```bash
   # OpenRouter (100+ models)
   export OPENROUTER_API_KEY='sk-or-v1-...'

   # OR Gemini direct
   export GEMINI_API_KEY='...'

   # OR Vertex AI (Express mode)
   export VERTEX_API_KEY='...'

   # OR Vertex AI (OAuth mode - uses gcloud ADC)
   export VERTEX_PROJECT='your-gcp-project-id'
   ```

3. **Optional but recommended:**
   ```bash
   export ANTHROPIC_API_KEY='sk-ant-api03-placeholder'
   ```

## Top Models for Development

| Model ID | Provider | Category | Best For |
|----------|----------|----------|----------|
| `openai/gpt-5.3` | OpenAI | Reasoning | **Default** - Most advanced reasoning |
| `minimax/minimax-m2.1` | MiniMax | Coding | Budget-friendly, fast |
| `z-ai/glm-4.7` | Z.AI | Coding | Balanced performance |
| `google/gemini-3-pro-preview` | Google | Reasoning | 1M context window |
| `moonshotai/kimi-k2-thinking` | MoonShot | Reasoning | Extended thinking |
| `deepseek/deepseek-v3.2` | DeepSeek | Coding | Code specialist |
| `qwen/qwen3-vl-235b-a22b-thinking` | Alibaba | Vision | Vision + reasoning |

**Direct API Options (lower latency):**

| Model ID | Backend | Best For |
|----------|---------|----------|
| `g/gemini-2.0-flash` | Gemini | Fast tasks, large context |
| `v/gemini-2.5-flash` | Vertex AI | Enterprise, GCP billing |
| `oai/gpt-4o` | OpenAI | General purpose |
| `ollama/llama3.2` | Local | Free, private |

**Vertex AI Partner Models (MaaS):**

| Model ID | Provider | Best For |
|----------|----------|----------|
| `vertex/minimax/minimax-m2-maas` | MiniMax | Fast, budget-friendly |
| `vertex/mistralai/codestral-2` | Mistral | Code specialist |
| `vertex/deepseek/deepseek-v3-2-maas` | DeepSeek | Deep reasoning |
| `vertex/qwen/qwen3-coder-480b-a35b-instruct-maas` | Qwen | Agentic coding |
| `vertex/openai/gpt-oss-120b-maas` | OpenAI | Open-weight reasoning |

**Update models:**
```bash
claudish --models --force-update
```

## Critical: File-Based Pattern for Sub-Agents

### ⚠️ Problem: Context Window Pollution

Running Claudish directly in main conversation pollutes context with:
- Entire conversation transcript
- All tool outputs
- Model reasoning (10K+ tokens)

### ✅ Solution: File-Based Sub-Agent Pattern

**Pattern:**
1. Write instructions to file
2. Run Claudish with file input
3. Read result from file
4. Return summary only (not full output)

**Example:**
```typescript
// Step 1: Write instruction file
const instructionFile = `/tmp/claudish-task-${Date.now()}.md`;
const resultFile = `/tmp/claudish-result-${Date.now()}.md`;

const instruction = `# Task
Implement user authentication

# Requirements
- JWT tokens
- bcrypt password hashing
- Protected route middleware

# Output
Write to: ${resultFile}
`;

await Write({ file_path: instructionFile, content: instruction });

// Step 2: Run Claudish
await Bash(`claudish --model x-ai/grok-code-fast-1 --stdin < ${instructionFile}`);

// Step 3: Read result
const result = await Read({ file_path: resultFile });

// Step 4: Return summary only
const summary = extractSummary(result);
return `✅ Completed. ${summary}`;

// Clean up
await Bash(`rm ${instructionFile} ${resultFile}`);
```

## Using Claudish in Sub-Agents

### Method 1: Direct Bash Execution

```typescript
// For simple tasks with short output
const { stdout } = await Bash("claudish --model x-ai/grok-code-fast-1 --json 'quick task'");
const result = JSON.parse(stdout);

// Return only essential info
return `Cost: $${result.total_cost_usd}, Result: ${result.result.substring(0, 100)}...`;
```

### Method 2: Task Tool Delegation

```typescript
// For complex tasks requiring isolation
const result = await Task({
  subagent_type: "general-purpose",
  description: "Implement feature with Grok",
  prompt: `
Use Claudish to implement feature with Grok model:

STEPS:
1. Create instruction file at /tmp/claudish-instruction-${Date.now()}.md
2. Write feature requirements to file
3. Run: claudish --model x-ai/grok-code-fast-1 --stdin < /tmp/claudish-instruction-*.md
4. Read result and return ONLY:
   - Files modified (list)
   - Brief summary (2-3 sentences)
   - Cost (if available)

DO NOT return full implementation details.
Keep response under 300 tokens.
  `
});
```

### Method 3: Multi-Model Comparison

```typescript
// Compare results from multiple models
const models = [
  "x-ai/grok-code-fast-1",
  "google/gemini-2.5-flash",
  "openai/gpt-5"
];

for (const model of models) {
  const result = await Bash(`claudish --model ${model} --json "analyze security"`);
  const data = JSON.parse(result.stdout);

  console.log(`${model}: $${data.total_cost_usd}`);
  // Store results for comparison
}
```

## Essential CLI Flags

### Core Flags

| Flag | Description | Example |
|------|-------------|---------|
| `--model <model>` | OpenRouter model to use | `--model x-ai/grok-code-fast-1` |
| `--stdin` | Read prompt from stdin | `cat task.md \| claudish --stdin --model grok` |
| `--json` | JSON output (structured) | `claudish --json "task"` |
| `--list-models` | List available models | `claudish --list-models --json` |

### Useful Flags

| Flag | Description | Default |
|------|-------------|---------|
| `--quiet` / `-q` | Suppress logs | Enabled in single-shot |
| `--verbose` / `-v` | Show logs | Enabled in interactive |
| `--debug` / `-d` | Debug logging to file | Disabled |
| `--no-auto-approve` | Require prompts | Auto-approve enabled |

## Common Workflows

### Workflow 1: Quick Code Fix (Grok)

```bash
# Fast coding with visible reasoning
claudish --model x-ai/grok-code-fast-1 "fix null pointer error in user.ts"
```

### Workflow 2: Complex Refactoring (GPT-5)

```bash
# Advanced reasoning for architecture
claudish --model openai/gpt-5 "refactor to microservices architecture"
```

### Workflow 3: Code Review (Gemini)

```bash
# Deep analysis with large context
git diff | claudish --stdin --model google/gemini-2.5-flash "review for bugs"
```

### Workflow 4: UI Implementation (Qwen Vision)

```bash
# Vision model for visual tasks
claudish --model qwen/qwen3-vl-235b-a22b-instruct "implement dashboard from design"
```

## MCP Server Mode

Claudish can run as an MCP (Model Context Protocol) server, exposing OpenRouter models as tools that Claude Code can call mid-conversation. This is useful when you want to:

- Query external models without spawning a subprocess
- Compare responses from multiple models
- Use specific models for specific subtasks

### Starting MCP Server

```bash
# Start MCP server (stdio transport)
claudish --mcp
```

### Claude Code Configuration

Add to `~/.claude/settings.json`:

```json
{
  "mcpServers": {
    "claudish": {
      "command": "claudish",
      "args": ["--mcp"],
      "env": {
        "OPENROUTER_API_KEY": "sk-or-v1-..."
      }
    }
  }
}
```

Or use npx (no installation needed):

```json
{
  "mcpServers": {
    "claudish": {
      "command": "npx",
      "args": ["claudish@latest", "--mcp"]
    }
  }
}
```

### Available MCP Tools

| Tool | Description | Example Use |
|------|-------------|-------------|
| `run_prompt` | Execute prompt on any model | Get a second opinion from Grok |
| `list_models` | Show recommended models | Find models with tool support |
| `search_models` | Fuzzy search all models | Find vision-capable models |
| `compare_models` | Run same prompt on multiple models | Compare reasoning approaches |

### Using MCP Tools from Claude Code

Once configured, Claude Code can use these tools directly:

```
User: "Use Grok to review this code"
Claude: [calls run_prompt tool with model="x-ai/grok-code-fast-1"]

User: "What models support vision?"
Claude: [calls search_models tool with query="vision"]

User: "Compare how GPT-5 and Gemini explain this concept"
Claude: [calls compare_models tool with models=["openai/gpt-5.3", "google/gemini-3-pro-preview"]]
```

### MCP vs CLI Mode

| Feature | CLI Mode | MCP Mode |
|---------|----------|----------|
| Use case | Replace Claude Code model | Call models as tools |
| Context | Full Claude Code session | Single prompt/response |
| Streaming | Full streaming | Buffered response |
| Best for | Primary model replacement | Second opinions, comparisons |

### MCP Tool Details

**run_prompt**
```typescript
{
  model: string,        // e.g., "x-ai/grok-code-fast-1"
  prompt: string,       // The prompt to send
  system_prompt?: string,  // Optional system prompt
  max_tokens?: number   // Default: 4096
}
```

**list_models**
```typescript
// No parameters - returns curated list of recommended models
{}
```

**search_models**
```typescript
{
  query: string,   // e.g., "grok", "vision", "free"
  limit?: number   // Default: 10
}
```

**compare_models**
```typescript
{
  models: string[],      // e.g., ["openai/gpt-5.3", "x-ai/grok-code-fast-1"]
  prompt: string,        // Prompt to send to all models
  system_prompt?: string // Optional system prompt
}
```

## Getting Model List

### JSON Output (Recommended)

```bash
claudish --list-models --json
```

**Output:**
```json
{
  "version": "1.8.0",
  "lastUpdated": "2025-11-19",
  "source": "https://openrouter.ai/models",
  "models": [
    {
      "id": "x-ai/grok-code-fast-1",
      "name": "Grok Code Fast 1",
      "description": "Ultra-fast agentic coding",
      "provider": "xAI",
      "category": "coding",
      "priority": 1,
      "pricing": {
        "input": "$0.20/1M",
        "output": "$1.50/1M",
        "average": "$0.85/1M"
      },
      "context": "256K",
      "supportsTools": true,
      "supportsReasoning": true
    }
  ]
}
```

### Parse in TypeScript

```typescript
const { stdout } = await Bash("claudish --list-models --json");
const data = JSON.parse(stdout);

// Get all model IDs
const modelIds = data.models.map(m => m.id);

// Get coding models
const codingModels = data.models.filter(m => m.category === "coding");

// Get cheapest model
const cheapest = data.models.sort((a, b) =>
  parseFloat(a.pricing.average) - parseFloat(b.pricing.average)
)[0];
```

## JSON Output Format

When using `--json` flag, Claudish returns:

```json
{
  "result": "AI response text",
  "total_cost_usd": 0.068,
  "usage": {
    "input_tokens": 1234,
    "output_tokens": 5678
  },
  "duration_ms": 12345,
  "num_turns": 3,
  "modelUsage": {
    "x-ai/grok-code-fast-1": {
      "inputTokens": 1234,
      "outputTokens": 5678
    }
  }
}
```

**Extract fields:**
```bash
claudish --json "task" | jq -r '.result'          # Get result text
claudish --json "task" | jq -r '.total_cost_usd'  # Get cost
claudish --json "task" | jq -r '.usage'           # Get token usage
```

## Error Handling

### Check Claudish Installation

```typescript
try {
  await Bash("which claudish");
} catch (error) {
  console.error("Claudish not installed. Install with: npm install -g claudish");
  // Use fallback (embedded Claude models)
}
```

### Check API Key

```typescript
const apiKey = process.env.OPENROUTER_API_KEY;
if (!apiKey) {
  console.error("OPENROUTER_API_KEY not set. Get key at: https://openrouter.ai/keys");
  // Use fallback
}
```

### Handle Model Errors

```typescript
try {
  const result = await Bash("claudish --model x-ai/grok-code-fast-1 'task'");
} catch (error) {
  if (error.message.includes("Model not found")) {
    console.error("Model unavailable. Listing alternatives...");
    await Bash("claudish --list-models");
  } else {
    console.error("Claudish error:", error.message);
  }
}
```

### Graceful Fallback

```typescript
async function runWithClaudishOrFallback(task: string) {
  try {
    // Try Claudish with Grok
    const result = await Bash(`claudish --model x-ai/grok-code-fast-1 "${task}"`);
    return result.stdout;
  } catch (error) {
    console.warn("Claudish unavailable, using embedded Claude");
    // Run with standard Claude Code
    return await runWithEmbeddedClaude(task);
  }
}
```

## Cost Tracking

### View Cost in Status Line

Claudish shows cost in Claude Code status line:
```
directory • x-ai/grok-code-fast-1 • $0.12 • 67%
```

### Get Cost from JSON

```bash
COST=$(claudish --json "task" | jq -r '.total_cost_usd')
echo "Task cost: \$${COST}"
```

### Track Cumulative Costs

```typescript
let totalCost = 0;

for (const task of tasks) {
  const result = await Bash(`claudish --json --model grok "${task}"`);
  const data = JSON.parse(result.stdout);
  totalCost += data.total_cost_usd;
}

console.log(`Total cost: $${totalCost.toFixed(4)}`);
```

## Best Practices Summary

### ✅ DO

1. **Use file-based pattern** for sub-agents to avoid context pollution
2. **Choose appropriate model** for task (Grok=speed, GPT-5=reasoning, Qwen=vision)
3. **Use --json output** for automation and parsing
4. **Handle errors gracefully** with fallbacks
5. **Track costs** when running multiple tasks
6. **Update models regularly** with `--force-update`
7. **Use --stdin** for large prompts (git diffs, code review)

### ❌ DON'T

1. **Don't run Claudish directly** in main conversation (pollutes context)
2. **Don't ignore model selection** (different models have different strengths)
3. **Don't parse text output** (use --json instead)
4. **Don't hardcode model lists** (query dynamically)
5. **Don't skip error handling** (Claudish might not be installed)
6. **Don't return full output** in sub-agents (summary only)

## Quick Reference Commands

```bash
# Installation
npm install -g claudish

# Get models
claudish --list-models --json

# Run task
claudish --model x-ai/grok-code-fast-1 "your task"

# Large prompt
git diff | claudish --stdin --model google/gemini-2.5-flash "review"

# JSON output
claudish --json --model grok "task" | jq -r '.total_cost_usd'

# Update models
claudish --list-models --force-update

# Get help
claudish --help
```

## Example: Complete Sub-Agent Implementation

```typescript
/**
 * Example: Implement feature with Claudish + Grok
 * Returns summary only, full implementation in file
 */
async function implementFeatureWithGrok(description: string): Promise<string> {
  const timestamp = Date.now();
  const instructionFile = `/tmp/claudish-implement-${timestamp}.md`;
  const resultFile = `/tmp/claudish-result-${timestamp}.md`;

  try {
    // 1. Create instruction
    const instruction = `# Feature Implementation

## Description
${description}

## Requirements
- Clean, maintainable code
- Comprehensive tests
- Error handling
- Documentation

## Output File
${resultFile}

## Format
\`\`\`markdown
## Files Modified
- path/to/file1.ts
- path/to/file2.ts

## Summary
[2-3 sentence summary]

## Tests Added
- test description 1
- test description 2
\`\`\`
`;

    await Write({ file_path: instructionFile, content: instruction });

    // 2. Run Claudish
    await Bash(`claudish --model x-ai/grok-code-fast-1 --stdin < ${instructionFile}`);

    // 3. Read result
    const result = await Read({ file_path: resultFile });

    // 4. Extract summary
    const filesMatch = result.match(/## Files Modified\s*\n(.*?)(?=\n##|$)/s);
    const files = filesMatch ? filesMatch[1].trim().split('\n').length : 0;

    const summaryMatch = result.match(/## Summary\s*\n(.*?)(?=\n##|$)/s);
    const summary = summaryMatch ? summaryMatch[1].trim() : "Implementation completed";

    // 5. Clean up
    await Bash(`rm ${instructionFile} ${resultFile}`);

    // 6. Return concise summary
    return `✅ Feature implemented. Modified ${files} files. ${summary}`;

  } catch (error) {
    // 7. Handle errors
    console.error("Claudish implementation failed:", error.message);

    // Clean up if files exist
    try {
      await Bash(`rm -f ${instructionFile} ${resultFile}`);
    } catch {}

    return `❌ Implementation failed: ${error.message}`;
  }
}
```

## Additional Resources

- **Full Documentation:** `<claudish-install-path>/README.md`
- **Skill Document:** `skills/claudish-usage/SKILL.md` (in repository root)
- **Model Integration:** `skills/claudish-integration/SKILL.md` (in repository root)
- **OpenRouter Docs:** https://openrouter.ai/docs
- **Claudish GitHub:** https://github.com/MadAppGang/claude-code

## Get This Guide

```bash
# Print this guide
claudish --help-ai

# Save to file
claudish --help-ai > claudish-agent-guide.md
```

---

**Version:** 2.2.0
**Last Updated:** January 22, 2026
**Maintained by:** MadAppGang


================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to [Claudish](https://github.com/MadAppGang/claudish).

## [7.0.3] - 2026-04-21

### Bug Fixes

- inherit parent CWD so models can access the repo *(team)* ([`00a692a`](https://github.com/MadAppGang/claudish/commit/00a692a7c698cbd09a0320df65123d771d73fbf5))
- align OAuth flow with opencode for successful ChatGPT login *(codex)* ([`ceb5074`](https://github.com/MadAppGang/claudish/commit/ceb50743981b026c01e621649c71e9170c305041))
- detect in-stream error payloads from anthropic-compat providers (#106) *(anthropic-sse)* ([`9deb528`](https://github.com/MadAppGang/claudish/commit/9deb5286ecf0829e71a5d1de149dcc83a4b3ab8d))
- back interactive model picker with Firebase catalog([`b5f0e49`](https://github.com/MadAppGang/claudish/commit/b5f0e49caba6740367bc345346e31b08cf4d6bbe))

### Documentation

- update CHANGELOG.md for v7.0.1([`0ee1c1e`](https://github.com/MadAppGang/claudish/commit/0ee1c1e66c16149ebd202f5723a0ae160d748f6b))

### New Features

- --advisor flag for multi-model advisor tool replacement *(advisor)* ([`460bfd0`](https://github.com/MadAppGang/claudish/commit/460bfd01e166392e9b1693678b469735302d5068))
- enable OAuth authentication for ChatGPT Plus/Pro subscriptions *(codex)* ([`7098992`](https://github.com/MadAppGang/claudish/commit/709899215ba16afaa296fca2eb37afbad159b6b3))

### Other Changes

- release v7.0.3([`e898715`](https://github.com/MadAppGang/claudish/commit/e8987155ea634ddb84505832bfe9592c1316ddb3))

## [7.0.1] - 2026-04-16

### Bug Fixes

- filter thinking blocks from MiniMax SSE to prevent leaking internal reasoning *(minimax)* ([`bd9bd85`](https://github.com/MadAppGang/claudish/commit/bd9bd85b122c5fbade05b619e5571cc5109a96fa))
- address edge cases in PR #103 interactive-mode detection([`8932edf`](https://github.com/MadAppGang/claudish/commit/8932edfb733ebcd602154d3487db142804cc5e1e))
- default to interactive mode when only flags are passed (no prompt) (#103)([`cba30c9`](https://github.com/MadAppGang/claudish/commit/cba30c936b0afa82920b9e1e8c05a61dbaad0842))
- rewrite parser for restructured pricing page *(google-scraper)* ([`473d539`](https://github.com/MadAppGang/claudish/commit/473d539bb3ffa954735ccfb7e9e8bafe9fc29fda))

### Documentation

- update all documentation for v7.0.0 release([`297a797`](https://github.com/MadAppGang/claudish/commit/297a797d70bfb8b2f4bd90e77beeb71d9ef67911))
- update CHANGELOG.md for v7.0.0([`75fce0a`](https://github.com/MadAppGang/claudish/commit/75fce0a2d54e5a12b6ee6b992d59dad2b4bfa36a))

### Refactoring

- move model catalog system to models-index repo([`cb75290`](https://github.com/MadAppGang/claudish/commit/cb75290e836acc0059b13ee69ab7c177dc553e3e))

## [7.0.0] - 2026-04-16

### Documentation

- update CHANGELOG.md for v6.14.0([`8f18ec2`](https://github.com/MadAppGang/claudish/commit/8f18ec21e67babcebab862f49e2dade859d1f44c))

### New Features

- v7.0.0 — configurable default provider, custom endpoints([`c5ae212`](https://github.com/MadAppGang/claudish/commit/c5ae2127aee0f27d3d226958490741460f7a88e2))

### Other Changes

- add opt-in advisor-tool swap module *(experiment)* ([`fda7852`](https://github.com/MadAppGang/claudish/commit/fda78525727262baf75e5a99f298e77244915ebc))

## [6.14.0] - 2026-04-15

### New Features

- v6.14.0 — Firebase-only catalog, semantic search, --list-providers([`95684ae`](https://github.com/MadAppGang/claudish/commit/95684ae540a4cdc049a7a6cee19dfa41d6790cf7))

## [6.13.3] - 2026-04-15

### Bug Fixes

- gate consent prompt while Claude Code owns TTY (#85, #88, #99) *(telemetry)* ([`72f4460`](https://github.com/MadAppGang/claudish/commit/72f4460958a85a4c2c85179b3bfbed8013aecd15))

### Documentation

- reflect ?catalog=top100, slim PublicModel projection, search fix *(api)* ([`bdcef63`](https://github.com/MadAppGang/claudish/commit/bdcef63d9f5444753c34cd0af3ce1f979ba76298))
- update CHANGELOG.md for v6.13.2([`688e483`](https://github.com/MadAppGang/claudish/commit/688e4833774e2cb5efc37ea7e12800e1b8d1bec7))

### New Features

- slim public API — strip internal provenance from responses *(firebase)* ([`d21c2c9`](https://github.com/MadAppGang/claudish/commit/d21c2c9f4f1002fc321a83e4401506f77acf94ce))
- add ?catalog=top100 endpoint + fix search ordering bug *(firebase)* ([`f71f9ef`](https://github.com/MadAppGang/claudish/commit/f71f9eff6eaf0f308980ef947bb0977332eb99ef))

### Other Changes

- v6.13.3 — fix interactive stdin race (#85, #88, #99) *(release)* ([`ec01715`](https://github.com/MadAppGang/claudish/commit/ec0171581b09fe3cf33362c7a5e7fa4c43b57020))

### Refactoring

- align manual trigger alert paths with scheduled cron *(catalog)* ([`16379d9`](https://github.com/MadAppGang/claudish/commit/16379d9941844b80c3593b6b8ff7d8efb53d1475))

## [6.13.2] - 2026-04-15

### Bug Fixes

- stream format priority — explicit adapter wins over model dialect *(#102)* ([`a0b15a9`](https://github.com/MadAppGang/claudish/commit/a0b15a97e0586d2fea09c98bdf7fb4591ee6fd82))
- thread Slack webhook as parameter, not process.env *(recommender)* ([`0fddebd`](https://github.com/MadAppGang/claudish/commit/0fddebd69db249bb627be2d34d0eb6370d3ac677))
- centralize all-models.json through v2 helpers *(cache)* ([`157c580`](https://github.com/MadAppGang/claudish/commit/157c580e46f9ec144eecea2721a182b1ce29a736))
- #102 GLM stream parser + structural prevention + #85/88/99 stdin cleanup([`f876e79`](https://github.com/MadAppGang/claudish/commit/f876e7916979cbae1db7ba5bdf57f19d4b37ebb3))

### Documentation

- update API reference for recommender v2.0 (S1-S7 refactor)([`a68735f`](https://github.com/MadAppGang/claudish/commit/a68735f5b12ef09c2790ecae29a8d80bea563cbe))
- update CHANGELOG.md for v6.13.1([`ae86f4f`](https://github.com/MadAppGang/claudish/commit/ae86f4f0f18b2f1d16a577ef6b413228e3a162f4))

### New Features

- v6.13.2 — fix #102 GLM/Z.AI 0-byte output + #85/88/99 stdin cleanup([`c959d0e`](https://github.com/MadAppGang/claudish/commit/c959d0e37dce1ce9d7317bcdfaafcdd4d6ade419))
- add aggregators[] field to ModelDoc and slim catalog *(firebase)* ([`8a08535`](https://github.com/MadAppGang/claudish/commit/8a08535ceb3fa941e9859adea0926e804728425b))
- runtime-registered custom endpoints *(providers)* ([`1451aea`](https://github.com/MadAppGang/claudish/commit/1451aea57448417e44d64e1a7d2ccf2d7a8ee789))
- demote LiteLLM from hardcoded priority *(routing)* ([`5a0d294`](https://github.com/MadAppGang/claudish/commit/5a0d294f63203e068da5e4e241dd56d9ea509964))
- add defaultProvider key + customEndpoints schemas *(config)* ([`12ff0b1`](https://github.com/MadAppGang/claudish/commit/12ff0b110cedef365dd6146550f0afb2f3af573c))

## [6.13.1] - 2026-04-14

### Bug Fixes

- reject category headings as model IDs *(google-scraper)* ([`0582413`](https://github.com/MadAppGang/claudish/commit/058241372fe2263654ad9f165ceb9ed523cf5613))
- set en-US locale headers on every page *(browserbase)* ([`ed93c11`](https://github.com/MadAppGang/claudish/commit/ed93c1180f22aa6a1484c3905aa1cb3b1eac4f50))
- retry up to 3 times on empty response *(qwen-scraper)* ([`4fb6716`](https://github.com/MadAppGang/claudish/commit/4fb6716d87a87ee80fb51f4cd80be646184df682))

### Documentation

- update CHANGELOG.md for v6.13.0([`f66d397`](https://github.com/MadAppGang/claudish/commit/f66d397fcc69d7f014e4b7b78c7d4c23b935b23b))

### New Features

- v6.13.1 — magmux IPC integration + e2e tests([`26c7a29`](https://github.com/MadAppGang/claudish/commit/26c7a29efda8c1171c36abeae93ef84627bb825e))

### Other Changes

- gitignore local dev test scripts in firebase/functions([`a0776f0`](https://github.com/MadAppGang/claudish/commit/a0776f0490246829791d80636e1b7fb3b52ded23))

### Refactoring

- delegate all lifecycle tracking to magmux *(team-grid)* ([`168c814`](https://github.com/MadAppGang/claudish/commit/168c814db601da2976b48dd752dea5a319bd2bba))

## [6.13.0] - 2026-04-14

### Bug Fixes

- restore scroll+click that actually triggers render *(qwen-scraper)* ([`42a17d8`](https://github.com/MadAppGang/claudish/commit/42a17d8c24be0d220c20637ca6b2a883f2aa2cfe))
- wait for JS-rendered content, not a blind setTimeout *(browserbase)* ([`8e273f6`](https://github.com/MadAppGang/claudish/commit/8e273f6a715ea95d2e39d2bf7026d48e98ce08df))
- click International tab before scraping *(qwen-scraper)* ([`b04861e`](https://github.com/MadAppGang/claudish/commit/b04861e48adf7b967a6fa23b215af705120b6180))
- diff gate ignores category recategorization *(recommender)* ([`c174797`](https://github.com/MadAppGang/claudish/commit/c17479761e10d3f33b564c3e567cc337cd25baa0))
- parseVersion strips parameter-count suffixes *(recommender)* ([`32d3307`](https://github.com/MadAppGang/claudish/commit/32d33072f753e11d891ac4214cdff407d4772443))
- date-stamp handling + missing provider aliases *(firebase/recommender)* ([`760b6db`](https://github.com/MadAppGang/claudish/commit/760b6dbd45ff9be8052734db4ef9fcfe841e3798))
- fix 6 cron output issues — vendor prefix, model selection, timeouts *(recommender)* ([`6ba9043`](https://github.com/MadAppGang/claudish/commit/6ba90430281193bfadf991f43cf4408621064511))

### Documentation

- add API reference for Firebase endpoints, MCP tools, and schemas([`5f38f08`](https://github.com/MadAppGang/claudish/commit/5f38f08ceeb5182a6dcec23ecbc8c0fd8e20c322))
- update CHANGELOG.md for v6.12.3([`a39970f`](https://github.com/MadAppGang/claudish/commit/a39970fae6f188df954542730bf533abf522c00e))

### New Features

- interactive TUI with bordered result cards *(probe)* ([`22865e7`](https://github.com/MadAppGang/claudish/commit/22865e77be0c65a1b8f9a97b84c33ff84f74340a))
- lexical modality fallback in isCodingCandidate *(firebase/recommender)* ([`cdcafc6`](https://github.com/MadAppGang/claudish/commit/cdcafc6733a86cb0046fe2990483e08dd900dfa6))
- deterministic version-aware picker *(firebase/recommender)* ([`1eb5808`](https://github.com/MadAppGang/claudish/commit/1eb580831785283dab5e12d3d2c8bd20f8cda891))
- pre-publish diff gate and provider-drop alerts *(firebase/recommender)* ([`42c2b82`](https://github.com/MadAppGang/claudish/commit/42c2b825fe5d8e33936aa104e36c82ce76ecaf9d))
- add one-off cleanupStalePrefixedDocs migration endpoint *(cleanup)* ([`a6fdbbf`](https://github.com/MadAppGang/claudish/commit/a6fdbbf7f1ca3bb4b64f0fc5f733aff2c2a61982))
- --probe sends real 1-token requests to validate each provider([`f843f3e`](https://github.com/MadAppGang/claudish/commit/f843f3e1ed0e553e9303e9bb2f44ae459436dcf4))

### Other Changes

- clean up unused symbols after S1-S7 refactor *(firebase)* ([`be07e5a`](https://github.com/MadAppGang/claudish/commit/be07e5ac3f26e9a33a6ff0fc6ac70f271cc41a16))

### Refactoring

- remove tab-click, rely on en-US locale *(qwen-scraper)* ([`00b2bc1`](https://github.com/MadAppGang/claudish/commit/00b2bc147d2a0333f648f1e65a87c84fa3d5e998))
- install schema gate at RawModel ingress *(firebase/recommender)* ([`656e37a`](https://github.com/MadAppGang/claudish/commit/656e37a5a156ab061a8627aea77d84156c3a5164))

## [6.12.3] - 2026-04-11

### Bug Fixes

- make codesign verification non-fatal for Bun binaries([`2cfbccb`](https://github.com/MadAppGang/claudish/commit/2cfbccb727058b7b55119daf7945242f743e0bc9))
- Qwen pricing scraper, stale doc cleanup, xAI alias fix([`0468eae`](https://github.com/MadAppGang/claudish/commit/0468eaed19fa57e62f30ba66debc080a9f832144))
- stale doc cleanup + xAI alias resolution for correct model IDs([`343e619`](https://github.com/MadAppGang/claudish/commit/343e61952b26ba5e23accac5a61a98b4a811ea8e))

### Documentation

- update CHANGELOG.md for v6.12.2([`9e89555`](https://github.com/MadAppGang/claudish/commit/9e895558e81449660f096c47d0d35e9f195f60c2))

### New Features

- v6.12.3 — Browserbase integration for JS-rendered pricing pages([`b2e2ccc`](https://github.com/MadAppGang/claudish/commit/b2e2ccc01a841320955f2c0ae78b86f8211d8b68))
- add Qwen pricing scraper from Alibaba Cloud Model Studio docs([`f9fe44d`](https://github.com/MadAppGang/claudish/commit/f9fe44d3e7054847696759953ed456380a52eeea))

### Other Changes

- add gitignore for magmux binaries and team session dirs([`89291a3`](https://github.com/MadAppGang/claudish/commit/89291a31cb1785bdc9e4d7d4db1f3722c7efad61))

### Refactoring

- remove local magmux source, use upstream releases([`e1f8dd1`](https://github.com/MadAppGang/claudish/commit/e1f8dd1556d33d220385dfb4df2ff2894178f386))

## [6.12.2] - 2026-04-10

### Bug Fixes

- v6.12.2 — team orchestrator race conditions and test hardening([`302e3f3`](https://github.com/MadAppGang/claudish/commit/302e3f372f0be1961175ea217b07e576a3262e2c))
- use official pricing from provider docs, not aggregator prices([`0e8bc48`](https://github.com/MadAppGang/claudish/commit/0e8bc480790d92763b49f5cc99f619b8d370fa53))

### Documentation

- update CHANGELOG.md for v6.12.1([`21c5fc0`](https://github.com/MadAppGang/claudish/commit/21c5fc07cca05040097f18f5c9e7dcac92280767))

## [6.12.1] - 2026-04-10

### Bug Fixes

- v6.12.1 — fix xAI pricing conversion (was 100x too low)([`871e957`](https://github.com/MadAppGang/claudish/commit/871e95727fc18bf55963819c2b081a7f5ef952f9))
- close remaining race conditions in team-orchestrator *(team)* ([`832cbb7`](https://github.com/MadAppGang/claudish/commit/832cbb7e96e01eaca8564cdb42db400a2026a8e3))

### Documentation

- update CHANGELOG.md for v6.12.0([`107e843`](https://github.com/MadAppGang/claudish/commit/107e8439cea41cc248677714c4d14e97ed1fafb6))

## [6.12.0] - 2026-04-09

### Documentation

- update CHANGELOG.md for v6.11.1([`d89cddd`](https://github.com/MadAppGang/claudish/commit/d89cdddd5ad2004356e7727ad0898e7ef39bc0e7))

### New Features

- v6.12.0 — new API collectors, error report ingest, auto-recommender, team timeout fix([`e940c79`](https://github.com/MadAppGang/claudish/commit/e940c79a60fa3ab74dbf98ac6e0f657b6f9063ef))

## [6.11.1] - 2026-04-08

### Bug Fixes

- v6.11.1 — fix OAuth login in bundled dist, model catalog improvements([`73cff9c`](https://github.com/MadAppGang/claudish/commit/73cff9caa24818935fce2304c77756c7f13639b9))

### Documentation

- update CHANGELOG.md for v6.11.0([`f6a4ce0`](https://github.com/MadAppGang/claudish/commit/f6a4ce09af964a2df6f1dee5f83fc0ddd26f7a04))

## [6.11.0] - 2026-04-07

### Bug Fixes

- remove uncommitted warmRecommendedModels import that breaks CI([`b4265ff`](https://github.com/MadAppGang/claudish/commit/b4265ff66e0c52eac57c513eee15a0f65e39dd3a))

### Documentation

- update CHANGELOG.md for v6.10.1([`8233ae5`](https://github.com/MadAppGang/claudish/commit/8233ae5cfc20c2e802b1239856c2337ec9d65c57))

### New Features

- v6.11.0 — Anthropic error format, SSE pings, web search detection([`a249eb4`](https://github.com/MadAppGang/claudish/commit/a249eb4a2e86ec2b3a023a2183d7a3a7b76fb0a7))

## [6.10.1] - 2026-04-07

### Documentation

- update CHANGELOG.md for v6.10.0([`aaf24f2`](https://github.com/MadAppGang/claudish/commit/aaf24f21df44867cf42770202d0d7ee0a0cd0033))

### New Features

- v6.10.1 — auto-update with changelog, single version source of truth([`de889eb`](https://github.com/MadAppGang/claudish/commit/de889eb6609145bb1a40643101b70236576be1e3))

## [6.10.0] - 2026-04-07

### Documentation

- update CHANGELOG.md for v6.9.1([`714b1b5`](https://github.com/MadAppGang/claudish/commit/714b1b5166662ea3aac3087faad51be0e896fd25))

### New Features

- v6.10.0 — Codex subscription OAuth, unified login/logout, quota registry([`a2dd1ea`](https://github.com/MadAppGang/claudish/commit/a2dd1ea156b96da16ac8021702edf614ce9ebe3d))

## [6.9.1] - 2026-04-06

### Documentation

- update CHANGELOG.md for v6.9.0([`3075035`](https://github.com/MadAppGang/claudish/commit/3075035e28ffc425917f3ccc0680f27f9b860693))

### Other Changes

- bump to v6.9.1 — verify magmux npm publishing([`3384f03`](https://github.com/MadAppGang/claudish/commit/3384f034facf1da80cef0061da7ed4e2d3b5815b))

## [6.9.0] - 2026-04-06

### Documentation

- update CHANGELOG.md for v6.8.1([`9b376b6`](https://github.com/MadAppGang/claudish/commit/9b376b6eb588441bcaf165764c41052303598bc2))

### New Features

- v6.9.0 — model catalog overhaul, team grid mode, Slack alerts([`de0b815`](https://github.com/MadAppGang/claudish/commit/de0b81554206fc3072f6e74549a3699220c2862e))

## [6.8.1] - 2026-04-06

### Documentation

- update CHANGELOG.md for v6.8.0([`d72520d`](https://github.com/MadAppGang/claudish/commit/d72520db1264cf6799a9c470f5fc94d1e86fe3a3))

### New Features

- platform-specific magmux npm packages + stripped binaries([`efd6bba`](https://github.com/MadAppGang/claudish/commit/efd6bba4dd71f3ae34e9868501d10941a10b9258))

### Other Changes

- bump to v6.8.1 — platform-specific magmux packages([`a03e995`](https://github.com/MadAppGang/claudish/commit/a03e99558e06c1bae0bdfb485d471716b1bbe785))

## [6.8.0] - 2026-04-06

### Documentation

- update CHANGELOG.md for v6.7.0([`57d6ae5`](https://github.com/MadAppGang/claudish/commit/57d6ae522dc11f9d3c9c08e0c78fca12817f745b))

### New Features

- v6.8.0 — add DeepSeek as native direct API provider([`a833000`](https://github.com/MadAppGang/claudish/commit/a833000d59d3a4ce5d610201bf967ea867dd9ead))

## [6.7.0] - 2026-04-06

### Documentation

- update CHANGELOG.md for v6.6.3([`dd7e6fb`](https://github.com/MadAppGang/claudish/commit/dd7e6fbe9d47df1ba63d4bfc30436ddbd7429c31))

### New Features

- v6.7.0 — replace mtm with magmux, improve catalog resolver, add OAuth manager([`6759005`](https://github.com/MadAppGang/claudish/commit/675900567be9f139aece1f674ed8f6880843bd89))

## [6.6.3] - 2026-04-06

### Bug Fixes

- handle magmux artifact names in release file preparation *(ci)* ([`c8aca08`](https://github.com/MadAppGang/claudish/commit/c8aca08575f3265c869ca85b7b79f04dad83f2a3))
- v6.6.3 — reject sentinel model names in team orchestrator([`e485263`](https://github.com/MadAppGang/claudish/commit/e485263cfdd99aeda77b195fb7de572274c355ce))
- reject sentinel model names in team orchestrator *(team)* ([`91ee9a8`](https://github.com/MadAppGang/claudish/commit/91ee9a811fb821dbd1f01214cdbfd977017ed96f))

### Documentation

- update CHANGELOG.md for v6.6.2([`4c071a6`](https://github.com/MadAppGang/claudish/commit/4c071a69e105daf92fb2967392b0637d1129074c))

## [6.6.2] - 2026-04-06

### Bug Fixes

- use Node 24 + always-auth for npm OIDC trusted publishing *(ci)* ([`9cfb12a`](https://github.com/MadAppGang/claudish/commit/9cfb12a86d21961fe01ec07894a144ac2af49230))
- remove FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 from publish-npm *(ci)* ([`f44750d`](https://github.com/MadAppGang/claudish/commit/f44750df739616e942418ef4b9bc22124e89ccde))
- use Node 20 for npm publish — Node 22.22.2 npm is broken *(ci)* ([`0414155`](https://github.com/MadAppGang/claudish/commit/0414155ef090a8a2cd1ed3cb5b40d6d417c9ecfd))
- use npm@11 for OIDC publish compatibility *(ci)* ([`f0a746e`](https://github.com/MadAppGang/claudish/commit/f0a746edb08219210f0628d0a119f4fdd14791a3))
- v6.6.2 — Gemini image translation, CI npm fix([`bba0327`](https://github.com/MadAppGang/claudish/commit/bba03275bbfaf9cb8448eff00723d800d2094341))

### Documentation

- update CHANGELOG.md for v6.6.2([`dba5006`](https://github.com/MadAppGang/claudish/commit/dba5006456b9d9d6dc16e7581b95c206c9b71dce))
- update CHANGELOG.md for v6.6.2([`84a403b`](https://github.com/MadAppGang/claudish/commit/84a403b8c27326ea975668d5ae5ce6e22ddd7863))
- update CHANGELOG.md for v6.6.2([`ade7e09`](https://github.com/MadAppGang/claudish/commit/ade7e0933686c4f045916d52bc1780f4d511f25b))
- update CHANGELOG.md for v6.6.2([`fe30c6b`](https://github.com/MadAppGang/claudish/commit/fe30c6b56f0243da48c726baca7b0f6544d154f8))
- update CHANGELOG.md for v6.6.1([`5fd634b`](https://github.com/MadAppGang/claudish/commit/5fd634b40022fd2b8d332372db9091a1ab5119b5))

## [6.6.1] - 2026-04-06

### Bug Fixes

- v6.6.1 — OpenAI schema compatibility for bare object MCP tools([`8fe7373`](https://github.com/MadAppGang/claudish/commit/8fe73736d7f3a5d07ede283e407e7a5889f9a1ca))
- ensure properties:{} on bare object schemas for OpenAI compatibility([`99d3e73`](https://github.com/MadAppGang/claudish/commit/99d3e732f82e776a4d3d809666f95233c206fb55))
- quota bar without pill bg — add lowercase color codes to magmux([`d029001`](https://github.com/MadAppGang/claudish/commit/d0290013c04248ee593b88388fa257827b694f5e))

### Documentation

- update CHANGELOG.md for v6.6.0([`2bf5e9a`](https://github.com/MadAppGang/claudish/commit/2bf5e9a6b962e4b1bc15afc46702a62f10f4c9c0))

## [6.6.0] - 2026-04-01

### Bug Fixes

- cleaner status bar — remove ok pill, provider as plain text, mini quota bar([`a9ad5be`](https://github.com/MadAppGang/claudish/commit/a9ad5be2098dad03932b5e31e439553f93436f09))

### Documentation

- update CHANGELOG.md for v6.6.0([`5d186cb`](https://github.com/MadAppGang/claudish/commit/5d186cb84dfe695938c6e7f3d75a8e3d5b888798))
- update CHANGELOG.md for v6.5.3([`76e4df5`](https://github.com/MadAppGang/claudish/commit/76e4df586c651289b17196366cd4f5711a320058))

### New Features

- magmux v0.3.0 — grid mode, status bar, socket IPC, tint overlays([`4bbbce2`](https://github.com/MadAppGang/claudish/commit/4bbbce21f341405009ee06baac0a66e7c3c7245d))

## [6.5.3] - 2026-04-01

### Bug Fixes

- quota display in status bar — strip provider prefix, await fetch, rewrite token file([`b026b2f`](https://github.com/MadAppGang/claudish/commit/b026b2ff3d2a3b95530f3136e125971177315508))

### Documentation

- update CHANGELOG.md for v6.5.2([`67d4181`](https://github.com/MadAppGang/claudish/commit/67d418143f2ee718ee425ce7a26d6f32fb3e2f8d))

### Other Changes

- bump to v6.5.3([`1eafee8`](https://github.com/MadAppGang/claudish/commit/1eafee81943eb2d45ee552de3184935f8365205a))

## [6.5.2] - 2026-04-01

### Bug Fixes

- poll token file for provider/quota in magmux status bar([`15adbb4`](https://github.com/MadAppGang/claudish/commit/15adbb488a85d9b8827ad4b4dc1bb776c8c52647))

### Documentation

- update CHANGELOG.md for v6.5.1([`6f31af7`](https://github.com/MadAppGang/claudish/commit/6f31af73460921abcc3d6a896c48f30b0dd36538))

### Other Changes

- bump to v6.5.2([`7b5a267`](https://github.com/MadAppGang/claudish/commit/7b5a2678339b79af1a73c8e18a3bd28de27aca06))

## [6.5.1] - 2026-04-01

### Bug Fixes

- show provider name and quota in claudish status bar([`eb8693c`](https://github.com/MadAppGang/claudish/commit/eb8693c9b60ed3e6e7f007c7061f51918a07733d))

### Documentation

- update CHANGELOG.md for v6.5.0([`ad801f6`](https://github.com/MadAppGang/claudish/commit/ad801f66c7862212752442b455677857301367f2))

### Other Changes

- bump to v6.5.1([`9ed4074`](https://github.com/MadAppGang/claudish/commit/9ed40745d52c7a278faa7a00a15680a2fddfebd7))

## [6.5.0] - 2026-04-01

### Bug Fixes

- magmux set TERM=screen-256color (root cause of all VT issues)([`488cf7e`](https://github.com/MadAppGang/claudish/commit/488cf7e99a18321bdabb146b58e0f81ac39d5321))
- magmux handle Kitty keyboard protocol CSI sequences([`b4b02ff`](https://github.com/MadAppGang/claudish/commit/b4b02ff56261ca01067451dfc12de184f783090c))
- magmux filter CSI intermediate bytes to prevent SGR corruption([`ea6e723`](https://github.com/MadAppGang/claudish/commit/ea6e72339ed2a5a88ef123ba96998d5629c9c61a))
- magmux suppress underline SGR + fix border rendering order([`a1b20b0`](https://github.com/MadAppGang/claudish/commit/a1b20b0f61a0a6638681fe41781784e6eb70e8c9))

### Documentation

- MTM-to-magmux migration guide for claudish developers([`c296671`](https://github.com/MadAppGang/claudish/commit/c2966716e423e4b38efc8728df908825952e00c4))
- add magmux usage guide to claudish documentation([`6ea796d`](https://github.com/MadAppGang/claudish/commit/6ea796dba3f0c5faa31a2f51315e281ab605ce66))
- update CHANGELOG.md for v6.4.6([`84674f5`](https://github.com/MadAppGang/claudish/commit/84674f5c8b6f05a92940531c300f3549091bc9a3))

### New Features

- v6.5.0 — Gemini Code Assist overhaul, auth commands, quota CLI, Codex OAuth([`f9b1c54`](https://github.com/MadAppGang/claudish/commit/f9b1c54682d16cf8684d3ec8ce4b4201cddef59d))
- magmux VT parser — implement tmux-equivalent escape sequence coverage([`c8abea2`](https://github.com/MadAppGang/claudish/commit/c8abea2f2023119f62c7e10def176ffdd87d938f))
- team grid mode — mtm-based multi-model visual display([`3da53f1`](https://github.com/MadAppGang/claudish/commit/3da53f196c90c2790d009af39ea1cf8573e9cc91))

### Performance

- magmux dirty-flag rendering — skip redraws when nothing changed([`7fb0eb3`](https://github.com/MadAppGang/claudish/commit/7fb0eb34e8d69c673c4e649beb5070e1b30e6fde))

## [6.4.6] - 2026-03-30

### Bug Fixes

- v6.4.6 - subcommand routing broken when shell alias prepends flags([`3d40667`](https://github.com/MadAppGang/claudish/commit/3d406677606b9c31b1cc638f017964e5edb2138f))

### Documentation

- update CHANGELOG.md for v6.4.5([`9751770`](https://github.com/MadAppGang/claudish/commit/975177019310c5a07f0fe38b0878e5d101e9aee1))

### New Features

- magmux - Go terminal multiplexer replacing C MTM implementation([`4e436e9`](https://github.com/MadAppGang/claudish/commit/4e436e9380b4c104072fab2cd880154270b9a70c))
- add plugin defaults endpoint for Magus plugin system([`c43d927`](https://github.com/MadAppGang/claudish/commit/c43d9277fca41ffbc28013102094187a90a97103))

## [6.4.5] - 2026-03-28

### Bug Fixes

- v6.4.5 - enforce per-model tool count limits (OpenAI 128 max)([`498a2ed`](https://github.com/MadAppGang/claudish/commit/498a2ede644daa5ed67e7119143ecedfb607f5dc))

### New Features

- v6.4.4 - team-grid orchestrator for parallel multi-model execution([`1971b71`](https://github.com/MadAppGang/claudish/commit/1971b7193aa34e160cee31fd1fc39c0685c0e48a))

## [6.4.3] - 2026-03-28

### Bug Fixes

- v6.4.3 - error reporting hints on all MCP tool failures, mtm grid improvements([`781362b`](https://github.com/MadAppGang/claudish/commit/781362bd9e207145f8458ecf1be955633a5ba2a3))

### Documentation

- update documentation for channel mode and v6.4.2([`db9fcdb`](https://github.com/MadAppGang/claudish/commit/db9fcdb9dc76075a99e06cabdadfed05424c1381))
- update CHANGELOG.md for v6.4.2([`431a473`](https://github.com/MadAppGang/claudish/commit/431a4734c1284d345324ac2d5350dbf47749c19a))

## [6.4.2] - 2026-03-28

### Bug Fixes

- v6.4.2 - channel mode test coverage + scrollback indexOf bug fix([`d2610e8`](https://github.com/MadAppGang/claudish/commit/d2610e880c60a8d1a63f8872178a8f0020be443b))
- add ignoreUndefinedProperties for Firestore writes([`fef0a59`](https://github.com/MadAppGang/claudish/commit/fef0a596427985761c61a4e5b4a3c47567c91db9))

### Documentation

- update CHANGELOG.md for v6.4.1([`7b1e6ec`](https://github.com/MadAppGang/claudish/commit/7b1e6ec921d4c31bddee1af7ef1b1804211f365a))

### New Features

- model catalog collector — Firebase Cloud Functions([`4e97178`](https://github.com/MadAppGang/claudish/commit/4e9717890cc492852a09f6eeb1eefa0ab00ffc3d))

### Other Changes

- change catalog schedule from every 6h to daily at 03:00 UTC([`a1b5d91`](https://github.com/MadAppGang/claudish/commit/a1b5d915a061a72a914d6adbd1dc36e123e211d5))

## [6.4.1] - 2026-03-28

### Bug Fixes

- v6.4.1 - fix mtm underline rendering, use xterm-256color TERM([`dd74640`](https://github.com/MadAppGang/claudish/commit/dd74640b5fea09e891735b4b7661a9bf7f094ba6))
- parseLogMessage regex, mtm rendering artifacts, fallback caching([`199b04e`](https://github.com/MadAppGang/claudish/commit/199b04eaa0851a336b2e789673846625170a4a2b))

### Documentation

- update CHANGELOG.md for v6.4.0([`ba5c7c3`](https://github.com/MadAppGang/claudish/commit/ba5c7c352a29916b1c6b009f7b4e7e0e95e080b6))

## [6.4.0] - 2026-03-27

### Documentation

- update CHANGELOG.md for v6.3.2([`79e9fa4`](https://github.com/MadAppGang/claudish/commit/79e9fa43d4736d2542e07235d85856e006a8cecf))

### New Features

- v6.4.0 - MCP multi-provider routing, channel system, TUI overhaul([`1f667cb`](https://github.com/MadAppGang/claudish/commit/1f667cb4ff646b9200de4407a0ddbd491bfb9479))

## [6.3.2] - 2026-03-25

### Bug Fixes

- v6.3.2 - rebuild mtm binary with -L flag support, remove debug code([`8842ac2`](https://github.com/MadAppGang/claudish/commit/8842ac2277a2b0268d8677e7c4490eb4dce13f42))

### Documentation

- update CHANGELOG.md for v6.3.1([`ec18d6b`](https://github.com/MadAppGang/claudish/commit/ec18d6b4e3f9965b0b1c85320eb1fc807786d557))

## [6.3.1] - 2026-03-25

### Bug Fixes

- v6.3.1 - Gemini Code Assist auth failure falls through to Direct API([`692e207`](https://github.com/MadAppGang/claudish/commit/692e207e0895b20ba9ef07a79d936be6170cca77))
- Gemini Code Assist auth failure now falls through to Google Direct API([`f063aad`](https://github.com/MadAppGang/claudish/commit/f063aade21fc6e6ba1a4b5134a506267a50907e9))

### Documentation

- update CHANGELOG.md for v6.3.0([`8f3bdc4`](https://github.com/MadAppGang/claudish/commit/8f3bdc4245aa4f2f9ba659762936615cafd87d11))

## [6.3.0] - 2026-03-25

### Documentation

- update CHANGELOG.md for v6.3.0([`eb5ac71`](https://github.com/MadAppGang/claudish/commit/eb5ac7172e679fc6cee378288d1b55d0d8ad5e66))
- update CHANGELOG.md for v6.2.2([`6ffafd4`](https://github.com/MadAppGang/claudish/commit/6ffafd4512aa05b8d0c455d907f58db87a6007a0))

### New Features

- expandable diagnostics panel — click status bar or Ctrl-G d to toggle([`42debca`](https://github.com/MadAppGang/claudish/commit/42debca56ae15f19f5e6c39c87b384f7bad1d9e5))
- v6.3.0 - TUI redesign, provider key test, route probe([`207813a`](https://github.com/MadAppGang/claudish/commit/207813acb05637df083613ea14d7e5e0f477bf55))

### Other Changes

- update landing page model names to latest versions (March 2026)([`63f652c`](https://github.com/MadAppGang/claudish/commit/63f652cec86919efbaf167ad9348ea545ab5c3a7))

## [6.2.2] - 2026-03-24

### Bug Fixes

- v6.2.2 - include mtm binary in npm package (CI fix)([`2c50c2c`](https://github.com/MadAppGang/claudish/commit/2c50c2c9c0c5a3f153ef7ae31d7c6c1c8cb3d550))
- include native/mtm binaries in npm publish CI step([`b14e4e0`](https://github.com/MadAppGang/claudish/commit/b14e4e0d29377e058e8b08e283a232a1c6bea48d))

### Documentation

- update CHANGELOG.md for v6.2.1([`fd04d4e`](https://github.com/MadAppGang/claudish/commit/fd04d4ebd8296ac64e0923a99acb1fb4deafa9d1))

## [6.2.1] - 2026-03-24

### Bug Fixes

- v6.2.1 - bundle mtm binary, reject upstream mtm, fix path resolution([`c8df199`](https://github.com/MadAppGang/claudish/commit/c8df199d8efa625870a53a68f8ac6612fb00e1d0))
- add 429 retry with exponential backoff to OpenAI transport (#66)([`9ac8991`](https://github.com/MadAppGang/claudish/commit/9ac8991deaf65e08c85e5100a3fe7dc70130452e))

### Documentation

- update CHANGELOG.md for v6.2.0([`68bf83c`](https://github.com/MadAppGang/claudish/commit/68bf83c6377c595de8452cde07d023870a627d78))

## [6.2.0] - 2026-03-24

### Documentation

- update CHANGELOG.md for v6.1.1([`d0af752`](https://github.com/MadAppGang/claudish/commit/d0af752ae85e69fda091906adc9ef9259089fcd2))

### New Features

- v6.2.0 - isProviderAvailable interface, xAI provider, model selector improvements([`e84dcc6`](https://github.com/MadAppGang/claudish/commit/e84dcc608dc9695b2f48b7d2fbe95cf3288bc070))

## [6.1.1] - 2026-03-24

### Bug Fixes

- v6.1.1 - Zen Go routing, OpenAI schema sanitization, Kimi reasoning_content([`6563f13`](https://github.com/MadAppGang/claudish/commit/6563f13b748387143e1481b3c2feb70d56943056))

### Documentation

- update CHANGELOG.md for v6.1.0([`dfb7abd`](https://github.com/MadAppGang/claudish/commit/dfb7abd476e3d3f402cd0190d52e2141af11cb26))

### New Features

- first-run auto-approve confirmation (#57)([`aff10b2`](https://github.com/MadAppGang/claudish/commit/aff10b27366eeac7202b4227a7d6764b22005f9e))

## [6.1.0] - 2026-03-23

### Bug Fixes

- ad-hoc sign macOS binaries for Gatekeeper compatibility (#73)([`e1eb919`](https://github.com/MadAppGang/claudish/commit/e1eb91930c1ac99427eff77e3c041ce768c7841a))

### Documentation

- update CHANGELOG.md for v6.0.1([`05ae6a2`](https://github.com/MadAppGang/claudish/commit/05ae6a21c4304a86f5186567912a9173224fc527))

### New Features

- v6.1.0 - centralized model catalog and MiniMax Anthropic API fixes([`fa0cf0f`](https://github.com/MadAppGang/claudish/commit/fa0cf0f0e17dda06e34bdd5707bec1c1603ac995))

## [6.0.1] - 2026-03-23

### Bug Fixes

- v6.0.1 - statusline input_tokens and -p flag conflict([`0b46b5f`](https://github.com/MadAppGang/claudish/commit/0b46b5f7253187d1ff1efb5d6c25bae22d37f9b6))
- statusline input_tokens (#74) and -p flag conflict (#76)([`056835c`](https://github.com/MadAppGang/claudish/commit/056835c69d278d4e1e7b42d62d7edbc799c87586))

### Documentation

- update CHANGELOG.md for v6.0.0([`a791d14`](https://github.com/MadAppGang/claudish/commit/a791d14a76c7d1092e864bbe4922114339215051))

## [6.0.0] - 2026-03-22

### Documentation

- update CHANGELOG.md for v5.19.0([`48c12f5`](https://github.com/MadAppGang/claudish/commit/48c12f5f9479bf121ba3763c992b697681591f02))

### New Features

- v6.0.0 - three-layer architecture rename (APIFormat / ModelDialect / ProviderTransport)([`14efceb`](https://github.com/MadAppGang/claudish/commit/14efceb0fdb819f07180bcef7540eab7d7f7fe05))

## [5.19.0] - 2026-03-22

### Bug Fixes

- include missing files for v5.19.0 CI build([`655644d`](https://github.com/MadAppGang/claudish/commit/655644d1f8020063ed00a8cba690922440d0eb3e))
- remove stale tests/ directory and export team-orchestrator helpers([`1608186`](https://github.com/MadAppGang/claudish/commit/1608186681974f18a66bb6de2b4f09f23b1051e5))

### Documentation

- update CHANGELOG.md for v5.18.1([`dfcef8f`](https://github.com/MadAppGang/claudish/commit/dfcef8f46ee4b4d8c2c09819635c82c139362ea7))

### New Features

- v5.19.0 - MCP team orchestrator, error reporting, TUI redesign([`821d348`](https://github.com/MadAppGang/claudish/commit/821d3484fd10b03d8317a91471e5358104f07939))

### Other Changes

- add FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 to all CI jobs([`1524747`](https://github.com/MadAppGang/claudish/commit/15247478063f2ce35ba391badea6aead1e5bf5aa))
- upgrade GitHub Actions to Node.js 24 compatibility([`a2a6aca`](https://github.com/MadAppGang/claudish/commit/a2a6acace88313bef25b50f16948d520c1da12bf))

## [5.18.1] - 2026-03-22

### Documentation

- update CHANGELOG.md for v5.18.0([`3e934c5`](https://github.com/MadAppGang/claudish/commit/3e934c592263e58afb3885c3a4c03d982a004558))

### New Features

- v5.18.1 - API key provenance in debug logs and --probe([`cedd48d`](https://github.com/MadAppGang/claudish/commit/cedd48d22bd26e68a99a43269caeee83c987f073))
- API key provenance tracking in debug logs and --probe (#83)([`c9996a1`](https://github.com/MadAppGang/claudish/commit/c9996a155515e1e4a588d177a7204bee8b442fe8))

## [5.18.0] - 2026-03-21

### Documentation

- update CHANGELOG.md for v5.17.0([`edff2d2`](https://github.com/MadAppGang/claudish/commit/edff2d245726937940f203ec0a74441b9e504ae8))

### New Features

- v5.18.0 - auto-detect Gemini subscription tier on login([`d691140`](https://github.com/MadAppGang/claudish/commit/d691140a36ceae1bb66f8bbc2b7c4621ef86974e))

## [5.17.0] - 2026-03-20

### Bug Fixes

- release.yml heredoc syntax for GitHub Actions YAML parser([`3265a74`](https://github.com/MadAppGang/claudish/commit/3265a748fa2b5e760a6f898635ff71ffb58819f4))

### New Features

- v5.17.0 - automatic changelog generation with git-cliff([`c7caef9`](https://github.com/MadAppGang/claudish/commit/c7caef9987d55d2b0bb3728c77b06cb62925e7ee))

## [5.16.2] - 2026-03-20

### Bug Fixes

- v5.16.2 - target correct tmux pane for diag split([`e328d6b`](https://github.com/MadAppGang/claudish/commit/e328d6bc3fd0de6f95bdb962623ef55d3c5a41bf))

## [5.16.1] - 2026-03-20

### Refactoring

- v5.16.1 - single source of truth for provider definitions, fix adapter matching([`072697b`](https://github.com/MadAppGang/claudish/commit/072697bf7405f6cc47a655b8c0188cb79528efdc))
- single source of truth for provider definitions + fix adapter matching (#82)([`7fb091d`](https://github.com/MadAppGang/claudish/commit/7fb091d1ff4dcd3a7177f1b37f7efa50d4721779))

## [5.16.0] - 2026-03-20

### New Features

- v5.16.0 - DiagOutput for clean diagnostic display([`b8f82d8`](https://github.com/MadAppGang/claudish/commit/b8f82d87dc09aca56fd0945e8e2a8d4f34602ea2))
- DiagOutput — separate claudish diagnostics from Claude Code TUI([`e53b7fc`](https://github.com/MadAppGang/claudish/commit/e53b7fcc46afcd1923fefdbe8aba160dad5069ef))

## [5.15.0] - 2026-03-19

### Bug Fixes

- include team-cli and mcp-server files needed for CI build([`723a1e9`](https://github.com/MadAppGang/claudish/commit/723a1e9ed2a4878d9f0463160221c9388da3e935))
- preserve real auth credentials when native Claude models are in config([`f356328`](https://github.com/MadAppGang/claudish/commit/f356328f302098eb9fb0a69751b0f35021ba8c33))

### Documentation

- update CLAUDE.md with 3-layer architecture and debug-logs workflow([`b8dce83`](https://github.com/MadAppGang/claudish/commit/b8dce83c3f1772f658387943f64e3c8c3eb144d9))

### New Features

- v5.15.0 - XiaomiAdapter, dynamic OpenRouter context windows, fix all hardcoded context sizes([`bff916c`](https://github.com/MadAppGang/claudish/commit/bff916cd27f3e384404d80085174267ea7c340c1))
- always-on structural logging without --debug([`2f1b284`](https://github.com/MadAppGang/claudish/commit/2f1b284e8328146d5c7c96a5af8862992b79bb39))

## [5.14.0] - 2026-03-18

### Bug Fixes

- upgrade MCP SDK to ^1.27.0 to fix Zod 4 tool schema serialization([`951963c`](https://github.com/MadAppGang/claudish/commit/951963cec7880686ac2a71117ecd0fe44abfc88b))
- add ToolSearch to tool-call-recovery inference (#63)([`5a2afcf`](https://github.com/MadAppGang/claudish/commit/5a2afcfb2a3aab1f8d22f84bb04bc3b243444e7a))
- resolve spawn EINVAL on Windows when Claude binary is a .cmd file (#67)([`e511efa`](https://github.com/MadAppGang/claudish/commit/e511efa0f94b01ef36d6955032684184ea9df14d))

### New Features

- v5.14.0 - adapter architecture rearchitecture with 3-layer separation([`871f338`](https://github.com/MadAppGang/claudish/commit/871f3387c6e68dba4b3820aa711aaa6f3bcb3bb2))

## [5.13.4] - 2026-03-18

### Bug Fixes

- v5.13.4 - suppress stderr during interactive Claude Code sessions([`7cdf94d`](https://github.com/MadAppGang/claudish/commit/7cdf94d5b3c842c088ed625de26b62c8d18575d2))

## [5.13.3] - 2026-03-18

### Bug Fixes

- v5.13.3 - clean error display and openrouter/ native prefix support([`af2daec`](https://github.com/MadAppGang/claudish/commit/af2daec0cc6afee0c8b6ac98267e81c16a01df1d))

## [5.13.2] - 2026-03-18

### Bug Fixes

- v5.13.2 - recognize openrouter/ vendor prefix in model parser([`2e3d0fc`](https://github.com/MadAppGang/claudish/commit/2e3d0fc2db673f2446482253185f8af51d11bcf1))

## [5.13.1] - 2026-03-16

### Bug Fixes

- v5.13.1 - use Zen Go (subscription) instead of Zen (credits) in default fallback chain([`b610462`](https://github.com/MadAppGang/claudish/commit/b6104628906722173a311f30c475282b9fc26c4e))

## [5.13.0] - 2026-03-16

### New Features

- v5.13.0 - anonymous usage stats with OTLP format([`ca0d015`](https://github.com/MadAppGang/claudish/commit/ca0d015c4d03f5456b89aac3720605067c38a40b))

## [5.12.3] - 2026-03-16

### Bug Fixes

- v5.12.3 - Node.js launcher with Bun detection([`5c8a99b`](https://github.com/MadAppGang/claudish/commit/5c8a99be6a3ecbc02d9c32ce745cbb45d579ab3b))

## [5.12.2] - 2026-03-16

### Bug Fixes

- v5.12.2 - switch from Node to Bun runtime target([`5e85801`](https://github.com/MadAppGang/claudish/commit/5e858010ff31ee4db2aeadb319a857f676379453))

## [5.12.1] - 2026-03-16

### Bug Fixes

- v5.12.1 - exclude OpenTUI bun:ffi from Node bundle([`a0150ea`](https://github.com/MadAppGang/claudish/commit/a0150ead59f4eb8ad5ede4b610a7a742f7a46790))

## [5.12.0] - 2026-03-16

### Bug Fixes

- update landing page with brew install and v5.11.0 badge([`00438ee`](https://github.com/MadAppGang/claudish/commit/00438ee856a6e4988dcab8c506195a2470999b4a))
- add "no healthy deployment" to retryable errors for LiteLLM fallback([`8bdff19`](https://github.com/MadAppGang/claudish/commit/8bdff19d3b8c86924ecdc895c35e04bee2167acc))
- dynamically fetch top models from OpenRouter API([`71f5b1d`](https://github.com/MadAppGang/claudish/commit/71f5b1d501a5aa381cb32b4342d06c4255292646))
- use canonical homebrew-tap repo name in CI([`ca3053f`](https://github.com/MadAppGang/claudish/commit/ca3053fcabb83acff90c47ece10706cc93ceb11d))

### New Features

- v5.12.0 - LiteLLM fallback fix, dynamic top models([`37f27e4`](https://github.com/MadAppGang/claudish/commit/37f27e410ca6ecc9418ccb2a06c3d8827295dc90))

## [5.11.0] - 2026-03-15

### Bug Fixes

- skip vision probe for glm (glm-5 is text-only) *(smoke)* ([`cb8660c`](https://github.com/MadAppGang/claudish/commit/cb8660c912089d192c17d7016502d867ce4cb436))

### New Features

- v5.11.0 - config TUI, API key storage, Homebrew tap migration([`5de8c2c`](https://github.com/MadAppGang/claudish/commit/5de8c2ce4de5bc22b30519bc8f9d7d063d246d18))

## [5.10.0] - 2026-03-15

### Bug Fixes

- revert minimax supportsVision to true, skip in smoke only *(smoke)* ([`92a8d1a`](https://github.com/MadAppGang/claudish/commit/92a8d1aeab738b13d612e77a53c8508a084619d6))
- glm-coding representative model codegeex-4 → glm-5 *(smoke)* ([`a6c0b6e`](https://github.com/MadAppGang/claudish/commit/a6c0b6ebae0564d174beae05613c9a956fb4891b))
- fix zen-go reasoning, enable glm-coding, fix minimax vision *(smoke)* ([`534053f`](https://github.com/MadAppGang/claudish/commit/534053f0bf0bc2aef2bfdb785177134ab61fd0a0))
- re-enable minimax provider (balance topped up) *(smoke)* ([`3526ba5`](https://github.com/MadAppGang/claudish/commit/3526ba5a78b0ea04df87bb9dab757cc041daf663))
- skip minimax provider (redundant with minimax-coding) *(smoke)* ([`d253a5a`](https://github.com/MadAppGang/claudish/commit/d253a5a1246990dced5668965425f58847c4ae1a))
- add LITELLM_BASE_URL to smoke test workflow env *(smoke)* ([`795df6b`](https://github.com/MadAppGang/claudish/commit/795df6bbdfce33ac34d6a46b450103e9369c8f56))

### Documentation

- update landing page hero version to v5.9.0([`aa0bd65`](https://github.com/MadAppGang/claudish/commit/aa0bd651c2ed3903819f3ce3b449950e3334a1f2))

### New Features

- v5.10.0 - custom routing rules, 429 retryable, smoke test fixes([`e38af0e`](https://github.com/MadAppGang/claudish/commit/e38af0e526421de555a4d96c75d08291911a5aba))

## [5.9.0] - 2026-03-14

### Bug Fixes

- fix tool probe, opencode-zen model, minimax-coding vision *(smoke)* ([`5072d5b`](https://github.com/MadAppGang/claudish/commit/5072d5b1eefca16bcffccf1bb81611c9e46d0610))
- litellm representative model → gemini-2.5-flash (gpt-4o-mini not deployed) *(smoke)* ([`b2bb925`](https://github.com/MadAppGang/claudish/commit/b2bb925208fb89bc4942e055924c33ea080d6210))

### New Features

- v5.9.0 - provider fallback chain for auto-routed models([`dfb60dd`](https://github.com/MadAppGang/claudish/commit/dfb60dd01055a87adef9ad12fcdb71345c0f7dd1))

## [5.8.0] - 2026-03-06

### New Features

- v5.8.0 - periodic smoke test suite for all providers([`df24c7d`](https://github.com/MadAppGang/claudish/commit/df24c7d7dcd803cb803d4ea59f930e56e7ef5275))

## [5.7.1] - 2026-03-06

### Bug Fixes

- v5.7.1 - strip tool_reference blocks; fix qwen OpenRouter vendor prefix([`b8ea099`](https://github.com/MadAppGang/claudish/commit/b8ea099efcad1fdfb7036cb0519e348f87731c9f))

### Documentation

- v5.7.0 - update README and CHANGELOG for Zen Go provider([`f3cef40`](https://github.com/MadAppGang/claudish/commit/f3cef403c3bece598bade12f6b482d92cbd0bd01))

## [5.7.0] - 2026-03-06

### New Features

- v5.7.0 - add OpenCode Zen Go provider (zgo@) with live model discovery([`10afe39`](https://github.com/MadAppGang/claudish/commit/10afe39531a2b76cc63c8e1cf46713602eb278e6))

## [5.6.1] - 2026-03-05

### Bug Fixes

- v5.6.1 - fix MiniMax direct API auth (Bearer vs x-api-key)([`74d1f84`](https://github.com/MadAppGang/claudish/commit/74d1f842023fe7285d56c510fee72888b404346b))
- switch direct API auth from x-api-key to Authorization: Bearer *(minimax)* ([`0d96b8c`](https://github.com/MadAppGang/claudish/commit/0d96b8c86fd5eb55dcece4dbc810538b279d2464))

## [5.6.0] - 2026-03-05

### New Features

- v5.6.0 - auto-resolve vendor prefixes for OpenRouter and LiteLLM([`8703b2a`](https://github.com/MadAppGang/claudish/commit/8703b2a083269a45a798f2cebea2f135f4e9a3d0))

## [5.5.2] - 2026-03-03

### Bug Fixes

- v5.5.2 - truncateContent crash on undefined content([`3c047ca`](https://github.com/MadAppGang/claudish/commit/3c047ca94d9978756004ab8796382829af06fe58))

## [5.5.1] - 2026-03-03

### Bug Fixes

- v5.5.1 - consolidate duplicate update command into single path([`7bdfa14`](https://github.com/MadAppGang/claudish/commit/7bdfa147d0473a74971204b88ceae344ed9254c0))

## [5.5.0] - 2026-03-03

### New Features

- v5.5.0 - provider-agnostic recommended models and GLM adapter([`ccde45b`](https://github.com/MadAppGang/claudish/commit/ccde45b43a34b5b9ed3698f356ef611f09b47231))

## [5.4.1] - 2026-03-03

### Bug Fixes

- v5.4.1 - monitor mode no longer sets invalid model name([`956f513`](https://github.com/MadAppGang/claudish/commit/956f513fd179519640e07ea7bbd31a01af8f3e1d))
- monitor mode no longer sets ANTHROPIC_MODEL="unknown"([`f333e11`](https://github.com/MadAppGang/claudish/commit/f333e1156d0aa708eed1699f309e564f4ebd057c))

## [5.4.0] - 2026-03-03

### New Features

- v5.4.0 - anonymous error telemetry with opt-in consent([`5ac3df1`](https://github.com/MadAppGang/claudish/commit/5ac3df1b9309d9ed8152484ba92a7e57be0f5a7c))

## [5.3.1] - 2026-03-02

### Bug Fixes

- v5.3.1 - provider error visibility and quiet suppression([`066d058`](https://github.com/MadAppGang/claudish/commit/066d058c1cf20a53d8ba9e6c6db17bd146a85fca))

## [5.3.0] - 2026-03-02

### New Features

- v5.3.0 - Claude Code flag passthrough([`8422c59`](https://github.com/MadAppGang/claudish/commit/8422c59e85095669df516bdf52e049d9d6e694ca))

## [5.2.0] - 2026-02-26

### New Features

- v5.2.0 - auto model routing without provider prefix([`cabcef3`](https://github.com/MadAppGang/claudish/commit/cabcef3b14afb26654676cbf7b04f8062f6e04ea))

## [5.1.2] - 2026-02-25

### Bug Fixes

- v5.1.2 - fix landing page CI deploy (bun lockfile, Firebase project ID)([`63a9c4f`](https://github.com/MadAppGang/claudish/commit/63a9c4f03615baeda614483f05009a109f0e3c9e))
- use bun instead of pnpm for landing page deploy, correct Firebase project ID([`ff34904`](https://github.com/MadAppGang/claudish/commit/ff349040609f2009b585017cd180154ccdfce183))

## [5.1.1] - 2026-02-25

### Bug Fixes

- include LiteLLM models in --models search and listing([`06ee4e6`](https://github.com/MadAppGang/claudish/commit/06ee4e6eea9b9b2177a8266a4c19409da547b59c))
- v5.1.1 - unset CLAUDECODE env var for nested session compatibility([`9c62ca9`](https://github.com/MadAppGang/claudish/commit/9c62ca97b6c6f30ea165b1ff6aace32c3eedff56))
- v5.1.0 - landing page vision section, Gemini pricing, lint fixes([`bf9ac8c`](https://github.com/MadAppGang/claudish/commit/bf9ac8cc4238f9ee5eaee3aee120c520e3b74940))

### Documentation

- add vision proxy section to README([`0029cde`](https://github.com/MadAppGang/claudish/commit/0029cdedd20776e5b889ec60de4361ea05db9647))

### New Features

- add Changelog section to landing page with auto-deploy on release([`8aa64a7`](https://github.com/MadAppGang/claudish/commit/8aa64a77fec4a78f702b030504b1c6c43f5cdeeb))
- auto-generate structured release notes from conventional commits([`ada936f`](https://github.com/MadAppGang/claudish/commit/ada936fe3a011394b3867296773d775df7320a21))

## [5.1.0] - 2026-02-19

### New Features

- v5.1.0 - vision proxy for non-vision models([`355bbb0`](https://github.com/MadAppGang/claudish/commit/355bbb063903f473d23f31a9c4503a6226a4d91a))

## [5.0.0] - 2026-02-18

### New Features

- v5.0.0 - composable handler architecture, minimax-coding provider([`fdcadd5`](https://github.com/MadAppGang/claudish/commit/fdcadd51eac54d27eab34b3b6be9cee29db5cce8))

## [4.6.11] - 2026-02-16

### Bug Fixes

- v4.6.11 - sync reasoning_content fix to packages/cli([`0b46f87`](https://github.com/MadAppGang/claudish/commit/0b46f87857cc93ba9fcffa93f0f0f5b2546fe686))

## [4.6.10] - 2026-02-16

### Bug Fixes

- v4.6.10 - handle reasoning_content for Kimi thinking models via LiteLLM([`8af631c`](https://github.com/MadAppGang/claudish/commit/8af631cce5dac500ae1e6185503c141b9d0324b0))

## [4.6.9] - 2026-02-15

### Bug Fixes

- v4.6.9 - force-update clears all model caches, add --list-models alias([`618db96`](https://github.com/MadAppGang/claudish/commit/618db96fea42dec51c0c421533ad02e47e1932c3))
- add User-Agent header for Kimi models via LiteLLM([`6758f21`](https://github.com/MadAppGang/claudish/commit/6758f211dbd994d2a1e2369acf324746b3dd75d8))
- convert image_url to inline base64 for MiniMax via LiteLLM([`6be13ee`](https://github.com/MadAppGang/claudish/commit/6be13eebb66d90ca45cef93d0aa6131bab83782e))

## [4.6.8] - 2026-02-14

### Bug Fixes

- v4.6.8 - sync LiteLLM handler to packages/cli for npm publish([`7d27f2d`](https://github.com/MadAppGang/claudish/commit/7d27f2dead831a67bee768e1fdb540a5a5285fcf))

## [4.6.7] - 2026-02-14

### Bug Fixes

- v4.6.7 - strip images for non-vision GLM models([`e8b676e`](https://github.com/MadAppGang/claudish/commit/e8b676e57121fb8819850aa5a8879dcf325448ab))

## [4.6.6] - 2026-02-13

### Bug Fixes

- v4.6.6 - use Promise.allSettled for provider fetches([`130a00f`](https://github.com/MadAppGang/claudish/commit/130a00fe2e31839ea880073cab8a2098518e9fe8))

## [4.6.5] - 2026-02-13

### New Features

- v4.6.5 - interactive provider filter in model selector([`a937998`](https://github.com/MadAppGang/claudish/commit/a9379989eb0f6913f5a9f0d64348edff270e3e4e))

## [4.6.4] - 2026-02-13

### New Features

- v4.6.4 - add @provider filter to interactive model search([`8631bf0`](https://github.com/MadAppGang/claudish/commit/8631bf08605da02aa12834e971f0c7ffc04eada0))

## [4.6.3] - 2026-02-13

### Bug Fixes

- v4.6.3 - remove silent provider fallback, fix LiteLLM endpoint([`1b30325`](https://github.com/MadAppGang/claudish/commit/1b30325c416a54b436c622db24e97a54e93e1cde))

## [4.6.2] - 2026-02-13

### Bug Fixes

- v4.6.2 - sync LiteLLM model discovery to packages/cli for npm publish([`1db5432`](https://github.com/MadAppGang/claudish/commit/1db5432c305fc72d9f0210eb7a70155f9ee9f7aa))

## [4.6.1] - 2026-02-12

### Bug Fixes

- v4.6.1 - model routing and self-update fixes([`0b972e3`](https://github.com/MadAppGang/claudish/commit/0b972e36526b01131caa30b5001a771f2d8a27a3))

### Documentation

- update CLAUDE.md with version bump checklist and LiteLLM shortcut([`4bb7ea3`](https://github.com/MadAppGang/claudish/commit/4bb7ea32f39d5b0d5d970b9e05943cdc0226a99b))

## [4.6.0] - 2026-02-12

### Bug Fixes

- update packages/cli/package.json version to 4.6.0([`20d4fb7`](https://github.com/MadAppGang/claudish/commit/20d4fb77751ed22cfe4d5471e7cb394f120b27dd))

### New Features

- v4.6.0 - LiteLLM provider support([`fdf3719`](https://github.com/MadAppGang/claudish/commit/fdf371948c737ef85ecf9fbd60170d4fffe61403))

## [4.5.3] - 2026-02-12

### New Features

- v4.5.3 - OllamaCloud/GLM model discovery, fuzzy search improvements([`bdd27e5`](https://github.com/MadAppGang/claudish/commit/bdd27e5437d470953cfa0faeccca7635b0202db0))

## [4.5.2] - 2026-02-12

### New Features

- v4.5.2 - GLM Coding Plan provider, local/global profiles, landing page updates([`dda1c3a`](https://github.com/MadAppGang/claudish/commit/dda1c3aadb361b847dc89744ebcb41424fc91d6c))

## [4.5.1] - 2026-02-09

### New Features

- v4.5.1 - Kimi Coding provider sync and model updates([`5575ea6`](https://github.com/MadAppGang/claudish/commit/5575ea6732fd3192da2ab5f6ac98bd18b053ad45))

## [4.5.0] - 2026-02-06

### New Features

- v4.5.0 - Profile-based model routing and dynamic status line([`e0aa3eb`](https://github.com/MadAppGang/claudish/commit/e0aa3ebb76335161f075f41d035f1365cc587bad))

## [4.4.5] - 2026-02-03

### New Features

- v4.4.5 - Progress bar for context display, Vertex routing fix([`25d70ba`](https://github.com/MadAppGang/claudish/commit/25d70baa233e6d3ba3d8e8d96e0d3e42420aa212))

## [4.4.4] - 2026-02-03

### Bug Fixes

- v4.4.4 - Use models.dev API for accurate OpenAI context windows([`c85dddf`](https://github.com/MadAppGang/claudish/commit/c85dddf3a16ea3a8f915d4339da4e481aa667845))

### Other Changes

- add original OG image for landing page([`796d4a0`](https://github.com/MadAppGang/claudish/commit/796d4a0347b10136d6dca93fbac629797a7f9762))

## [4.4.3] - 2026-01-30

### Bug Fixes

- v4.4.3 - Add missing getToolNameMap method and tool-name-utils([`f9e885b`](https://github.com/MadAppGang/claudish/commit/f9e885bf6b28f001bcf578a32194942b1526b2fa))

## [4.4.2] - 2026-01-30

### Bug Fixes

- v4.4.2 - Fix update command with -y flag alias([`fe3f280`](https://github.com/MadAppGang/claudish/commit/fe3f28057655a07f35fd505b380607d84dbd492d))

## [4.4.1] - 2026-01-30

### New Features

- v4.4.1 - Add claudish update command([`ae44988`](https://github.com/MadAppGang/claudish/commit/ae449880d8f2d2ecc18c17f333e18b66f79b4954))

## [4.4.0] - 2026-01-30

### New Features

- v4.4.0 - Interactive model selector improvements([`89fd34e`](https://github.com/MadAppGang/claudish/commit/89fd34e1a53a02af3b099e99b531f45c061da0c1))

## [4.3.1] - 2026-01-30

### New Features

- v4.3.1 - SEO improvements and multi-provider documentation([`74a73b9`](https://github.com/MadAppGang/claudish/commit/74a73b94b2b52bdfd0cb6e5e39fce32383a4d042))

## [4.3.0] - 2026-01-30

### Bug Fixes

- sync packages/cli version to 4.3.0([`02700dd`](https://github.com/MadAppGang/claudish/commit/02700ddf5fc463908acaf62f619754dab1a795fc))

### New Features

- v4.3.0 - Add --stream flag for NDJSON streaming output([`7b2403b`](https://github.com/MadAppGang/claudish/commit/7b2403b1a37d8c3c447f378af5c8e13f0c7ab0ad))

## [4.2.2] - 2026-01-30

### Bug Fixes

- profile flag now skips model selector, Gemini tool name sanitization([`f97271d`](https://github.com/MadAppGang/claudish/commit/f97271dfc3491b3e79fd512e6c872f96c7d5c59b))

## [4.2.1] - 2026-01-30

### Bug Fixes

- update xAI model references to use latest Grok 4.1 models([`40f5fb2`](https://github.com/MadAppGang/claudish/commit/40f5fb29c9b584b78f8791496de72861a7a9a78a))

## [4.2.0] - 2026-01-30

### Bug Fixes

- support Anthropic subscription auth in monitor mode *(monitor)* ([`8f4fb3c`](https://github.com/MadAppGang/claudish/commit/8f4fb3c8f310e3fbff20e79bfa03b07de598ee95))

### New Features

- v4.2.0 - Add direct xAI/Grok API support and multi-provider model selector([`78bd21d`](https://github.com/MadAppGang/claudish/commit/78bd21d9221bde6cee33cd368584bf0236dfd191))

## [4.1.1] - 2026-01-28

### Bug Fixes

- use ~/.claudish/ for models cache in standalone binaries([`05583f5`](https://github.com/MadAppGang/claudish/commit/05583f5f490c5fc256f76ace76aff2e9533cbbb6))

## [4.1.0] - 2026-01-28

### Bug Fixes

- implement --gemini-login and --gemini-logout CLI flags([`ea6a5f0`](https://github.com/MadAppGang/claudish/commit/ea6a5f05f4840d1a9ff610a6f3b260c820b51129))

### New Features

- v4.1.0 - Dynamic pricing and status line improvements([`bb59b06`](https://github.com/MadAppGang/claudish/commit/bb59b06b814ee0484fff81baa92289152988f2b4))

### Other Changes

- remove AI session artifacts and legacy lockfiles([`4cb76fb`](https://github.com/MadAppGang/claudish/commit/4cb76fb3065c54cd30ada59ce900bd946f445d6b))

## [4.0.6] - 2026-01-26

### Bug Fixes

- use correct bun command for global package updates *(update)* ([`a7eee57`](https://github.com/MadAppGang/claudish/commit/a7eee579b3497132652e6bbeb4cc643c8faeb89e))

## [4.0.5] - 2026-01-26

### Bug Fixes

- model switching and role mappings now work correctly([`40fc939`](https://github.com/MadAppGang/claudish/commit/40fc939b05e05f870ea38c93dfdb0a43a4ab177d))

## [4.0.4] - 2026-01-26

### Bug Fixes

- don't skip permissions by default (safer behavior)([`54293f2`](https://github.com/MadAppGang/claudish/commit/54293f20d0a433156221d5b2e845ffab2fc8e293))

## [4.0.3] - 2026-01-26

### Bug Fixes

- improve Termux/Android support *(android)* ([`5b8e14d`](https://github.com/MadAppGang/claudish/commit/5b8e14dcb8bf26bf557dbd04862a2c5be988123d))

## [4.0.2] - 2026-01-26

### Bug Fixes

- use claude.cmd instead of claude shell script *(windows)* ([`18ae794`](https://github.com/MadAppGang/claudish/commit/18ae794699ef31f62876cec5f22052bed9b6ea85))

## [4.0.1] - 2026-01-26

### Bug Fixes

- explicit provider routing for all CLI commands([`87c4ae0`](https://github.com/MadAppGang/claudish/commit/87c4ae0e494888f9a7f1794d67633f65d0d569d5))

## [4.0.0] - 2026-01-26

### Bug Fixes

- make build work without private markdown file([`ba5427c`](https://github.com/MadAppGang/claudish/commit/ba5427cb387317283ab36c0f88c92a6bbd5096f2))

### New Features

- v4.0.0 - New provider@model routing syntax([`f16caf4`](https://github.com/MadAppGang/claudish/commit/f16caf4c06c0140accf5c7d5aa5af8d552442afc))
- auto-update recommended models on release([`e1cd5e4`](https://github.com/MadAppGang/claudish/commit/e1cd5e4ffc4587b31a74d02eccbb6cf28cf64fbf))

### Other Changes

- remove all references to shared/recommended-models.md([`98d106d`](https://github.com/MadAppGang/claudish/commit/98d106d1d5f5623307b98f7ff0cc44881bcf1ffb))

### Refactoring

- remove obsolete extract-models.ts system([`08a044c`](https://github.com/MadAppGang/claudish/commit/08a044cf9c1d9eea4dd2df227511349d5f00b051))

## [3.11.0] - 2026-01-25

### Bug Fixes

- sync workspace package versions to 3.10.0([`36eea9d`](https://github.com/MadAppGang/claudish/commit/36eea9d8ed2fc6521fb42fd7d7622e245546bd06))

### Documentation

- add Z.AI to help text([`9524a0c`](https://github.com/MadAppGang/claudish/commit/9524a0cee5d3bcbc223b92e8138b3ff713e3d275))

### New Features

- v3.11.0 - local model concurrency queue([`d51755e`](https://github.com/MadAppGang/claudish/commit/d51755e34a54cb0fb982861cbb105f2b41d968e2))

## [3.10.0] - 2026-01-25

### Bug Fixes

- route google/ and openai/ to OpenRouter, add tests([`a29087c`](https://github.com/MadAppGang/claudish/commit/a29087cf4c27f727af3d3856977f1c30ed54de74))
- API key precedence and provider resolution (#38)([`5d7d3a9`](https://github.com/MadAppGang/claudish/commit/5d7d3a940dcd7e4812846ee7f0cabbc623cbb802))
- package.json scripts (#37)([`017ce5e`](https://github.com/MadAppGang/claudish/commit/017ce5e21fbd97aa34168b02b7305b33186b0bb4))

### New Features

- v3.10.0 - add Z.AI direct provider and fix GLM reasoning([`a6d259e`](https://github.com/MadAppGang/claudish/commit/a6d259e79867d64b9f36de6c17f7c4e2afb4af42))

## [3.9.0] - 2026-01-24

### New Features

- v3.9.0 - rate limiting queue and improved error handling([`eda8b0e`](https://github.com/MadAppGang/claudish/commit/eda8b0e768eea99e2760ad338d56268eead1bf5a))

## [3.8.0] - 2026-01-23

### Bug Fixes

- sync src/ with packages/ for OpenCode Zen support([`4a22f08`](https://github.com/MadAppGang/claudish/commit/4a22f087fd7b1493381a9c57ce00cae3d5a10097))
- show FREE in status line for OpenRouter free models([`a1397e6`](https://github.com/MadAppGang/claudish/commit/a1397e619822e06c7061131ae47e247220c39d33))
- filter --free models to only show those with tool support([`47c6026`](https://github.com/MadAppGang/claudish/commit/47c6026ff7a4e3a0b16f3bea478c04fa2e2fe0d8))
- show FREE in status line for free zen/ models([`cdfc913`](https://github.com/MadAppGang/claudish/commit/cdfc9134a1aa6be7fa29869874d40af1b5c186ed))
- use correct pricing for zen/ free models([`a1ece06`](https://github.com/MadAppGang/claudish/commit/a1ece06d51c0039e59d703aa16a2b70aca035061))
- show correct provider name in status line for zen/ models([`4b0d81d`](https://github.com/MadAppGang/claudish/commit/4b0d81d9e282ac3121be2fbac60bb6c8b1de8712))
- zen/ provider skip auth header for free models([`e704671`](https://github.com/MadAppGang/claudish/commit/e7046715f82f5de640dcc2009bfc58d7a04ed8fe))

### New Features

- friendly error messages for OpenRouter API errors([`d920585`](https://github.com/MadAppGang/claudish/commit/d920585f6f51f63645f267169141de8f0922f1a7))
- add rate limiting queue for OpenRouter API([`ac46c00`](https://github.com/MadAppGang/claudish/commit/ac46c00cadafdf1ffe3f3181b625f32f3d28ac10))
- v3.8.0 - add OpenCode Zen provider (zen/ prefix)([`3568c3a`](https://github.com/MadAppGang/claudish/commit/3568c3a5fe8d4338b2f23459db176e44e0b56fe7))

## [3.7.9] - 2026-01-23

### Bug Fixes

- v3.7.9 - check all model slots for API key requirement([`568610a`](https://github.com/MadAppGang/claudish/commit/568610a7348f3fe8c9e50ec638e2380196d1650d))

## [3.7.8] - 2026-01-23

### New Features

- v3.7.8 - skip OpenRouter API key for local models([`382e741`](https://github.com/MadAppGang/claudish/commit/382e741457aadf68598ec968dd53129777534928))

## [3.7.7] - 2026-01-23

### Bug Fixes

- v3.7.7 - fix package.json not found in compiled binaries([`503897f`](https://github.com/MadAppGang/claudish/commit/503897fdd9d4986c6d6d58121247bb3a3a858ef7))

## [3.7.6] - 2026-01-23

### Bug Fixes

- v3.7.6 - improve Claude Code detection on Mac([`6566d96`](https://github.com/MadAppGang/claudish/commit/6566d964cdfd8e918e19cc8e1e74cb33cbd8fbc5))

## [3.7.5] - 2026-01-23

### Bug Fixes

- v3.7.5 - bypass Claude Code login screen in interactive mode([`350f48c`](https://github.com/MadAppGang/claudish/commit/350f48cee2d0b6265e572a137674745f6d09a703))

## [3.7.4] - 2026-01-23

### Bug Fixes

- v3.7.4 - support local Claude Code installations([`54fb39c`](https://github.com/MadAppGang/claudish/commit/54fb39c32b00c72463b6269d225122f40c8892f6))

## [3.7.3] - 2026-01-22

### New Features

- v3.7.3 - dynamic provider and model name in status line([`3e413fc`](https://github.com/MadAppGang/claudish/commit/3e413fcb47ae321480b0cd27d669a21d0568fb49))

## [3.7.2] - 2026-01-22

### Bug Fixes

- v3.7.2 - show FREE for OAuth sessions, ~$ for estimated pricing([`605c589`](https://github.com/MadAppGang/claudish/commit/605c589fc9a0ad827c10ab701385bbd1a5d4ce9c))

## [3.7.1] - 2026-01-22

### Bug Fixes

- v3.7.1 - type coercion for local model tool arguments([`a3fddd6`](https://github.com/MadAppGang/claudish/commit/a3fddd647265019494a10d25fb760328c3f8eb29))
- add type coercion for tool arguments from local models (#30)([`23ca258`](https://github.com/MadAppGang/claudish/commit/23ca25850b9c4711d1c2fa42e7c1c612fb7fa16c))

## [3.7.0] - 2026-01-22

### New Features

- v3.7.0 - Gemini Code Assist OAuth support with rate limiting([`687b953`](https://github.com/MadAppGang/claudish/commit/687b953da738bedf944c387e7bfe3e01857e946a))

## [3.6.1] - 2026-01-22

### Bug Fixes

- v3.6.1 - network error handling with SSE response format([`be37a5c`](https://github.com/MadAppGang/claudish/commit/be37a5cc226421eca7bdef69cfd7fede8c4849fb))
- handle network errors with proper SSE response format([`7f00208`](https://github.com/MadAppGang/claudish/commit/7f002084ee187a38cd043e7bd8cd1649460fae4e))

## [3.6.0] - 2026-01-22

### Documentation

- add OllamaCloud to packages/cli help text([`04c6aeb`](https://github.com/MadAppGang/claudish/commit/04c6aeb2612e0f4e938588be58b76f972fa69b88))
- add OllamaCloud provider documentation([`2bdb38a`](https://github.com/MadAppGang/claudish/commit/2bdb38a6421f0e889ee40f68d98f5f103c4dde79))

### New Features

- v3.6.0 - OllamaCloud provider support([`835ffdf`](https://github.com/MadAppGang/claudish/commit/835ffdf59f1830c636dd83078f3dc3101fd7154e))
- add OllamaCloud provider support with oc/ prefix([`4dba1a5`](https://github.com/MadAppGang/claudish/commit/4dba1a5bfc74f49b78c36f0b7b1c421bd7b7de30))
- add Claude Code Action for PR assistance([`f3d548d`](https://github.com/MadAppGang/claudish/commit/f3d548d334e6facba4cdf5c38fff99e4f53078db))
- add issue triage bot with Claude Code([`5d8b970`](https://github.com/MadAppGang/claudish/commit/5d8b9700c425b307313c8420e798182eb6e926f6))
- add Poe API provider support *(providers)* ([`57c5cb3`](https://github.com/MadAppGang/claudish/commit/57c5cb362a2abe64fb6a634bdccc0d86675d341c))

## [3.5.0] - 2026-01-21

### Bug Fixes

- use fixed default port 8899 for reliable communication *(proxy)* ([`ddd1c70`](https://github.com/MadAppGang/claudish/commit/ddd1c709e16e380b011c71600bc74c39df604c1e))

### New Features

- add Vertex AI OAuth mode and partner model support([`2a3605d`](https://github.com/MadAppGang/claudish/commit/2a3605d0bd5b703ebac575146e9adb374c5d7771))
- robust port communication with lock file and health checks *(proxy)* ([`f4b5faa`](https://github.com/MadAppGang/claudish/commit/f4b5faaee1ec66d74c97b2e98451cf818a4118b1))
- per-instance proxy via --proxy-server flag *(ClaudishProxy)* ([`2325d4d`](https://github.com/MadAppGang/claudish/commit/2325d4d15e64dec60f4437d4243cf86f7efa0ba6))
- add Vertex AI Express Mode support *(providers)* ([`c214a3c`](https://github.com/MadAppGang/claudish/commit/c214a3c6a00ef6def1e24e7edf8508616e48b547))
- native OpenAI routing, error display, and config sync *(proxy)* ([`515399e`](https://github.com/MadAppGang/claudish/commit/515399e67cc9aee76f852bb7888dca4fe1827dae))
- add auto-recovery and stale proxy cleanup *(ClaudishProxy)* ([`f2769ab`](https://github.com/MadAppGang/claudish/commit/f2769abfe65182ee777688cc71f12626dfb46ba0))
- add model routing and conversation sync persistence *(macos-bridge)* ([`ca645f3`](https://github.com/MadAppGang/claudish/commit/ca645f36a2418771dd1e733100f0f2c647f51499))

### Other Changes

- remove verbose status check debug log([`9cfc753`](https://github.com/MadAppGang/claudish/commit/9cfc753f0320d48bfc27aa7a62e512993008b617))

## [3.4.1] - 2026-01-20

### Documentation

- add MCP server documentation to --help and AI_AGENT_GUIDE([`91646f3`](https://github.com/MadAppGang/claudish/commit/91646f3936d7154424cadfa796f82ceb93ffab8a))

### New Features

- add zombie process hunting and recovery *(macos-bridge)* ([`087cf56`](https://github.com/MadAppGang/claudish/commit/087cf564667d604eff7a9a132238bfc889cfca52))
- SQLite stats, HTTPS interception, improved About screen *(ClaudishProxy)* ([`52e0626`](https://github.com/MadAppGang/claudish/commit/52e0626e6fd24887a16187a91fe0152e3306d282))
- add model profiles and dynamic model picker *(ClaudishProxy)* ([`6ce5cf6`](https://github.com/MadAppGang/claudish/commit/6ce5cf6c5c341fb851cf778ea7c239edb62f516f))
- add StatsPanel UI with activity table *(ClaudishProxy)* ([`9cc4fe1`](https://github.com/MadAppGang/claudish/commit/9cc4fe1e18395c65b431836bf23b9639a15b26fe))

## [3.4.0] - 2026-01-16

### New Features

- v3.4.0 - add claudish update command([`23a09e7`](https://github.com/MadAppGang/claudish/commit/23a09e76a34770f1e9d94b4898a6fb436313a337))
- add claudish update command([`504b52e`](https://github.com/MadAppGang/claudish/commit/504b52e21a6f4d80dd074c3c36dfc8975cc00d29))

## [3.3.12] - 2026-01-15

### Bug Fixes

- OpenAI Codex Responses API streaming and ID mapping([`b033084`](https://github.com/MadAppGang/claudish/commit/b033084d16a2c3ea85c603be6f2d2c22cc9bd730))
- proper cleanup and send() helper in Codex streaming([`d9cd2dd`](https://github.com/MadAppGang/claudish/commit/d9cd2dd9aef2e463ba51f7761977f25a470c36fc))

## [3.3.10] - 2026-01-15

### Bug Fixes

- add ping event after message_start for Responses API streaming([`6ee1da2`](https://github.com/MadAppGang/claudish/commit/6ee1da2b88454277dd3c149c37ee2d1915bc1425))

## [3.3.9] - 2026-01-15

### Bug Fixes

- calculate cost using incremental input tokens, not full context([`08aa13c`](https://github.com/MadAppGang/claudish/commit/08aa13ca70a7cd67ca30139573fe20bf0a0a6ad7))

## [3.3.8] - 2026-01-15

### Bug Fixes

- use placeholder input_tokens in message_start for Responses API([`a974c49`](https://github.com/MadAppGang/claudish/commit/a974c4906fb7b21fdf18ee269be7b63de0954341))

## [3.3.7] - 2026-01-15

### Bug Fixes

- handle both response.completed and response.done for token counting([`1a6b383`](https://github.com/MadAppGang/claudish/commit/1a6b383dbfb20836637b9474750f69624caf66b2))

## [3.3.6] - 2026-01-15

### Bug Fixes

- Responses API function_call as top-level items, not content blocks([`c9ed4ef`](https://github.com/MadAppGang/claudish/commit/c9ed4ef85c909a982d9eea0cf60e27f5f3b1ebf6))

## [3.3.5] - 2026-01-15

### Bug Fixes

- proper Responses API format for images and function calling([`b6d4af0`](https://github.com/MadAppGang/claudish/commit/b6d4af054aee29ec0bcb77aea0733f0639b1ea12))

## [3.3.4] - 2026-01-15

### Bug Fixes

- correct Responses API message format for Codex models([`8178f8e`](https://github.com/MadAppGang/claudish/commit/8178f8e3d349866ae1947b07cadd8100d4dfe86d))

## [3.3.3] - 2026-01-15

### New Features

- add OpenAI Codex model support via Responses API([`5b7d630`](https://github.com/MadAppGang/claudish/commit/5b7d63092f8dde7e0338fda2bcf591814341891c))

## [3.3.2] - 2026-01-15

### Bug Fixes

- build core before binary in CI([`1b3d93d`](https://github.com/MadAppGang/claudish/commit/1b3d93db959433c2595aa0e806211aff1b608417))

## [3.3.1] - 2026-01-15

### Bug Fixes

- build from root to preserve workspace resolution in CI([`4bcc332`](https://github.com/MadAppGang/claudish/commit/4bcc33260c267862a0d1768f297aa546ab266184))

## [3.3.0] - 2026-01-15

### Bug Fixes

- update CI/CD for monorepo structure([`97d2f68`](https://github.com/MadAppGang/claudish/commit/97d2f68c4bbf8e313d149dbfa8321b9cf9c1e444))

### New Features

- convert to monorepo with macOS desktop proxy support([`1962c38`](https://github.com/MadAppGang/claudish/commit/1962c387790de1ee7363809c17ace77899c3d72f))

## [3.2.3] - 2026-01-12

### Bug Fixes

- add thoughtSignature support for Gemini direct API([`42fa475`](https://github.com/MadAppGang/claudish/commit/42fa47534e9931652089df48328bb9b1e05dfeb1))

## [3.2.2] - 2026-01-12

### Bug Fixes

- use max_completion_tokens for newer OpenAI models([`b82f447`](https://github.com/MadAppGang/claudish/commit/b82f4472b513e289c221579a89386b679c83c4ef))

## [3.2.1] - 2026-01-11

### Bug Fixes

- sanitize JSON schema for Gemini API compatibility([`94318fb`](https://github.com/MadAppGang/claudish/commit/94318fbc173ad0fe1aac6185b02fd23c0993873e))

### Other Changes

- format codebase and update recommended models([`b350fb9`](https://github.com/MadAppGang/claudish/commit/b350fb9867a7156ced575011d63570cf9e746667))

## [3.2.0] - 2026-01-07

### New Features

- add direct API support for MiniMax, Kimi, and GLM providers([`129417b`](https://github.com/MadAppGang/claudish/commit/129417bc2e2b4278ee8c9456370cf13b505680fe))

## [3.1.3] - 2026-01-05

### Bug Fixes

- google/ prefix now routes to OpenRouter, not Gemini Direct([`9ccfa19`](https://github.com/MadAppGang/claudish/commit/9ccfa19461232fcffc4d465ff4bdc655a913f026))

## [3.1.2] - 2026-01-05

### Documentation

- update documentation for multi-provider routing([`1cab9d7`](https://github.com/MadAppGang/claudish/commit/1cab9d753d70a43ee729fe53af878050f44f62c6))

## [3.1.1] - 2026-01-05

### Bug Fixes

- enable tool support for MLX provider([`41203bd`](https://github.com/MadAppGang/claudish/commit/41203bdc77bedb40756edcff619d69be98a3a790))

## [3.1.0] - 2026-01-04

### New Features

- direct Gemini and OpenAI API support with prefix routing([`2b0064d`](https://github.com/MadAppGang/claudish/commit/2b0064d29e65ef3200716bc56d3a81998efaddeb))

## [3.0.6] - 2025-12-29

### Bug Fixes

- status line cost display always showing $0.000([`2f53e70`](https://github.com/MadAppGang/claudish/commit/2f53e70931371950bbb4e76ed043f095c808539a))

## [3.0.5] - 2025-12-29

### Bug Fixes

- token file path mismatch causing status line to show 100% context([`c2e396d`](https://github.com/MadAppGang/claudish/commit/c2e396d4e7d08216194a324387cd1fd6bf955fc9))

## [3.0.4] - 2025-12-29

### Bug Fixes

- expand Gemini reasoning filter patterns([`5a014c4`](https://github.com/MadAppGang/claudish/commit/5a014c40505d91c8a9edb6d41d16ca9f2f98ef41))

## [3.0.3] - 2025-12-27

### Bug Fixes

- Gemini reasoning leakage and native thinking block support([`523c0e4`](https://github.com/MadAppGang/claudish/commit/523c0e40cd5949aa09a1bd2b300bc87cc9bf4cf1))

## [3.0.2] - 2025-12-26

### Bug Fixes

- OpenRouter token tracking and debug logging([`f4c1df2`](https://github.com/MadAppGang/claudish/commit/f4c1df2c24f8d5255c77481339481a8fabd35746))

## [3.0.1] - 2025-12-23

### Bug Fixes

- update HTTP-Referer to claudish.com for OpenRouter visibility([`dae66c4`](https://github.com/MadAppGang/claudish/commit/dae66c44e8d892113f0ec46b4bc0af7f661603d9))
- move settings files to ~/.claudish to avoid socket watch errors([`20271eb`](https://github.com/MadAppGang/claudish/commit/20271ebb25dd85515d9cf9b8b2e93ac22ec6037b))

### Other Changes

- add CLAUDE.md and update .gitignore([`30c65d1`](https://github.com/MadAppGang/claudish/commit/30c65d1b21dda587ac7e9941a58d276a5790960a))

## [3.0.0] - 2025-12-14

### New Features

- v3.0.0 - Full local model support (Ollama, LM Studio)([`a216c95`](https://github.com/MadAppGang/claudish/commit/a216c9556f2c0b9e20ee68e45ac1579275a72604))

## [2.11.0] - 2025-12-13

### New Features

- Add tool summarization and improved local model support([`3139af9`](https://github.com/MadAppGang/claudish/commit/3139af919b958e0aefa23245c772db5ba80e1fca))

## [2.10.1] - 2025-12-13

### Bug Fixes

- Windows spawn ENOENT - runtime platform detection([`51de48f`](https://github.com/MadAppGang/claudish/commit/51de48f1b464e5cceceb05aee5d07a1f56a2b44c))

## [2.10.0] - 2025-12-13

### New Features

- Improve local model UX - tool support detection, context tracking([`d71a9ca`](https://github.com/MadAppGang/claudish/commit/d71a9ca9139bd03aa7d45ed53a770c5605b7b521))

## [2.9.0] - 2025-12-13

### Documentation

- Update installation section with all distribution options([`a43949b`](https://github.com/MadAppGang/claudish/commit/a43949b648abda9a704af8e84dd6a604f19aac78))

### New Features

- Add local Ollama models support([`d92933e`](https://github.com/MadAppGang/claudish/commit/d92933e0377d15d141c27226cc1c38f154db5392))

## [2.8.1] - 2025-12-12

### Bug Fixes

- Use build:ci for npm publish (skip extract-models)([`e60ad5b`](https://github.com/MadAppGang/claudish/commit/e60ad5b0764628b177d1bc5071104e708883bef4))

## [2.8.0] - 2025-12-12

### Bug Fixes

- CI workflow - use macos-15-intel, skip extract-models([`07db17e`](https://github.com/MadAppGang/claudish/commit/07db17e99e6e520f3a1580ecc225c057772b2204))
- fix some view of langing page([`8b9004d`](https://github.com/MadAppGang/claudish/commit/8b9004d0dd9f873b6c9796a0f7113066ba48fde6))

### New Features

- Add automated release pipeline([`31492fc`](https://github.com/MadAppGang/claudish/commit/31492fcba0d8c1dcdf0c7c745244c42b10cbabfa))
- Add profile-based model configuration v2.8.0 *(profiles)* ([`a3303a1`](https://github.com/MadAppGang/claudish/commit/a3303a12dbb54b9e5c0d2eb0ff27b19814fd43c1))




================================================
FILE: CLAUDE.md
================================================
# Claudish - Development Notes

## Release Process

**Releases are handled by CI/CD** - do NOT manually run `npm publish`.

1. Bump version in `package.json`
2. Commit with conventional commit message (e.g., `feat!: v3.0.0 - description`)
3. Create annotated tag: `git tag -a v3.0.0 -m "message"`
4. Push with tags: `git push origin main --tags`
5. CI/CD will automatically publish to npm

## Build Commands

- `bun run build` - Full build (extracts models + bundles)
- `bun run build:ci` - CI build (bundles only, no model extraction)
- `bun run dev` - Development mode

## Model Routing (v4.0+)

### New Syntax: `provider@model[:concurrency]`

```bash
# Explicit provider routing
claudish --model google@gemini-2.0-flash "task"
claudish --model openrouter@deepseek/deepseek-r1 "task"

# Native auto-detection (no prefix needed)
claudish --model gpt-4o "task"          # → OpenAI
claudish --model gemini-2.0-flash "task" # → Google
claudish --model llama-3.1-70b "task"   # → OllamaCloud

# Local models with concurrency
claudish --model ollama@llama3.2:3 "task"  # 3 concurrent requests
```

### Provider Shortcuts
- `g@`, `google@` → Google Gemini
- `oai@` → OpenAI Direct
- `cx@`, `codex@` → OpenAI Codex (Responses API)
- `or@`, `openrouter@` → OpenRouter
- `mm@`, `mmax@` → MiniMax
- `mmc@` → MiniMax Coding Plan
- `kimi@`, `moon@` → Kimi
- `glm@`, `zhipu@` → GLM
- `gc@` → GLM Coding Plan
- `llama@`, `oc@` → OllamaCloud
- `litellm@`, `ll@` → LiteLLM (requires LITELLM_BASE_URL)
- `ollama@` → Ollama (local)
- `lmstudio@` → LM Studio (local)
- Custom endpoint names also work as provider prefixes (e.g., `my-vllm@model-name`) — see "Custom Endpoints" below

### Default Provider Configuration (v7.0.0+)

The default provider for auto-routing is configurable. Set it via:

- **Config file**: `"defaultProvider": "openrouter"` in `~/.claudish/config.json`
- **Env var**: `CLAUDISH_DEFAULT_PROVIDER=litellm`
- **CLI flag**: `claudish --default-provider google "task"`

**Precedence** (highest to lowest):
1. CLI flag `--default-provider`
2. `CLAUDISH_DEFAULT_PROVIDER` env var
3. `defaultProvider` in config file
4. Legacy LITELLM auto-promotion (if `LITELLM_BASE_URL` + `LITELLM_API_KEY` set without explicit `defaultProvider`)
5. `OPENROUTER_API_KEY` present → OpenRouter
6. Hardcoded `"openrouter"`

**Example config**:
```json
{
  "defaultProvider": "litellm",
  "customEndpoints": { ... }
}
```

Valid values: any built-in provider name (`"openrouter"`, `"litellm"`, `"openai"`, `"anthropic"`, `"google"`) or a custom endpoint name defined in `customEndpoints`.

**Interaction with routing rules**: When `defaultProvider` is set and no explicit `routing["*"]` catch-all exists, Claudish synthesizes `routing["*"] = [defaultProvider]` at config load time. An explicit `routing["*"]` always wins.

**Legacy behavior**: If `LITELLM_BASE_URL` and `LITELLM_API_KEY` are set but `defaultProvider` is absent, LiteLLM is still promoted to first in the fallback chain. Claudish emits a one-shot stderr hint suggesting you set `defaultProvider` explicitly.

### Vendor Prefix Auto-Resolution (ModelCatalogResolver)

API aggregators (OpenRouter, LiteLLM) require vendor-prefixed model names that users shouldn't need to know. The `ModelCatalogResolver` interface searches each aggregator's dynamic model catalog to find the correct prefix automatically.

**How it works**: User types bare model name → resolver searches the provider's already-fetched model list → finds the exact match with vendor prefix → sends the prefixed name to the API.

**Current resolvers**:
- **OpenRouter**: `or@qwen3-coder-next` → searches catalog → sends `qwen/qwen3-coder-next`
- **LiteLLM**: `ll@gpt-4o` → searches model groups → finds `openai/gpt-4o` (prefix-strip match)
- **Static fallback**: `OPENROUTER_VENDOR_MAP` for cold starts when catalog isn't loaded yet

**Key design rules**:
- Exact match only — no fuzzy/normalized matching. Find the right prefix, don't guess the model.
- Dynamic catalogs (from provider APIs) are PRIMARY. Static map is cold-start fallback only.
- Resolution happens BEFORE handler construction (in `proxy-server.ts`), not inside adapters.
- Sync entry point (`resolveModelNameSync()`) — uses in-memory caches + `readFileSync`, no async propagation.

**Firebase slim catalog** (v7.0.0+): The `aggregators[]` field on model documents provides a typed multi-provider routing index. Each entry is `{ provider, externalId, confidence }`. CLI consumers can look up `provider → externalId` directly instead of walking the `sources` array. The catalog backend lives in the [models-index](https://github.com/MadAppGang/models-index) repo.

**Adding a new aggregator resolver**: Implement `ModelCatalogResolver` interface in `providers/catalog-resolvers/`, register in `model-catalog-resolver.ts`. No changes to proxy-server or provider-resolver needed.

**Architecture doc**: `ai-docs/sessions/dev-arch-20260305-104836-a48a463d/architecture.md`

## Local Model Support

Claudish supports local models via:
- **Ollama**: `claudish --model ollama@llama3.2` (or `ollama@llama3.2:3` for concurrency)
- **LM Studio**: `claudish --model lmstudio@model-name`
- **Custom URLs**: `claudish --model http://localhost:11434/model`

### Context Tracking for Local Models

Local model APIs (LM Studio, Ollama) report `prompt_tokens` as the **full conversation context** each request, not incremental tokens. The `writeTokenFile` function uses assignment (`=`) not accumulation (`+=`) for input tokens to handle this correctly.

## Custom Endpoints (v7.0.0+)

Define named custom endpoints in `~/.claudish/config.json` under the `customEndpoints` key. Each endpoint registers as a provider prefix usable with `@` syntax.

### Config schema

**Simple endpoint** (most common):
```json
{
  "customEndpoints": {
    "my-vllm": {
      "kind": "simple",
      "url": "http://gpu-box:8000",
      "format": "openai",
      "apiKey": "${VLLM_API_KEY}",
      "modelPrefix": "my-org/",
      "models": ["llama3.1-70b", "qwen2.5-72b"]
    }
  }
}
```

**Complex endpoint** (full control):
```json
{
  "customEndpoints": {
    "corp-proxy": {
      "kind": "complex",
      "displayName": "Corporate LLM Proxy",
      "transport": "openai",
      "baseUrl": "https://llm.corp.internal",
      "apiPath": "/api/v2/chat/completions",
      "apiKey": "${CORP_LLM_KEY}",
      "authScheme": "X-Api-Key",
      "headers": { "X-Team": "platform" },
      "streamFormat": "openai-sse",
      "modelPrefix": "",
      "models": ["gpt-4o", "claude-sonnet"]
    }
  }
}
```

Use as: `claudish --model my-vllm@llama3.1-70b "task"` or `claudish --model corp-proxy@gpt-4o "task"`.

### Key details

- **`${VAR_NAME}` expansion**: The `apiKey` field expands environment variables at startup. Use this instead of hardcoding secrets in config.
- **Zod validation**: Claudish validates all custom endpoints at proxy startup. Invalid entries emit a stderr warning and are skipped — they don't crash the proxy.
- **Runtime registration**: Endpoints call `registerRuntimeProvider()` and `registerRuntimeProfile()` to inject themselves into the provider resolver and transport layers.
- **`models` field** (optional): When present, limits the endpoint to listed models. Omit to allow any model name.
- **`modelPrefix` field** (optional): Prepended to the user-specified model name before sending to the API.

## Three-Layer Adapter Architecture (v5.14.0+)

The translation pipeline has three decoupled layers:

### Layer 1: FormatConverter — wire format translation
Translates between Claude API format and target model's wire format (messages, tools, payload).
Each converter declares its stream format via `getStreamFormat()`.
- **Interface**: `adapters/format-converter.ts`
- **Implementations**: OpenAIAdapter, AnthropicPassthroughAdapter, GeminiAdapter, CodexAdapter, OllamaCloudAdapter, LiteLLMAdapter
- **Message/tool conversion**: `handlers/shared/format/openai-messages.ts`, `openai-tools.ts`

### Layer 2: ModelTranslator — model dialect translation
Translates model-specific dialect differences (context windows, thinking→reasoning_effort, vision rules).
- **Interface**: `adapters/model-translator.ts`
- **Implementations**: GLMAdapter, GrokAdapter, MiniMaxAdapter, DeepSeekAdapter, QwenAdapter, CodexAdapter
- **Selection**: `AdapterManager` auto-selects based on model ID

### Layer 3: ProviderTransport — HTTP transport
Handles auth, endpoints, headers, rate limiting. Optionally overrides stream format for aggregators.
- **Interface**: `providers/transport/types.ts`
- **Stream format override**: LiteLLM and OpenRouter implement `overrideStreamFormat()` → `"openai-sse"`

### Composition in ComposedHandler
```
ComposedHandler = FormatConverter (explicit adapter) + ModelTranslator (auto-selected) + ProviderTransport
```

**Stream parser selection** (3-tier priority):
```typescript
transport.overrideStreamFormat() ?? modelAdapter.getStreamFormat() ?? providerAdapter.getStreamFormat()
```

**Adding a new provider**: Add one entry to `PROVIDER_PROFILES` table in `providers/provider-profiles.ts`.
**Adding a new model**: Create a ModelTranslator adapter, register in `adapters/adapter-manager.ts`.
**Verifying wiring**: `claudish --probe <model>` shows the full adapter composition.

### Stream Parsers
Located in `handlers/shared/stream-parsers/`:
- `openai-sse.ts` — OpenAI SSE → Claude SSE (used by most providers)
- `anthropic-sse.ts` — Anthropic SSE passthrough (MiniMax, Kimi direct)
- `gemini-sse.ts` — Gemini SSE → Claude SSE
- `ollama-jsonl.ts` — Ollama JSONL → Claude SSE
- `openai-responses-sse.ts` — OpenAI Responses API → Claude SSE (Codex)

## Debug Logging

Debug logging is behind the `--debug` flag and outputs to `logs/` directory. It's disabled by default.
Keep full debug logging (including empty chunks, raw deltas) in log files — needed to understand real model streaming behavior. Suppress noise at the registration/initialization level (e.g., conditional middleware), not at the streaming data level.

### Raw SSE Capture (v5.14.0+)

When `--debug` is active, both stream parsers log raw SSE events:
- `[SSE:openai] {...}` — every OpenAI SSE data line
- `[SSE:anthropic] {...}` — every Anthropic SSE data line

These are greppable and extractable into test fixtures for regression testing.

## Debugging Failed Model Translations

When a model produces wrong output (0 bytes, garbled, wrong format), use this workflow:

### 1. Reproduce with --debug
```bash
claudish --model minimax-m2.5 --debug "say hello"
# Debug log written to logs/claudish_YYYY-MM-DD_HH-MM-SS.log
```

### 2. Verify wiring with --probe
```bash
claudish --probe minimax-m2.5
# Shows: transport, format adapter, model translator, stream format, overrides
```

### 3. Analyze the debug log
Use the `/debug-logs` slash command in Claude Code:
```
/debug-logs logs/claudish_2026-03-17_09-41-32.log
```

This command:
1. Reads the log and counts text chunks, tool calls, HTTP errors, fallback chains
2. Diagnoses the failure mode (no SSE content, text but 0 stdout, wrong parser, etc.)
3. Extracts SSE fixtures from `[SSE:*]` lines using `test-fixtures/extract-sse-from-log.ts`
4. Adds a regression test to `format-translation.test.ts`
5. Runs tests to confirm the regression is captured

### 4. Extract fixtures manually (alternative)
```bash
bun run packages/cli/src/test-fixtures/extract-sse-from-log.ts logs/claudish_*.log
# Creates: test-fixtures/sse-responses/<model>-<format>-turn<N>.sse
```

### 5. Run format translation tests
```bash
bun test packages/cli/src/format-translation.test.ts
```

## Channel Mode (v6.4.0+)

The MCP server supports a channel mode that enables async model sessions with push notifications.

### Architecture

Uses the low-level `Server` class (not `McpServer`) from `@modelcontextprotocol/sdk/server/index.js` to declare `experimental: { 'claude/channel': {} }` capability. The SDK's `assertNotificationCapability()` has no default case — custom notification methods like `notifications/claude/channel` pass through.

### Components (`packages/cli/src/channel/`)

- **SessionManager** — spawns `claudish --model X --stdin --quiet` child processes, tracks lifecycle, enforces timeouts
- **SignalWatcher** — per-session state machine (starting→running→tool_executing→waiting_for_input→completed/failed/cancelled)
- **ScrollbackBuffer** — in-memory ring buffer (2000 lines) for session output

### MCP Tools (11 total)

- **Low-level** (4): `run_prompt`, `list_models`, `search_models`, `compare_models`
- **Agentic** (2): `team`, `report_error`
- **Channel** (5): `create_session`, `send_input`, `get_output`, `cancel_session`, `list_sessions`

Tool gating via `CLAUDISH_MCP_TOOLS` env var: `all` (default), `low-level`, `agentic`, `channel`.

### Tool Registration Pattern

Uses a `ToolDefinition[]` registry with raw JSON Schema (not Zod). Two `setRequestHandler` calls replace McpServer's ergonomic API:
- `ListToolsRequestSchema` → returns filtered tool list
- `CallToolRequestSchema` → dispatches to handler by name

### Channel Notifications

`server.notification({ method: "notifications/claude/channel", params: { content, meta } })` — pushed by SessionManager's `onStateChange` callback on state transitions.

### Testing

```bash
bun test --cwd . ./packages/cli/src/channel/*.test.ts
```

59 tests across 4 files: scrollback-buffer (11), signal-watcher (12), session-manager (21), e2e-channel (15).

E2E tests use `--strict-mcp-config --bare --dangerously-skip-permissions` for isolation. SessionManager tests use a fake-claudish PATH shim (`channel/test-helpers/fake-claudish.ts`).

## Test Infrastructure

### Format Translation Test Harness
`packages/cli/src/format-translation.test.ts` — SSE replay tests for the full translation pipeline.

**Fixture-based**: Each `.sse` file in `test-fixtures/sse-responses/` is a captured SSE stream from a real provider response. Tests replay fixtures through the stream parser and assert correct Claude SSE output.

**Helpers**: `parseClaudeSseStream()`, `extractText()`, `extractToolNames()`, `extractStopReason()`, `fixtureToResponse()`

**Adding regression tests**: After extracting fixtures from a debug log, add a `describe("Regression: <model>")` block. Template is at the bottom of the test file.

## Version Bumping Checklist

When releasing a new version, update ALL of these locations:
1. `package.json` (root monorepo version)
2. `packages/cli/package.json` (npm-published package - **CI/CD publishes from here**)
3. `packages/cli/src/version.ts` (fallback VERSION constant — moved from cli.ts in v7.0.0)

The fallback VERSION in version.ts ensures compiled binaries (Homebrew, standalone) display the correct version when package.json isn't available. The `packages/cli/package.json` version is what npm publishes - if it's not updated, npm publish will fail.

## Learned Preferences

### Tools & Commands
<!-- learned: 2026-03-28 session: 03cd7cc5 source: repeated_pattern -->
- Use `bun` for all package management and scripts (`bun run build`, `bun test`, etc.) — not npm or yarn
<!-- learned: 2026-04-06 session: df311293 source: repeated_pattern -->
- Use Grep/grep tool for code investigation instead of mnemex — prefer built-in search tools during investigation phases

### Workflow
<!-- learned: 2026-04-06 session: df311293 source: explicit_rule -->
- Don't run claudish directly in main bash — use dedicated channel sessions or `/delegate`


================================================
FILE: README.md
================================================
<div align="center">

# 🔮 Claudish

### Claude Code. Any Model.

[![npm version](https://img.shields.io/npm/v/claudish.svg?style=flat-square&color=00D4AA)](https://www.npmjs.com/package/claudish)
[![license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE)
[![Claude Code](https://img.shields.io/badge/Claude_Code-Compatible-d97757?style=flat-square)](https://claude.ai/claude-code)

**Use your existing AI subscriptions with Claude Code.** Works with Anthropic Max, Gemini Advanced, ChatGPT Plus/Codex, Kimi, GLM, OllamaCloud — plus 580+ models via OpenRouter and local models for complete privacy.

[Website](https://claudish.com) · [Documentation](https://github.com/MadAppGang/claudish/blob/main/docs/index.md) · [Report Bug](https://github.com/MadAppGang/claudish/issues)

</div>

---

**Claudish** (Claude-ish) is a CLI tool that allows you to run Claude Code with any AI model by proxying requests through a local Anthropic API-compatible server.

**Supported Providers:**
- **Cloud:** OpenRouter (580+ models), Google Gemini, OpenAI, MiniMax, Kimi, GLM, Z.AI, OllamaCloud, OpenCode Zen
- **Local:** Ollama, LM Studio, vLLM, MLX
- **Enterprise:** Vertex AI (Google Cloud)

## Use Your Existing AI Subscriptions

**Stop paying for multiple AI subscriptions.** Claudish lets you use subscriptions you already have with Claude Code's powerful interface:

| Your Subscription | Command |
|-------------------|---------|
| **Anthropic Max** | Native support (just use `claude`) |
| **Gemini Advanced** | `claudish --model g@gemini-3-pro-preview` |
| **ChatGPT Plus/Codex** | `claudish --model oai@gpt-5.3` or `oai@gpt-5.3-codex` |
| **Kimi** | `claudish --model kimi@kimi-k2.5` |
| **GLM** | `claudish --model glm@GLM-4.7` |
| **MiniMax** | `claudish --model mm@minimax-m2.1` |
| **OllamaCloud** | `claudish --model oc@qwen3-next` |
| **OpenCode Zen Go** | `claudish --model zgo@glm-5` |

**100% Offline Option — Your code never leaves your machine:**
```bash
claudish --model ollama@qwen3-coder:latest "your task"
```

## Bring Your Own Key (BYOK)

Claudish is a **BYOK AI coding assistant**:
- ✅ Use API keys you already have
- ✅ No additional subscription fees
- ✅ Full cost control — pay only for what you use
- ✅ Works with any provider
- ✅ Switch models mid-session

## Features

- ✅ **Multi-provider support** - OpenRouter, Gemini, Vertex AI, OpenAI, OllamaCloud, and local models
- ✅ **New routing syntax** - Use `provider@model[:concurrency]` for explicit routing (e.g., `google@gemini-2.0-flash`)
- ✅ **Native auto-detection** - Models like `gpt-4o`, `gemini-2.0-flash`, `llama-3.1-70b` route to their native APIs automatically
- ✅ **Direct API access** - Google, OpenAI, MiniMax, Kimi, GLM, Z.AI, OllamaCloud, Poe with direct billing
- ✅ **Vertex AI Model Garden** - Access Google + partner models (MiniMax, Mistral, DeepSeek, Qwen, OpenAI OSS)
- ✅ **Local model support** - Ollama, LM Studio, vLLM, MLX with `ollama@`, `lmstudio@` syntax and concurrency control
- ✅ **Cross-platform** - Works with both Node.js and Bun (v1.3.0+)
- ✅ **Universal compatibility** - Use with `npx` or `bunx` - no installation required
- ✅ **Interactive setup** - Prompts for API key and model if not provided (zero config!)
- ✅ **Monitor mode** - Proxy to real Anthropic API and log all traffic (for debugging)
- ✅ **Protocol compliance** - 1:1 compatibility with Claude Code communication protocol
- ✅ **Headless mode** - Automatic print mode for non-interactive execution
- ✅ **Quiet mode** - Clean output by default (no log pollution)
- ✅ **JSON output** - Structured data for tool integration
- ✅ **Real-time streaming** - See Claude Code output as it happens
- ✅ **Parallel runs** - Each instance gets isolated proxy
- ✅ **Autonomous mode** - Bypass all prompts with flags
- ✅ **Context inheritance** - Runs in current directory with same `.claude` settings
- ✅ **Claude Code flag passthrough** - Forward any Claude Code flag (`--agent`, `--effort`, `--permission-mode`, etc.) in any order
- ✅ **Vision proxy** - Non-vision models automatically get image descriptions via Claude, so every model can "see"

## Installation

### Quick Install

```bash
# Shell script (Linux/macOS)
curl -fsSL https://raw.githubusercontent.com/MadAppGang/claudish/main/install.sh | bash

# Homebrew (macOS)
brew tap MadAppGang/tap && brew install claudish

# npm
npm install -g claudish

# Bun
bun install -g claudish
```

### Prerequisites

- [Claude Code](https://claude.com/claude-code) - Claude CLI must be installed
- At least one API key:
  - [OpenRouter API Key](https://openrouter.ai/keys) - Access 100+ models (free tier available)
  - [Google Gemini API Key](https://aistudio.google.com/apikey) - For direct Gemini access
  - [OpenAI API Key](https://platform.openai.com/api-keys) - For direct OpenAI access
  - [OllamaCloud API Key](https://ollama.com/account) - For cloud-hosted Ollama models (`oc/` prefix)
  - Or local models (Ollama, LM Studio) - No API key needed

### Other Install Options

**Use without installing:**

```bash
npx claudish@latest --model x-ai/grok-code-fast-1 "your prompt"
bunx claudish@latest --model x-ai/grok-code-fast-1 "your prompt"
```

**Install from source:**

```bash
git clone https://github.com/MadAppGang/claudish.git
cd claudish
bun install && bun run build && bun link
```

## Quick Start

### Step 0: Initialize Claudish Skill (First Time Only)

```bash
# Navigate to your project directory
cd /path/to/your/project

# Install Claudish skill for automatic best practices
claudish --init

# Reload Claude Code to discover the skill
```

**What this does:**
- ✅ Installs Claudish usage skill in `.claude/skills/claudish-usage/`
- ✅ Enables automatic sub-agent delegation
- ✅ Enforces file-based instruction patterns
- ✅ Prevents context window pollution

**After running --init**, Claude will automatically:
- Use sub-agents when you mention external models (Grok, GPT-5, etc.)
- Follow best practices for Claudish usage
- Suggest specialized agents for different tasks

### Option 1: Interactive Mode (Easiest)

```bash
# Just run it - will prompt for API key and model
claudish

# Enter your OpenRouter API key when prompted
# Select a model from the list
# Start coding!
```

### Option 2: With Environment Variables

```bash
# Set up environment
export OPENROUTER_API_KEY=sk-or-v1-...     # For OpenRouter models
export GEMINI_API_KEY=...                   # For direct Google API
export OPENAI_API_KEY=sk-...                # For direct OpenAI API
export ANTHROPIC_API_KEY=sk-ant-api03-placeholder  # Required placeholder

# Run with auto-detected model
claudish --model gpt-4o "implement user authentication"     # → OpenAI
claudish --model gemini-2.0-flash "add tests"               # → Google

# Or with explicit provider
claudish --model openrouter@anthropic/claude-3.5-sonnet "review code"
```

**Note:** In interactive mode, if `OPENROUTER_API_KEY` is not set, you'll be prompted to enter it. This makes first-time usage super simple!

## AI Agent Usage

**For AI agents running within Claude Code:** Use the dedicated AI agent guide for comprehensive instructions on file-based patterns and sub-agent delegation.

```bash
# Print complete AI agent usage guide
claudish --help-ai

# Save guide to file for reference
claudish --help-ai > claudish-agent-guide.md
```

**Quick Reference for AI Agents:**

### Main Workflow for AI Agents

1. **Get available models:**
   ```bash
   # List all models or search
   claudish --models
   claudish --models gemini

   # Get top recommended models (JSON)
   claudish --top-models --json
   ```

2. **Run Claudish through sub-agent** (recommended pattern):
   ```typescript
   // Don't run Claudish directly in main conversation
   // Use Task tool to delegate to sub-agent
   const result = await Task({
     subagent_type: "general-purpose",
     description: "Implement feature with Grok",
     prompt: `
   Use Claudish to implement feature with Grok model.

   STEPS:
   1. Create instruction file: /tmp/claudish-task-${Date.now()}.md
   2. Write feature requirements to file
   3. Run: claudish --model x-ai/grok-code-fast-1 --stdin < /tmp/claudish-task-*.md
   4. Read result and return ONLY summary (2-3 sentences)

   DO NOT return full implementation. Keep response under 300 tokens.
     `
   });
   ```

3. **File-based instruction pattern** (avoids context pollution):
   ```typescript
   // Write instructions to file
   const instructionFile = `/tmp/claudish-task-${Date.now()}.md`;
   const resultFile = `/tmp/claudish-result-${Date.now()}.md`;

   await Write({ file_path: instructionFile, content: `
   # Task
   Your task description here

   # Output
   Write results to: ${resultFile}
   ` });

   // Run Claudish with stdin
   await Bash(`claudish --model x-ai/grok-code-fast-1 --stdin < ${instructionFile}`);

   // Read result
   const result = await Read({ file_path: resultFile });

   // Return summary only
   return extractSummary(result);
   ```

**Key Principles:**
- ✅ Use file-based patterns to avoid context window pollution
- ✅ Delegate to sub-agents instead of running directly
- ✅ Return summaries only (not full conversation transcripts)
- ✅ Choose appropriate model for task (see `--models` or `--top-models`)

**Resources:**
- Full AI agent guide: `claudish --help-ai`
- Skill document: `skills/claudish-usage/SKILL.md` (in repository root)
- Model integration: `skills/claudish-integration/SKILL.md` (in repository root)

## Usage

### Basic Syntax

```bash
claudish [OPTIONS] <claude-args...>
```

### Options

> For the exhaustive reference with all details, see [Settings Reference](docs/settings-reference.md).

| Flag | Short | Description | Default |
|------|-------|-------------|---------|
| `--model <model>` | `-m` | Model to use (`provider@model` syntax) | Interactive selector |
| `--default-provider <name>` | | Default provider for bare model routing (v7.0.0+) | Auto-detected |
| `--model-opus <model>` | | Model for Opus role (planning, complex tasks) | |
| `--model-sonnet <model>` | | Model for Sonnet role (default coding) | |
| `--model-haiku <model>` | | Model for Haiku role (fast tasks) | |
| `--model-subagent <model>` | | Model for sub-agents (Task tool) | |
| `--profile <name>` | `-p` | Named profile for model mapping | Default profile |
| `--interactive` | `-i` | Interactive mode (persistent session) | Auto when no prompt |
| `--auto-approve` | `-y` | Skip permission prompts | `false` |
| `--no-auto-approve` | | Explicitly enable permission prompts | |
| `--dangerous` | | Pass `--dangerouslyDisableSandbox` | `false` |
| `--port <port>` | | Proxy server port | Random (3000-9000) |
| `--debug` | `-d` | Enable debug logging to `logs/` | `false` |
| `--log-level <level>` | | Log verbosity: `debug`, `info`, `minimal` | `info` |
| `--quiet` | `-q` | Suppress `[claudish]` messages | Default in single-shot |
| `--verbose` | `-v` | Show `[claudish]` messages | Default in interactive |
| `--json` | | JSON output for tool integration (implies `--quiet`) | `false` |
| `--stdin` | | Read prompt from stdin | `false` |
| `--free` | | Show only free models in selector | `false` |
| `--monitor` | | Proxy to real Anthropic API and log traffic | `false` |
| `--summarize-tools` | | Summarize tool descriptions (for local models) | `false` |
| `--cost-tracker` | | Enable cost tracking (enables monitor mode) | `false` |
| `--audit-costs` | | Show cost analysis report | |
| `--reset-costs` | | Reset accumulated cost statistics | |
| `--models [query]` | `-s` | List all models or fuzzy search | |
| `--top-models` | | Show curated recommended models | |
| `--force-update` | | Force refresh model cache | |
| `--init` | | Install Claudish skill in current project | |
| `--mcp` | | Run as MCP server | |
| `--gemini-login` | | Login to Gemini Code Assist via OAuth | |
| `--gemini-logout` | | Clear Gemini OAuth credentials | |
| `--kimi-login` | | Login to Kimi via OAuth | |
| `--kimi-logout` | | Clear Kimi OAuth credentials | |
| `--help-ai` | | Show AI agent usage guide | |
| `--version` | | Show version | |
| `--help` | `-h` | Show help message | |
| `--` | | Everything after passes to Claude Code | |

**Flag passthrough**: Any unrecognized flag is automatically forwarded to Claude Code (e.g., `--agent`, `--effort`, `--permission-mode`).

### Environment Variables

Claudish automatically loads `.env` from the current directory at startup. For the full list, see [Settings Reference](docs/settings-reference.md).

#### API Keys (at least one required for cloud models)

| Variable | Provider | Aliases |
|----------|----------|---------|
| `OPENROUTER_API_KEY` | OpenRouter (default backend, 580+ models) | |
| `GEMINI_API_KEY` | Google Gemini (`g@`, `google@`) | |
| `OPENAI_API_KEY` | OpenAI (`oai@`) | |
| `MINIMAX_API_KEY` | MiniMax (`mm@`, `mmax@`) | |
| `MINIMAX_CODING_API_KEY` | MiniMax Coding Plan (`mmc@`) | |
| `MOONSHOT_API_KEY` | Kimi/Moonshot (`kimi@`) | `KIMI_API_KEY` |
| `KIMI_CODING_API_KEY` | Kimi Coding Plan (`kc@`) | Or OAuth via `--kimi-login` |
| `ZHIPU_API_KEY` | GLM/Zhipu (`glm@`) | `GLM_API_KEY` |
| `GLM_CODING_API_KEY` | GLM Coding Plan (`gc@`) | `ZAI_CODING_API_KEY` |
| `ZAI_API_KEY` | Z.AI (`zai@`) | |
| `OLLAMA_API_KEY` | OllamaCloud (`oc@`) | |
| `OPENCODE_API_KEY` | OpenCode Zen (`zen@`) — optional for free models | |
| `LITELLM_API_KEY` | LiteLLM (`ll@`) — requires `LITELLM_BASE_URL` | |
| `POE_API_KEY` | Poe (`poe@`) | |
| `VERTEX_API_KEY` | Vertex AI Express (`v@`) | |
| `VERTEX_PROJECT` | Vertex AI OAuth mode (`v@`) | `GOOGLE_CLOUD_PROJECT` |
| `ANTHROPIC_API_KEY` | Placeholder (suppresses Claude Code dialog) | |

#### Claudish Settings

| Variable | Description | Default |
|----------|-------------|---------|
| `CLAUDISH_MODEL` | Default model (overrides `ANTHROPIC_MODEL`) | Interactive selector |
| `CLAUDISH_PORT` | Default proxy port | Random (3000-9000) |
| `CLAUDISH_CONTEXT_WINDOW` | Override context window size (local models) | Auto-detected |
| `CLAUDISH_MODEL_OPUS` | Model for Opus role | |
| `CLAUDISH_MODEL_SONNET` | Model for Sonnet role | |
| `CLAUDISH_MODEL_HAIKU` | Model for Haiku role | |
| `CLAUDISH_MODEL_SUBAGENT` | Model for sub-agents | |
| `CLAUDISH_SUMMARIZE_TOOLS` | Summarize tool descriptions (`true`/`1`) | `false` |
| `CLAUDISH_TELEMETRY` | Override telemetry (`0`/`false`/`off` to disable) | From config |
| `CLAUDISH_LOCAL_MAX_PARALLEL` | Max concurrent local model requests (1-8) | `1` |
| `CLAUDISH_LOCAL_QUEUE_ENABLED` | Enable/disable local model queue | `true` |
| `CLAUDISH_DEFAULT_PROVIDER` | Default provider for bare model routing (v7.0.0+) | Auto-detected |
| `CLAUDISH_QWEN_NO_THINK` | Disable thinking for Qwen models (`1`) | |

#### Claude Code Compatibility

| Variable | Description |
|----------|-------------|
| `ANTHROPIC_MODEL` | Fallback for `CLAUDISH_MODEL` |
| `ANTHROPIC_DEFAULT_OPUS_MODEL` | Fallback for `CLAUDISH_MODEL_OPUS` |
| `ANTHROPIC_DEFAULT_SONNET_MODEL` | Fallback for `CLAUDISH_MODEL_SONNET` |
| `ANTHROPIC_DEFAULT_HAIKU_MODEL` | Fallback for `CLAUDISH_MODEL_HAIKU` |
| `CLAUDE_CODE_SUBAGENT_MODEL` | Fallback for `CLAUDISH_MODEL_SUBAGENT` |
| `CLAUDE_PATH` | Custom path to Claude Code binary |

#### Custom Endpoints

| Variable | Provider | Default |
|----------|----------|---------|
| `GEMINI_BASE_URL` | Gemini API | `https://generativelanguage.googleapis.com` |
| `OPENAI_BASE_URL` | OpenAI/Azure | `https://api.openai.com` |
| `MINIMAX_BASE_URL` | MiniMax | `https://api.minimax.io` |
| `MOONSHOT_BASE_URL` | Kimi/Moonshot | `https://api.moonshot.ai` |
| `ZHIPU_BASE_URL` | GLM/Zhipu | `https://open.bigmodel.cn` |
| `ZAI_BASE_URL` | Z.AI | `https://api.z.ai` |
| `OLLAMACLOUD_BASE_URL` | OllamaCloud | `https://ollama.com` |
| `OPENCODE_BASE_URL` | OpenCode Zen | `https://opencode.ai/zen` |
| `LITELLM_BASE_URL` | LiteLLM proxy server | _(required with LITELLM_API_KEY)_ |
| `OLLAMA_BASE_URL` | Ollama (local) | `http://localhost:11434` |
| `OLLAMA_HOST` | Alias for `OLLAMA_BASE_URL` | |
| `LMSTUDIO_BASE_URL` | LM Studio (local) | `http://localhost:1234` |
| `VLLM_BASE_URL` | vLLM (local) | `http://localhost:8000` |
| `MLX_BASE_URL` | MLX (local) | `http://127.0.0.1:8080` |

**Priority order**: CLI flags > `CLAUDISH_*` env vars > `ANTHROPIC_*` env vars > profile config > interactive selector.

**Important Notes:**
- Set `ANTHROPIC_API_KEY=sk-ant-api03-placeholder` (or any value) to suppress the Claude Code login dialog
- In interactive mode, if no API key is set, you'll be prompted to enter one

### Configuration Files

Claudish uses a two-scope configuration system:

| File | Scope | Purpose |
|------|-------|---------|
| `~/.claudish/config.json` | Global | Profiles, telemetry, routing rules (shared across projects) |
| `.claudish.json` | Local | Project-specific profiles and routing rules (overrides global) |
| `.env` | Local | Environment variables (auto-loaded at startup) |

**Profile configuration** (`~/.claudish/config.json`):

```json
{
  "version": "1.0.0",
  "defaultProfile": "default",
  "profiles": {
    "default": {
      "name": "default",
      "models": {
        "opus": "oai@gpt-5.3",
        "sonnet": "google@gemini-3-pro",
        "haiku": "mm@MiniMax-M2.1",
        "subagent": "google@gemini-2.0-flash"
      }
    }
  },
  "routing": {
    "kimi-*": ["kc", "kimi", "openrouter"],
    "glm-*": ["gc", "glm"],
    "*": ["litellm", "openrouter"]
  }
}
```

**Custom routing rules** map model name patterns to ordered provider fallback chains. Patterns support exact names, globs (`kimi-*`), and `*` catch-all. Local `.claudish.json` routing rules **replace** global rules entirely.

Manage profiles with:

```bash
claudish init [--local|--global]            # Setup wizard
claudish profile list [--local|--global]    # List profiles
claudish profile add [--local|--global]     # Add profile
claudish profile use <name>                 # Set default
claudish profile edit <name>                # Edit profile
```

For the complete configuration reference, see [Settings Reference](docs/settings-reference.md).

## Model Routing (v4.0.0+)

Claudish uses **`provider@model[:concurrency]`** syntax for explicit routing, plus **smart auto-detection** for native providers:

### New Syntax: `provider@model[:concurrency]`

```bash
# Explicit provider routing
claudish --model google@gemini-2.0-flash "quick task"
claudish --model openrouter@deepseek/deepseek-r1 "analysis"
claudish --model oai@gpt-4o "implement feature"
claudish --model ollama@llama3.2:3 "code review"  # 3 concurrent requests
```

### Provider Shortcuts

| Shortcut | Provider | API Key | Example |
|----------|----------|---------|---------|
| `g@`, `google@` | Google Gemini | `GEMINI_API_KEY` | `g@gemini-2.0-flash` |
| `oai@` | OpenAI Direct | `OPENAI_API_KEY` | `oai@gpt-4o` |
| `or@`, `openrouter@` | OpenRouter | `OPENROUTER_API_KEY` | `or@deepseek/deepseek-r1` |
| `mm@`, `mmax@` | MiniMax Direct | `MINIMAX_API_KEY` | `mm@MiniMax-M2.1` |
| `kimi@`, `moon@` | Kimi Direct | `MOONSHOT_API_KEY` | `kimi@kimi-k2` |
| `glm@`, `zhipu@` | GLM Direct | `ZHIPU_API_KEY` | `glm@glm-4` |
| `zai@` | Z.AI Direct | `ZAI_API_KEY` | `zai@glm-4` |
| `llama@`, `lc@`, `meta@` | OllamaCloud | `OLLAMA_API_KEY` | `llama@llama-3.1-70b` |
| `oc@` | OllamaCloud | `OLLAMA_API_KEY` | `oc@llama-3.1-70b` |
| `zen@` | OpenCode Zen (free/paid) | `OPENCODE_API_KEY` _(optional)_ | `zen@gpt-5-nano` |
| `zgo@`, `zengo@` | OpenCode Zen Go plan | `OPENCODE_API_KEY` | `zgo@glm-5` |
| `v@`, `vertex@` | Vertex AI | `VERTEX_API_KEY` | `v@gemini-2.5-flash` |
| `go@` | Gemini CodeAssist | _(OAuth)_ | `go@gemini-2.5-flash` |
| `poe@` | Poe | `POE_API_KEY` | `poe@GPT-4o` |
| `ollama@` | Ollama (local) | _(none)_ | `ollama@llama3.2` |
| `lms@`, `lmstudio@` | LM Studio (local) | _(none)_ | `lms@qwen2.5-coder` |
| `vllm@` | vLLM (local) | _(none)_ | `vllm@mistral-7b` |
| `mlx@` | MLX (local) | _(none)_ | `mlx@llama-3.2-3b` |

### Native Model Auto-Detection

When no provider is specified, Claudish auto-detects from model name:

| Model Pattern | Routes To | Example |
|---------------|-----------|---------|
| `gemini-*`, `google/*` | Google Gemini | `gemini-2.0-flash` |
| `gpt-*`, `o1-*`, `o3-*` | OpenAI Direct | `gpt-4o` |
| `llama-*`, `meta-llama/*` | OllamaCloud | `llama-3.1-70b` |
| `abab-*`, `minimax/*` | MiniMax Direct | `abab-6.5` |
| `kimi-*`, `moonshot-*` | Kimi Direct | `kimi-k2` |
| `glm-*`, `zhipu/*` | GLM Direct | `glm-4` |
| `poe:*` | Poe | `poe:GPT-4o` |
| `claude-*`, `anthropic/*` | Native Anthropic | `claude-sonnet-4` |
| **Unknown `vendor/model`** | **Error** | Use `openrouter@vendor/model` |

### Examples

```bash
# Auto-detected native routing (no prefix needed!)
claudish --model gemini-2.0-flash "quick task"      # → Google API
claudish --model gpt-4o "implement feature"          # → OpenAI API
claudish --model llama-3.1-70b "code review"         # → OllamaCloud

# Explicit provider routing
claudish --model google@gemini-2.5-pro "complex analysis"
claudish --model oai@o1 "complex reasoning"
claudish --model openrouter@deepseek/deepseek-r1 "deep analysis"

# OllamaCloud - cloud-hosted Llama models
claudish --model llama@llama-3.1-70b "code review"
claudish --model oc@llama-3.2-vision "analyze image"

# Vertex AI - Google Cloud
VERTEX_API_KEY=... claudish --model v@gemini-2.5-flash "task"
VERTEX_PROJECT=my-project claudish --model vertex@gemini-2.5-flash "OAuth mode"

# Local models with concurrency control
claudish --model ollama@llama3.2:3 "review"     # 3 concurrent requests
claudish --model ollama@llama3.2:0 "fast"       # No limit (bypass queue)

# Unknown vendors require explicit OpenRouter
claudish --model openrouter@qwen/qwen-2.5 "task"
claudish --model or@mistralai/mistral-large "analysis"
```

### Default provider (v7.0.0+)

The routing priority for bare model names (no `provider@` prefix) is configurable. By default, Claudish tries LiteLLM (if configured), then OpenRouter. Override this with `defaultProvider`:

```bash
# Set default provider globally
claudish config set defaultProvider openrouter

# Or via env var
export CLAUDISH_DEFAULT_PROVIDER=openrouter

# Or per-invocation
claudish --default-provider litellm --model minimax-m2.5 "task"
```

Precedence: `--default-provider` flag > `CLAUDISH_DEFAULT_PROVIDER` env var > config file `defaultProvider` > legacy LiteLLM auto-promotion > `OPENROUTER_API_KEY` detection > hardcoded `"openrouter"`.

Explicit `provider@model` syntax always bypasses `defaultProvider` and routes directly.

### Custom endpoints (v7.0.0+)

Register your own OpenAI-compatible endpoints in `~/.claudish/config.json`. See [Settings Reference](docs/settings-reference.md) for the full schema.

```json
{
  "customEndpoints": {
    "my-vllm": {
      "kind": "simple",
      "url": "http://gpu-box:8000/v1",
      "format": "openai",
      "apiKey": "none"
    }
  },
  "defaultProvider": "my-vllm"
}
```

Then route to it with: `claudish --model my-vllm@llama3 "task"`

### Legacy Syntax (Deprecated)

The old `prefix/model` syntax still works but shows deprecation warnings:

```bash
# Old (deprecated)          →  New (recommended)
claudish --model g/gemini-pro     →  claudish --model g@gemini-pro
claudish --model oai/gpt-4o       →  claudish --model oai@gpt-4o
claudish --model ollama/llama3.2  →  claudish --model ollama@llama3.2
```

## Curated Models

Top recommended models for development (v3.1.1):

| Model | Provider | Best For |
|-------|----------|----------|
| `openai/gpt-5.3` | OpenAI | **Default** - Most advanced reasoning |
| `minimax/minimax-m2.1` | MiniMax | Budget-friendly, fast |
| `z-ai/glm-4.7` | Z.AI | Balanced performance |
| `google/gemini-3-pro-preview` | Google | 1M context window |
| `moonshotai/kimi-k2-thinking` | MoonShot | Extended reasoning |
| `deepseek/deepseek-v3.2` | DeepSeek | Code specialist |
| `qwen/qwen3-vl-235b-a22b-thinking` | Alibaba | Vision + reasoning |

**Vertex AI Partner Models (MaaS - Google Cloud billing):**

| Model | Provider | Best For |
|-------|----------|----------|
| `vertex/minimax/minimax-m2-maas` | MiniMax | Fast, budget-friendly |
| `vertex/mistralai/codestral-2` | Mistral | Code specialist |
| `vertex/deepseek/deepseek-v3-2-maas` | DeepSeek | Deep reasoning |
| `vertex/qwen/qwen3-coder-480b-a35b-instruct-maas` | Qwen | Agentic coding |
| `vertex/openai/gpt-oss-120b-maas` | OpenAI | Open-weight reasoning |

List all models:

```bash
claudish --models              # List all OpenRouter models
claudish --models gemini       # Search for specific models
claudish --top-models          # Show curated recommendations
```

## Claude Code Flag Passthrough (NEW in v5.3.0)

Claudish forwards all unrecognized flags directly to Claude Code. This means any Claude Code flag works with claudish — no wrapper needed:

```bash
# Use Claude Code agents
claudish --model grok --agent code-review "review auth system"

# Control effort and permissions
claudish --model grok --effort high --permission-mode plan "design API"

# Set budget caps
claudish --model grok --max-budget-usd 0.50 "quick fix"

# Custom system prompts
claudish --model grok --append-system-prompt "Always respond in JSON" "list files"

# Restrict available tools
claudish --model grok --allowedTools "Read,Grep" "search for auth bugs"
```

Claudish flags (`--model`, `--stdin`, `--quiet`, `-y`, etc.) can appear in **any order** — they are always recognized regardless of position.

Use `--` when a Claude Code flag value starts with `-`:
```bash
claudish --model grok -- --system-prompt "-verbose logging" "task"
```

## Vision Proxy (NEW in v5.1.0)

**Every model can now "see" images** — even models without native vision support.

When you send an image to a non-vision model (like local Ollama models), Claudish automatically:

1. Detects that the model cannot process images
2. Sends each image to the Anthropic API (Claude Sonnet) for a rich description
3. Replaces the image block with `[Image Description: ...]` text
4. Forwards the enriched message to the target model

```
Claude Code → image + "what's in this?" → Claudish
                                             ↓
                              ┌──────────────────────────────┐
                              │ Model supports vision?       │
                              │  YES → pass image through    │
                              │  NO  → describe via Claude → │
                              │        replace with text     │
                              └──────────────────────────────┘
                                             ↓
                                      Target Model
```

**How it works:**
- Uses your existing `x-api-key` from Claude Code (no extra configuration)
- Each image is described in parallel (fast even with multiple images)
- 30-second timeout per image with graceful fallback to stripping
- Descriptions include text content, layout, colors, code, diagrams, and UI elements

**Example:**

```bash
# Local Ollama model (no vision) — images are automatically described
claudish --model ollama@llama3.2 "what's in this screenshot?"

# Vision-capable model — images pass through unchanged
claudish --model g@gemini-2.5-flash "what's in this screenshot?"
```

**Fallback behavior:** If the vision proxy fails (network error, timeout, API issue), Claudish falls back to stripping images — the request still goes through, just without image context.

## Status Line Display

Claudish automatically shows critical information in the Claude Code status bar - **no setup required!**

**Ultra-Compact Format:** `directory • model-id • $cost • ctx%`

**Visual Design:**
- 🔵 **Directory** (bright cyan, bold) - Where you are
- 🟡 **Model ID** (bright yellow) - Actual OpenRouter model ID
- 🟢 **Cost** (bright green) - Real-time session cost from OpenRouter
- 🟣 **Context** (bright magenta) - % of context window remaining
- ⚪ **Separators** (dim) - Visual dividers

**Examples:**
- `claudish • x-ai/grok-code-fast-1 • $0.003 • 95%` - Using Grok, $0.003 spent, 95% context left
- `my-project • openai/gpt-5-codex • $0.12 • 67%` - Using GPT-5, $0.12 spent, 67% context left
- `backend • minimax/minimax-m2 • $0.05 • 82%` - Using MiniMax M2, $0.05 spent, 82% left
- `test • openrouter/auto • $0.01 • 90%` - Using any custom model, $0.01 spent, 90% left

**Critical Tracking (Live Updates):**
- 💰 **Cost tracking** - Real-time USD from Claude Code session data
- 📊 **Context monitoring** - Percentage of model's context window remaining
- ⚡ **Performance optimized** - Ultra-compact to fit with thinking mode UI

**Thinking Mode Optimized:**
- ✅ **Ultra-compact** - Directory limited to 15 chars (leaves room for everything)
- ✅ **Critical first** - Most important info (directory, model) comes first
- ✅ **Smart truncation** - Long directories shortened with "..."
- ✅ **Space reservation** - Reserves ~40 chars for Claude's thinking mode UI
- ✅ **Color-coded** - Instant visual scanning
- ✅ **No overflow** - Fits perfectly even with thinking mode enabled

**Custom Model Support:**
- ✅ **ANY OpenRouter model** - Not limited to shortlist (e.g., `openrouter/auto`, custom models)
- ✅ **Actual model IDs** - Shows exact OpenRouter model ID (no translation)
- ✅ **Context fallback** - Unknown models use 100k context window (safe default)
- ✅ **Shortlist optimized** - Our recommended models have accurate context sizes
- ✅ **Future-proof** - Works with new models added to OpenRouter

**How it works:**
- Each Claudish instance creates a temporary settings file with custom status line
- Settings use `--settings` flag (doesn't modify global Claude Code config)
- Status line uses simple bash script with ANSI colors (no external dependencies!)
- Displays actual OpenRouter model ID from `CLAUDISH_ACTIVE_MODEL_NAME` env var
- Context tracking uses model-specific sizes for our shortlist, 100k fallback for others
- Temp files are automatically cleaned up when Claudish exits
- Each instance is completely isolated - run multiple in parallel!

**Per-instance isolation:**
- ✅ Doesn't modify `~/.claude/settings.json`
- ✅ Each instance has its own config
- ✅ Safe to run multiple Claudish instances in parallel
- ✅ Standard Claude Code unaffected
- ✅ Temp files auto-cleanup on exit
- ✅ No external dependencies (bash only, no jq!)

## Examples

### Basic Usage

```bash
# Simple prompt
claudish "fix the bug in user.ts"

# Multi-word prompt
claudish "implement user authentication with JWT tokens"
```

### With Specific Model

```bash
# Auto-detected native routing (model name determines provider)
claudish --model gpt-4o "refactor entire API layer"           # → OpenAI
claudish --model gemini-2.0-flash "quick fix"                 # → Google
claudish --model llama-3.1-70b "code review"                  # → OllamaCloud

# Explicit provider routing (new @ syntax)
claudish --model google@gemini-2.5-pro "complex analysis"
claudish --model oai@o1 "deep reasoning task"
claudish --model openrouter@deepseek/deepseek-r1 "analysis"   # Unknown vendors need explicit OR

# Local models with concurrency control
claudish --model ollama@llama3.2 "code review"
claudish --model ollama@llama3.2:3 "parallel processing"      # 3 concurrent
claudish --model lmstudio@qwen2.5-coder "implement dashboard UI"
```

### Autonomous Mode

Auto-approve is **enabled by default**. For fully autonomous mode, add `--dangerous`:

```bash
# Basic usage (auto-approve already enabled)
claudish "delete unused files"

# Fully autonomous (auto-approve + dangerous sandbox disabled)
claudish --dangerous "install dependencies"

# Disable auto-approve if you want prompts
claudish --no-auto-approve "make important changes"
```

### Custom Port

```bash
# Use specific port
claudish --port 3000 "analyze codebase"

# Or set default
export CLAUDISH_PORT=3000
claudish "your task"
```

### Passing Claude Flags

```bash
# Verbose mode
claudish "debug issue" --verbose

# Custom working directory
claudish "analyze code" --cwd /path/to/project

# Multiple flags
claudish --model openai/gpt-5.3-codex "task" --verbose --debug
```

### Monitor Mode

**NEW!** Claudish now includes a monitor mode to help you understand how Claude Code works internally.

```bash
# Enable monitor mode (requires real Anthropic API key)
claudish --monitor --debug "implement a feature"
```

**What Monitor Mode Does:**
- ✅ **Proxies to REAL Anthropic API** (not OpenRouter) - Uses your actual Anthropic API key
- ✅ **Logs ALL traffic** - Captures complete requests and responses
- ✅ **Both streaming and JSON** - Logs SSE streams and JSON responses
- ✅ **Debug logs to file** - Saves to `logs/claudish_*.log` when `--debug` is used
- ✅ **Pass-through proxy** - No translation, forwards as-is to Anthropic

**When to use Monitor Mode:**
- 🔍 Understanding Claude Code's API protocol
- 🐛 Debugging integration issues
- 📊 Analyzing Claude Code's behavior
- 🔬 Research and development

**Requirements:**
```bash
# Monitor mode requires a REAL Anthropic API key (not placeholder)
export ANTHROPIC_API_KEY='sk-ant-api03-...'

# Use with --debug to save logs to file
claudish --monitor --debug "your task"

# Logs are saved to: logs/claudish_TIMESTAMP.log
```

**Example Output:**
```
[Monitor] Server started on http://127.0.0.1:8765
[Monitor] Mode: Passthrough to real Anthropic API
[Monitor] All traffic will be logged for analysis

=== [MONITOR] Claude Code → Anthropic API Request ===
{
  "model": "claude-sonnet-4.5",
  "messages": [...],
  "max_tokens": 4096,
  ...
}
=== End Request ===

=== [MONITOR] Anthropic API → Claude Code Response (Streaming) ===
event: message_start
data: {"type":"message_start",...}

event: content_block_start
data: {"type":"content_block_start",...}
...
=== End Streaming Response ===
```

**Note:** Monitor mode charges your Anthropic account (not OpenRouter). Use `--debug` flag to save logs for analysis.

### Output Modes

Claudish supports three output modes for different use cases:

#### 1. Quiet Mode (Default in Single-Shot)

Clean output with no `[claudish]` logs - perfect for piping to other tools:

```bash
# Quiet by default in single-shot
claudish "what is 2+2?"
# Output: 2 + 2 equals 4.

# Use in pipelines
claudish "list 3 colors" | grep -i blue

# Redirect to file
claudish "analyze code" > analysis.txt
```

#### 2. Verbose Mode

Show all `[claudish]` log messages for debugging:

```bash
# Verbose mode
claudish --verbose "what is 2+2?"
# Output:
# [claudish] Starting Claude Code with openai/gpt-4o
# [claudish] Proxy URL: http://127.0.0.1:8797
# [claudish] Status line: dir • openai/gpt-4o • $cost • ctx%
# ...
# 2 + 2 equals 4.
# [claudish] Shutting down proxy server...
# [claudish] Done

# Interactive mode is verbose by default
claudish --interactive
```

#### 3. JSON Output Mode

Structured output perfect for automation and tool integration:

```bash
# JSON output (always quiet)
claudish --json "what is 2+2?"
# Output: {"type":"result","result":"2 + 2 equals 4.","total_cost_usd":0.068,"usage":{...}}

# Extract just the result with jq
claudish --json "list 3 colors" | jq -r '.result'

# Get cost and token usage
claudish --json "analyze code" | jq '{result, cost: .total_cost_usd, tokens: .usage.input_tokens}'

# Use in scripts
RESULT=$(claudish --json "check if tests pass" | jq -r '.result')
echo "AI says: $RESULT"

# Track costs across multiple runs
for task in task1 task2 task3; do
  claudish --json "$task" | jq -r '"\(.total_cost_usd)"'
done | awk '{sum+=$1} END {print "Total: $"sum}'
```

**JSON Output Fields:**
- `result` - The AI's response text
- `total_cost_usd` - Total cost in USD
- `usage.input_tokens` - Input tokens used
- `usage.output_tokens` - Output tokens used
- `duration_ms` - Total duration in milliseconds
- `num_turns` - Number of conversation turns
- `modelUsage` - Per-model usage breakdown

## How It Works

### Architecture

```
claudish "your prompt"
    ↓
1. Parse arguments (--model, --no-auto-approve, --dangerous, etc.)
2. Find available port (random or specified)
3. Start local proxy on http://127.0.0.1:PORT
4. Spawn: claude --auto-approve --env ANTHROPIC_BASE_URL=http://127.0.0.1:PORT
5. Proxy translates: Anthropic API → OpenRouter API
6. Stream output in real-time
7. Cleanup proxy on exit
```

### Request Flow

**Normal Mode (OpenRouter):**
```
Claude Code → Anthropic API format → Local Proxy → OpenRouter API format → OpenRouter
                                         ↓
Claude Code ← Anthropic API format ← Local Proxy ← OpenRouter API format ← OpenRouter
```

**Monitor Mode (Anthropic Passthrough):**
```
Claude Code → Anthropic API format → Local Proxy (logs) → Anthropic API
                                         ↓
Claude Code ← Anthropic API format ← Local Proxy (logs) ← Anthropic API
```

### Parallel Runs

Each `claudish` invocation:
- Gets a unique random port
- Starts isolated proxy server
- Runs independent Claude Code instance
- Cleans up on exit

This allows multiple parallel runs:

```bash
# Terminal 1
claudish --model x-ai/grok-code-fast-1 "task A"

# Terminal 2
claudish --model openai/gpt-5.3-codex "task B"

# Terminal 3
claudish --model minimax/minimax-m2 "task C"
```

## Extended Thinking Support

**NEW in v1.1.0**: Claudish now fully supports models with extended thinking/reasoning capabilities (Grok, o1, etc.) with complete Anthropic Messages API protocol compliance.

### Thinking Translation Model (v1.5.0)

Claudish includes a sophisticated **Thinking Translation Model** that aligns Claude Code's native thinking budget with the unique requirements of every major AI provider.

When you set a thinking budget in Claude (e.g., `budget: 16000`), Claudish automatically translates it:

| Provider | Model | Translation Logic |
| :--- | :--- | :--- |
| **OpenAI** | o1, o3 | Maps budget to `reasoning_effort` (minimal/low/medium/high) |
| **Google** | Gemini 3 | Maps to `thinking_level` (low/high) |
| **Google** | Gemini 2.x | Passes exact `thinking_budget` (capped at 24k) |
| **xAI** | Grok 3 Mini | Maps to `reasoning_effort` (low/high) |
| **Qwen** | Qwen 2.5 | Enables `enable_thinking` + exact budget |
| **MiniMax** | M2 | Enables `reasoning_split` (interleaved thinking) |
| **DeepSeek** | R1 | Automatically manages reasoning (params stripped for safety) |

This ensures you can use standard Claude Code thinking controls with **ANY** supported model, without worrying about API specificities.

### What is Extended Thinking?

Some AI models (like Grok and OpenAI's o1) can show their internal reasoning process before providing the final answer. This "thinking" content helps you understand how the model arrived at its conclusion.

### How Claudish Handles Thinking

Claudish implements the Anthropic Messages API's `interleaved-thinking` protocol:

**Thinking Blocks (Hidden):**
- Contains model's reasoning process
- Automatically collapsed in Claude Code UI
- Shows "Claude is thinking..." indicator
- User can expand to view reasoning

**Text Blocks (Visible):**
- Contains final response
- Displayed normally
- Streams incrementally

### Supported Models with Thinking

- ✅ **x-ai/grok-code-fast-1** - Grok's reasoning mode
- ✅ **openai/gpt-5-codex** - o1 reasoning (when enabled)
- ✅ **openai/o1-preview** - Full reasoning support
- ✅ **openai/o1-mini** - Compact reasoning
- ⚠️ Other models may support reasoning in future

### Technical Details

**Streaming Protocol (V2 - Protocol Compliant):**
```
1. message_start
2. content_block_start (text, index=0)      ← IMMEDIATE! (required)
3. ping
4. [If reasoning arrives]
   - content_block_stop (index=0)           ← Close initial empty block
   - content_block_start (thinking, index=1) ← Reasoning
   - thinking_delta events × N
   - content_block_stop (index=1)
5. content_block_start (text, index=2)      ← Response
6. text_delta events × M
7. content_block_stop (index=2)
8. message_delta + message_stop
```

**Critical:** `content_block_start` must be sent immediately after `message_start`, before `ping`. This is required by the Anthropic Messages API protocol for proper UI initialization.

**Key Features:**
- ✅ Separate thinking and text blocks (proper indices)
- ✅ `thinking_delta` vs `text_delta` event types
- ✅ Thinking content hidden by default
- ✅ Smooth transitions between blocks
- ✅ Full Claude Code UI compatibility

### UX Benefits

**Before (v1.0.0 - No Thinking Support):**
- Reasoning visible as regular text
- Confusing output with internal thoughts
- No progress indicators
- "All at once" message updates

**After (v1.1.0 - Full Protocol Support):**
- ✅ Reasoning hidden/collapsed
- ✅ Clean, professional output
- ✅ "Claude is thinking..." indicator shown
- ✅ Smooth incremental streaming
- ✅ Message headers/structure visible
- ✅ Protocol compliant with Anthropic Messages API

### Documentation

For complete protocol documentation, see:
- [STREAMING_PROTOCOL.md](./STREAMING_PROTOCOL.md) - Complete SSE protocol spec
- [PROTOCOL_FIX_V2.md](./PROTOCOL_FIX_V2.md) - Critical V2 protocol fix (event ordering)
- [COMPREHENSIVE_UX_ISSUE_ANALYSIS.md](./COMPREHENSIVE_UX_ISSUE_ANALYSIS.md) - Technical analysis
- [THINKING_BLOCKS_IMPLEMENTATION.md](./THINKING_BLOCKS_IMPLEMENTATION.md) - Implementation summary

## Dynamic Reasoning Support (NEW in v1.4.0)

**Claudish now intelligently adapts to ANY reasoning model!**

No more hardcoded lists or manual flags. Claudish dynamically queries OpenRouter metadata to enable thinking capabilities for any model that supports them.

### 🧠 Dynamic Thinking Features

1.  **Auto-Detection**:
    - Automatically checks model capabilities at startup
    - Enables Extended Thinking UI *only* when supported
    - Future-proof: Works instantly with new models (e.g., `deepseek-r1` or `minimax-m2`)

2.  **Smart Parameter Mapping**:
    - **Claude**: Passes token budget directly (e.g., 16k tokens)
    - **OpenAI (o1/o3)**: Translates budget to `reasoning_effort`
        - "ultrathink" (≥32k) → `high`
        - "think hard" (16k-32k) → `medium`
        - "think" (<16k) → `low`
    - **Gemini & Grok**: Preserves thought signatures and XML traces automatically

3.  **Universal Compatibility**:
    - Use "ultrathink" or "think hard" prompts with ANY supported model
    - Claudish handles the translation layer for you

## Context Scaling & Auto-Compaction

**NEW in v1.2.0**: Claudish now intelligently manages token counting to support ANY context window size (from 128k to 2M+) while preserving Claude Code's native auto-compaction behavior.

### The Challenge

Claude Code naturally assumes a fixed context window (typically 200k tokens for Sonnet).
- **Small Models (e.g., Grok 128k)**: Claude might overuse context and crash.
- **Massive Models (e.g., Gemini 2M)**: Claude would compact way too early (at 10% usage), wasting the model's potential.

### The Solution: Token Scaling

Claudish implements a "Dual-Accounting" system:

1. **Internal Scaling (For Claude):**
   - We fetch the *real* context limit from OpenRouter (e.g., 1M tokens).
   - We scale reported token usage so Claude *thinks* 1M tokens is 200k.
   - **Result:** Auto-compaction triggers at the correct *percentage* of usage (e.g., 90% full), regardless of the actual limit.

2. **Accurate Reporting (For You):**
   - The status line displays the **Real Unscaled Usage** and **Real Context %**.
   - You see specific costs and limits, while Claude remains blissfully unaware and stable.

**Benefits:**
- ✅ **Works with ANY model** size (128k, 1M, 2M, etc.)
- ✅ **Unlocks massive context** windows (Claude Code becomes 10x more powerful with Gemini!)
- ✅ **Prevents crashes** on smaller models (Grok)
- ✅ **Native behavior** (compaction just works)


## Development

### Project Structure

```
mcp/claudish/
├── src/
│   ├── index.ts              # Main entry point
│   ├── cli.ts                # CLI argument parser
│   ├── proxy-server.ts       # Hono-based proxy server
│   ├── transform.ts          # API format translation (from claude-code-proxy)
│   ├── claude-runner.ts      # Claude CLI runner (creates temp settings)
│   ├── port-manager.ts       # Port utilities
│   ├── config.ts             # Constants and defaults
│   ├── types.ts              # TypeScript types
│   └── services/
│       └── vision-proxy.ts   # Image description for non-vision models
├── tests/                    # Test files
├── package.json
├── tsconfig.json
└── biome.json
```

### Proxy Implementation

Claudish uses a **Hono-based proxy server** inspired by [claude-code-proxy](https://github.com/kiyo-e/claude-code-proxy):

- **Framework**: [Hono](https://hono.dev/) - Fast, lightweight web framework
- **API Translation**: Converts Anthropic API format ↔ OpenAI format
- **Streaming**: Full support for Server-Sent Events (SSE)
- **Tool Calling**: Handles Claude's tool_use ↔ OpenAI's tool_calls
- **Battle-tested**: Based on production-ready claude-code-proxy implementation

**Why Hono?**
- Native Bun support (no adapters needed)
- Extremely fast and lightweight
- Middleware support (CORS, logging, etc.)
- Works across Node.js, Bun, and Cloudflare Workers

### Build & Test

```bash
# Install dependencies
bun install

# Development mode
bun run dev "test prompt"

# Build
bun run build

# Lint
bun run lint

# Format
bun run format

# Type check
bun run typecheck

# Run tests
bun test
```

### Protocol Compliance Testing

Claudish includes a comprehensive snapshot testing system to ensure 1:1 compatibility with the official Claude Code protocol:

```bash
# Run snapshot tests (13/13 passing ✅)
bun test tests/snapshot.test.ts

# Full workflow: capture fixtures + run tests
./tests/snapshot-workflow.sh --full

# Capture new test fixtures from monitor mode
./tests/snapshot-workflow.sh --capture

# Debug SSE events
bun tests/debug-snapshot.ts
```

**What Gets Tested:**
- ✅ Event sequence (message_start → content_block_start → deltas → stop → message_delta → message_stop)
- ✅ Content block indices (sequential: 0, 1, 2, ...)
- ✅ Tool input streaming (fine-grained JSON chunks)
- ✅ Usage metrics (present in message_start and message_delta)
- ✅ Stop reasons (always present and valid)
- ✅ Cache metrics (creation and read tokens)

**Documentation:**
- [Quick Start Guide](./QUICK_START_TESTING.md) - Get started with testing
- [Snapshot Testing Guide](./SNAPSHOT_TESTING.md) - Complete testing documentation
- [Implementation Details](./ai_docs/IMPLEMENTATION_COMPLETE.md) - Technical implementation summary
- [Protocol Compliance Plan](./ai_docs/PROTOCOL_COMPLIANCE_PLAN.md) - Detailed compliance roadmap

### Install Globally

```bash
# Link for global use
bun run install:global

# Now use anywhere
claudish "your task"
```

## Troubleshooting

### "Claude Code CLI is not installed"

Install Claude Code:

```bash
npm install -g claude-code
# or visit: https://claude.com/claude-code
```

### "OPENROUTER_API_KEY environment variable is required"

Set your API key:

```bash
export OPENROUTER_API_KEY=sk-or-v1-...
```

Or add to your shell profile (`~/.zshrc`, `~/.bashrc`):

```bash
echo 'export OPENROUTER_API_KEY=sk-or-v1-...' >> ~/.zshrc
source ~/.zshrc
```

### "No available ports found"

Specify a custom port:

```bash
claudish --port 3000 "your task"
```

Or increase port range in `src/config.ts`.

### Proxy errors

Check OpenRouter API status:
- https://openrouter.ai/status

Verify your API key works:
- https://openrouter.ai/keys

### Status line not showing model

If the status line doesn't show the model name:

1. **Check if --settings flag is being passed:**
   ```bash
   # Look for this in Claudish output:
   # [claudish] Instance settings: /tmp/claudish-settings-{timestamp}.json
   ```

2. **Verify environment variable is set:**
   ```bash
   # Should be set automatically by Claudish
   echo $CLAUDISH_ACTIVE_MODEL_NAME
   # Should output something like: xAI/Grok-1
   ```

3. **Test status line command manually:**
   ```bash
   export CLAUDISH_ACTIVE_MODEL_NAME="xAI/Grok-1"
   cat > /dev/null && echo "[$CLAUDISH_ACTIVE_MODEL_NAME] 📁 $(basename "$(pwd)")"
   # Should output: [xAI/Grok-1] 📁 your-directory-name
   ```

4. **Check temp settings file:**
   ```bash
   # File is create
Download .txt
gitextract_7tdmfy1z/

├── .github/
│   ├── ISSUE_TRIAGE.md
│   ├── prompts/
│   │   ├── issue-comment-system.md
│   │   └── issue-triage-system.md
│   ├── release.yml
│   └── workflows/
│       ├── claude-code.yml
│       ├── issue-triage.yml
│       ├── release.yml
│       └── smoke-test.yml
├── .gitignore
├── AI_AGENT_GUIDE.md
├── CHANGELOG.md
├── CLAUDE.md
├── README.md
├── apps/
│   ├── .gitignore
│   └── ClaudishProxy/
│       ├── Package.swift
│       └── Sources/
│           ├── ApiKeyManager.swift
│           ├── BridgeManager.swift
│           ├── CertificateManager.swift
│           ├── ClaudishProxyApp.swift
│           ├── ModelProvider.swift
│           ├── Models.swift
│           ├── ProcessManager.swift
│           ├── ProfileManager.swift
│           ├── ProfilePicker.swift
│           ├── ProfilesSettingsView.swift
│           ├── SettingsView.swift
│           ├── StatsDatabase.swift
│           ├── StatsPanel.swift
│           ├── Theme.swift
│           └── UnifiedModelPicker.swift
├── biome.json
├── cliff.toml
├── design-references/
│   └── stats-panel-style.md
├── docs/
│   ├── advanced/
│   │   ├── automation.md
│   │   ├── cost-tracking.md
│   │   ├── environment.md
│   │   └── mtm-to-magmux-migration.md
│   ├── ai-integration/
│   │   └── for-agents.md
│   ├── api-key-architecture.md
│   ├── api-reference.md
│   ├── getting-started/
│   │   └── quick-start.md
│   ├── index.md
│   ├── models/
│   │   ├── choosing-models.md
│   │   └── model-mapping.md
│   ├── settings-reference.md
│   ├── three-layer-architecture.md
│   ├── troubleshooting.md
│   └── usage/
│       ├── interactive-mode.md
│       ├── magmux.md
│       ├── mcp-server.md
│       ├── monitor-mode.md
│       └── single-shot-mode.md
├── experiments/
│   └── tool-replacement-proxy-2026-04/
│       ├── README.md
│       ├── claudish-patch/
│       │   ├── native-handler-advisor.test.ts
│       │   ├── native-handler-advisor.ts
│       │   └── native-handler.patch
│       ├── evidence/
│       │   ├── evidence-index.ndjson
│       │   ├── evidence-req-advisor-enabled.json
│       │   ├── evidence-resp-advisor-enabled.ndjson
│       │   ├── evidence-stage1-swap.ndjson
│       │   ├── evidence-stage2-rewrite.ndjson
│       │   └── evidence-stage2-ui-transcript.txt
│       ├── journal/
│       │   └── 2026-04-10-to-15-investigation.md
│       ├── poc/
│       │   ├── 01-recording-proxy.ts
│       │   ├── 02-mock-advisor-proxy.ts
│       │   ├── 03-sdk-validation.ts
│       │   ├── 04-multi-turn-validation.ts
│       │   ├── 05-tool-loop-proxy.ts
│       │   ├── 06-sdk-e2e-validation.ts
│       │   └── README.md
│       └── research/
│           ├── 01-advisor-pattern-research.md
│           ├── 01-research-plan.md
│           ├── 02-proxy-replacement-architecture.md
│           ├── 03-how-to-enable-advisor.md
│           ├── 04-real-test-results.md
│           ├── 05-stage1-tool-swap.md
│           └── 06-stage2-tool-result-rewrite.md
├── install.sh
├── landingpage/
│   ├── .firebaserc
│   ├── .gitignore
│   ├── App.tsx
│   ├── README.md
│   ├── components/
│   │   ├── BlockLogo.tsx
│   │   ├── BridgeDiagram.tsx
│   │   ├── Changelog.tsx
│   │   ├── FeatureSection.tsx
│   │   ├── HeroSection.tsx
│   │   ├── MultiModelAnimation.tsx
│   │   ├── SmartRouting.tsx
│   │   ├── SubscriptionSection.tsx
│   │   ├── SupportSection.tsx
│   │   ├── TerminalWindow.tsx
│   │   ├── TypingAnimation.tsx
│   │   └── VisionSection.tsx
│   ├── constants.ts
│   ├── firebase.json
│   ├── firebase.ts
│   ├── index.html
│   ├── index.tsx
│   ├── metadata.json
│   ├── package.json
│   ├── pnpm-workspace.yaml
│   ├── public/
│   │   └── site.webmanifest
│   ├── tsconfig.json
│   ├── types.ts
│   └── vite.config.ts
├── package.json
├── packages/
│   ├── .gitignore
│   ├── cli/
│   │   ├── .gitignore
│   │   ├── AI_AGENT_GUIDE.md
│   │   ├── bin/
│   │   │   └── claudish.cjs
│   │   ├── package.json
│   │   ├── recommended-models.json
│   │   ├── scripts/
│   │   │   ├── generate-version.ts
│   │   │   ├── smoke/
│   │   │   │   ├── probes.ts
│   │   │   │   ├── providers.ts
│   │   │   │   ├── reporter.ts
│   │   │   │   └── types.ts
│   │   │   ├── smoke-test.ts
│   │   │   └── smoke.test.ts
│   │   ├── skills/
│   │   │   └── claudish-usage/
│   │   │       └── SKILL.md
│   │   ├── src/
│   │   │   ├── adapters/
│   │   │   │   ├── anthropic-api-format.ts
│   │   │   │   ├── api-format.ts
│   │   │   │   ├── base-api-format.ts
│   │   │   │   ├── codex-api-format.ts
│   │   │   │   ├── deepseek-model-dialect.ts
│   │   │   │   ├── dialect-manager.ts
│   │   │   │   ├── gemini-api-format.ts
│   │   │   │   ├── glm-model-dialect.ts
│   │   │   │   ├── grok-model-dialect.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── litellm-api-format.ts
│   │   │   │   ├── local-adapter.ts
│   │   │   │   ├── minimax-model-dialect.ts
│   │   │   │   ├── model-catalog.test.ts
│   │   │   │   ├── model-catalog.ts
│   │   │   │   ├── model-dialect.ts
│   │   │   │   ├── ollama-api-format.ts
│   │   │   │   ├── openai-api-format.ts
│   │   │   │   ├── openrouter-api-format.ts
│   │   │   │   ├── qwen-model-dialect.ts
│   │   │   │   ├── tool-name-utils.ts
│   │   │   │   └── xiaomi-model-dialect.ts
│   │   │   ├── auth/
│   │   │   │   ├── auth-commands.ts
│   │   │   │   ├── codex-oauth.ts
│   │   │   │   ├── gemini-oauth.ts
│   │   │   │   ├── kimi-oauth.ts
│   │   │   │   ├── oauth-manager.ts
│   │   │   │   ├── oauth-registry.ts
│   │   │   │   ├── quota-command.ts
│   │   │   │   └── vertex-auth.ts
│   │   │   ├── channel/
│   │   │   │   ├── e2e-channel.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── scrollback-buffer.test.ts
│   │   │   │   ├── scrollback-buffer.ts
│   │   │   │   ├── session-manager.test.ts
│   │   │   │   ├── session-manager.ts
│   │   │   │   ├── signal-watcher.test.ts
│   │   │   │   ├── signal-watcher.ts
│   │   │   │   ├── test-helpers/
│   │   │   │   │   └── fake-claudish.ts
│   │   │   │   └── types.ts
│   │   │   ├── claude-runner.ts
│   │   │   ├── cli-passthrough.test.ts
│   │   │   ├── cli.test.ts
│   │   │   ├── cli.ts
│   │   │   ├── config-command.ts
│   │   │   ├── config-schema.test.ts
│   │   │   ├── config-schema.ts
│   │   │   ├── config.ts
│   │   │   ├── default-provider.test.ts
│   │   │   ├── default-provider.ts
│   │   │   ├── diag-output.ts
│   │   │   ├── format-translation.test.ts
│   │   │   ├── glm-adapter.test.ts
│   │   │   ├── handlers/
│   │   │   │   ├── composed-handler.test.ts
│   │   │   │   ├── composed-handler.ts
│   │   │   │   ├── default-provider-e2e.test.ts
│   │   │   │   ├── fallback-handler.test.ts
│   │   │   │   ├── fallback-handler.ts
│   │   │   │   ├── native-handler-advisor.test.ts
│   │   │   │   ├── native-handler-advisor.ts
│   │   │   │   ├── native-handler.ts
│   │   │   │   ├── shared/
│   │   │   │   │   ├── anthropic-error.test.ts
│   │   │   │   │   ├── anthropic-error.ts
│   │   │   │   │   ├── format/
│   │   │   │   │   │   ├── identity-filter.ts
│   │   │   │   │   │   ├── openai-messages.ts
│   │   │   │   │   │   └── openai-tools.ts
│   │   │   │   │   ├── gemini-queue.ts
│   │   │   │   │   ├── gemini-schema.ts
│   │   │   │   │   ├── local-queue.ts
│   │   │   │   │   ├── openai-compat.ts
│   │   │   │   │   ├── openrouter-queue.ts
│   │   │   │   │   ├── remote-provider-types.ts
│   │   │   │   │   ├── stream-parsers/
│   │   │   │   │   │   ├── anthropic-sse.ts
│   │   │   │   │   │   ├── gemini-sse.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── ollama-jsonl.ts
│   │   │   │   │   │   ├── openai-responses-sse.ts
│   │   │   │   │   │   └── openai-sse.ts
│   │   │   │   │   ├── token-tracker.ts
│   │   │   │   │   ├── tool-call-recovery.ts
│   │   │   │   │   └── web-search-detector.ts
│   │   │   │   └── types.ts
│   │   │   ├── index.ts
│   │   │   ├── logger.ts
│   │   │   ├── mcp-server.ts
│   │   │   ├── middleware/
│   │   │   │   ├── gemini-thought-signature.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── manager.ts
│   │   │   │   └── types.ts
│   │   │   ├── model-catalog.test.ts
│   │   │   ├── model-loader.ts
│   │   │   ├── model-selector.ts
│   │   │   ├── native-anthropic-mapping.test.ts
│   │   │   ├── port-manager.ts
│   │   │   ├── probe/
│   │   │   │   ├── probe-results-printer.ts
│   │   │   │   ├── probe-tui-app.tsx
│   │   │   │   └── probe-tui-runtime.tsx
│   │   │   ├── profile-commands.ts
│   │   │   ├── profile-config.ts
│   │   │   ├── providers/
│   │   │   │   ├── all-models-cache.test.ts
│   │   │   │   ├── all-models-cache.ts
│   │   │   │   ├── api-key-map.ts
│   │   │   │   ├── api-key-provenance.ts
│   │   │   │   ├── auto-route-default-provider.test.ts
│   │   │   │   ├── auto-route.ts
│   │   │   │   ├── catalog-resolvers/
│   │   │   │   │   ├── litellm.ts
│   │   │   │   │   ├── openrouter.test.ts
│   │   │   │   │   ├── openrouter.ts
│   │   │   │   │   └── static-fallback.ts
│   │   │   │   ├── custom-endpoints-loader.test.ts
│   │   │   │   ├── custom-endpoints-loader.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── model-catalog-resolver.ts
│   │   │   │   ├── model-parser.ts
│   │   │   │   ├── probe-live.ts
│   │   │   │   ├── provider-definitions.test.ts
│   │   │   │   ├── provider-definitions.ts
│   │   │   │   ├── provider-profiles.ts
│   │   │   │   ├── provider-registry.ts
│   │   │   │   ├── provider-resolver.ts
│   │   │   │   ├── provider-routing.test.ts
│   │   │   │   ├── remote-provider-registry.ts
│   │   │   │   ├── routing-rules.test.ts
│   │   │   │   ├── routing-rules.ts
│   │   │   │   ├── runtime-providers.test.ts
│   │   │   │   ├── runtime-providers.ts
│   │   │   │   └── transport/
│   │   │   │       ├── anthropic-compat.test.ts
│   │   │   │       ├── anthropic-compat.ts
│   │   │   │       ├── gemini-apikey.ts
│   │   │   │       ├── gemini-codeassist.ts
│   │   │   │       ├── litellm.ts
│   │   │   │       ├── local.ts
│   │   │   │       ├── ollamacloud.ts
│   │   │   │       ├── openai-codex.ts
│   │   │   │       ├── openai.test.ts
│   │   │   │       ├── openai.ts
│   │   │   │       ├── openrouter.ts
│   │   │   │       ├── poe.ts
│   │   │   │       ├── types.ts
│   │   │   │       └── vertex-oauth.ts
│   │   │   ├── proxy-server.ts
│   │   │   ├── services/
│   │   │   │   ├── pricing-cache.ts
│   │   │   │   └── vision-proxy.ts
│   │   │   ├── stats-buffer.test.ts
│   │   │   ├── stats-buffer.ts
│   │   │   ├── stats-otlp.test.ts
│   │   │   ├── stats-otlp.ts
│   │   │   ├── stats.test.ts
│   │   │   ├── stats.ts
│   │   │   ├── team-cli.ts
│   │   │   ├── team-grid.e2e-helpers.ts
│   │   │   ├── team-grid.e2e.test.ts
│   │   │   ├── team-grid.ts
│   │   │   ├── team-orchestrator.test.ts
│   │   │   ├── team-orchestrator.ts
│   │   │   ├── team-timeout-repro.test.ts
│   │   │   ├── telemetry.test.ts
│   │   │   ├── telemetry.ts
│   │   │   ├── test-fixtures/
│   │   │   │   ├── extract-sse-from-log.ts
│   │   │   │   └── sse-responses/
│   │   │   │       ├── SEED-anthropic-text-only.sse
│   │   │   │       ├── SEED-anthropic-thinking.sse
│   │   │   │       ├── SEED-openai-text-only.sse
│   │   │   │       ├── SEED-openai-tool-call.sse
│   │   │   │       ├── minimax-m25-turn1-thinking-text-tool.sse
│   │   │   │       ├── minimax-m25-turn2-thinking-tool-only.sse
│   │   │   │       ├── minimax-m25-turn3-thinking-multichunk.sse
│   │   │   │       ├── regression-zai-glm5-instream-error.sse
│   │   │   │       └── regression-zai-glm5-usage.sse
│   │   │   ├── transform.ts
│   │   │   ├── tui/
│   │   │   │   ├── App.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   ├── panels/
│   │   │   │   │   ├── ApiKeysPanel.tsx
│   │   │   │   │   ├── ConfigViewPanel.tsx
│   │   │   │   │   ├── ProfilesPanel.tsx
│   │   │   │   │   ├── ProvidersPanel.tsx
│   │   │   │   │   ├── RoutingPanel.tsx
│   │   │   │   │   ├── StatsPanel.tsx
│   │   │   │   │   └── TelemetryPanel.tsx
│   │   │   │   ├── providers.ts
│   │   │   │   ├── test-provider.ts
│   │   │   │   └── theme.ts
│   │   │   ├── types.ts
│   │   │   ├── update-checker.ts
│   │   │   ├── update-command.ts
│   │   │   ├── utils.ts
│   │   │   ├── version.ts
│   │   │   └── zai-glm.e2e.test.ts
│   │   ├── tsconfig.json
│   │   └── tsconfig.tui.json
│   ├── macos-bridge/
│   │   ├── docs/
│   │   │   └── PROXY_TRAFFIC_FLOW.md
│   │   ├── package.json
│   │   ├── scripts/
│   │   │   ├── full-test.js
│   │   │   ├── simple-test.js
│   │   │   ├── test-claude-desktop.sh
│   │   │   ├── test-cycletls.ts
│   │   │   ├── test-full-interception.sh
│   │   │   └── test-proxy.sh
│   │   ├── src/
│   │   │   ├── auth.ts
│   │   │   ├── bridge.test.ts
│   │   │   ├── certificate-manager.ts
│   │   │   ├── config-manager.ts
│   │   │   ├── connect-handler.ts
│   │   │   ├── cycletls-manager.ts
│   │   │   ├── detection.ts
│   │   │   ├── http-parser.ts
│   │   │   ├── https-proxy-server.ts
│   │   │   ├── index.ts
│   │   │   ├── process-manager.ts
│   │   │   ├── routing-middleware.ts
│   │   │   ├── server.ts
│   │   │   └── types.ts
│   │   └── tsconfig.json
│   ├── magmux-darwin-arm64/
│   │   ├── .gitignore
│   │   ├── bin/
│   │   │   └── .gitkeep
│   │   └── package.json
│   ├── magmux-darwin-x64/
│   │   ├── .gitignore
│   │   ├── bin/
│   │   │   └── .gitkeep
│   │   └── package.json
│   ├── magmux-linux-arm64/
│   │   ├── .gitignore
│   │   ├── bin/
│   │   │   └── .gitkeep
│   │   └── package.json
│   └── magmux-linux-x64/
│       ├── .gitignore
│       ├── bin/
│       │   └── .gitkeep
│       └── package.json
├── recommended-models.json
├── scripts/
│   ├── generate-manifest.ts
│   ├── postinstall.cjs
│   └── update-models.ts
├── skills/
│   └── claudish-usage/
│       └── SKILL.md
├── test-mcp-e2e.ts
└── tsconfig.json
Download .txt
SYMBOL INDEX (1864 symbols across 192 files)

FILE: experiments/tool-replacement-proxy-2026-04/claudish-patch/native-handler-advisor.ts
  constant ADVISOR_SERVER_TOOL_TYPE (line 27) | const ADVISOR_SERVER_TOOL_TYPE = "advisor_20260301";
  constant ADVISOR_BETA_FLAG (line 28) | const ADVISOR_BETA_FLAG = "advisor-tool-2026-03-01";
  type AdvisorSwapConfig (line 30) | interface AdvisorSwapConfig {
  function loadAdvisorSwapConfig (line 37) | function loadAdvisorSwapConfig(): AdvisorSwapConfig {
  type AdvisorInfo (line 45) | interface AdvisorInfo {
  function swapAdvisorToolInBody (line 63) | function swapAdvisorToolInBody(
  function stripAdvisorBeta (line 115) | function stripAdvisorBeta(
  function logAdvisorEvent (line 137) | function logAdvisorEvent(
  function recordAdvisorEventsFromChunk (line 159) | function recordAdvisorEventsFromChunk(
  constant MAX_TRACKED (line 204) | const MAX_TRACKED = 256;
  function extractAdvisorToolUseIds (line 214) | function extractAdvisorToolUseIds(chunkText: string): void {
  function rememberAdvisorToolUseId (line 233) | function rememberAdvisorToolUseId(id: string): void {
  function _debug_getTrackedAdvisorIds (line 244) | function _debug_getTrackedAdvisorIds(): string[] {
  function _debug_resetTrackedAdvisorIds (line 249) | function _debug_resetTrackedAdvisorIds(): void {
  function rewriteAdvisorToolResults (line 262) | function rewriteAdvisorToolResults(
  function stubAdvisorAdvice (line 309) | function stubAdvisorAdvice(toolUseId: string): string {

FILE: experiments/tool-replacement-proxy-2026-04/poc/01-recording-proxy.ts
  constant LOG_DIR (line 25) | const LOG_DIR = join(import.meta.dir, "logs");
  constant UPSTREAM (line 28) | const UPSTREAM = "https://api.anthropic.com";
  constant PORT (line 29) | const PORT = 8787;
  function logIndex (line 36) | function logIndex(entry: Record<string, unknown>) {
  method fetch (line 46) | async fetch(req: Request): Promise<Response> {
  function safeParseJSON (line 214) | function safeParseJSON(s: string): unknown {
  type SSEEvent (line 222) | interface SSEEvent {
  function parseSSE (line 227) | function parseSSE(block: string): SSEEvent | null {

FILE: experiments/tool-replacement-proxy-2026-04/poc/02-mock-advisor-proxy.ts
  constant LOG_DIR (line 25) | const LOG_DIR = join(import.meta.dir, "logs");
  constant PORT (line 27) | const PORT = 8788;
  constant MESSAGE_ID (line 32) | const MESSAGE_ID = "msg_poc_advisor_01";
  constant ADVISOR_ID (line 33) | const ADVISOR_ID = "srvtoolu_poc_advisor_01";
  constant MODEL (line 34) | const MODEL = "claude-sonnet-4-6";
  method fetch (line 53) | async fetch(req: Request): Promise<Response> {
  function buildNonStreamingResponse (line 100) | function buildNonStreamingResponse() {
  function buildStreamingResponse (line 160) | function buildStreamingResponse(): ReadableStream<Uint8Array> {
  function runSelfTest (line 282) | async function runSelfTest() {

FILE: experiments/tool-replacement-proxy-2026-04/poc/03-sdk-validation.ts
  constant BASE_URL (line 20) | const BASE_URL = "http://127.0.0.1:8788";
  function formatBlock (line 105) | function formatBlock(b: any): string {

FILE: experiments/tool-replacement-proxy-2026-04/poc/04-multi-turn-validation.ts
  constant BASE_URL (line 27) | const BASE_URL = "http://127.0.0.1:8788";

FILE: experiments/tool-replacement-proxy-2026-04/poc/05-tool-loop-proxy.ts
  constant EXECUTOR_PORT (line 47) | const EXECUTOR_PORT = 9001;
  constant ADVISOR_PORT (line 48) | const ADVISOR_PORT = 9002;
  constant PROXY_PORT (line 49) | const PROXY_PORT = 8789;
  constant MOCK_THIRD_PARTY_ADVICE (line 51) | const MOCK_THIRD_PARTY_ADVICE =
  function startMockExecutor (line 58) | function startMockExecutor() {
  function startMockAdvisor (line 133) | function startMockAdvisor() {
  constant EXECUTOR_URL (line 163) | const EXECUTOR_URL = `http://127.0.0.1:${EXECUTOR_PORT}`;
  constant ADVISOR_URL (line 164) | const ADVISOR_URL = `http://127.0.0.1:${ADVISOR_PORT}`;
  function extractAdvisorTool (line 170) | function extractAdvisorTool(tools: any[] | undefined): {
  function callThirdPartyAdvisor (line 200) | async function callThirdPartyAdvisor(
  function callExecutor (line 226) | async function callExecutor(requestBody: any): Promise<any> {
  function runToolLoop (line 241) | async function runToolLoop(
  function transformToAdvisorBlocks (line 311) | function transformToAdvisorBlocks(blocks: any[]): any[] {
  function processClientRequest (line 329) | async function processClientRequest(originalBody: any): Promise<any> {
  function startProxy (line 430) | function startProxy() {

FILE: experiments/tool-replacement-proxy-2026-04/poc/06-sdk-e2e-validation.ts
  constant EXECUTOR_PORT (line 55) | const EXECUTOR_PORT = 9101;
  constant ADVISOR_PORT (line 56) | const ADVISOR_PORT = 9102;
  constant PROXY_PORT (line 57) | const PROXY_PORT = 8889;
  constant MOCK_THIRD_PARTY_ADVICE (line 58) | const MOCK_THIRD_PARTY_ADVICE =
  method fetch (line 67) | async fetch(req) {
  method fetch (line 123) | async fetch(req) {
  method fetch (line 146) | async fetch(req) {

FILE: landingpage/components/BlockLogo.tsx
  constant LETTERS (line 4) | const LETTERS: Record<string, number[][]> = {
  constant WORD (line 50) | const WORD = "CLAUD";

FILE: landingpage/components/Changelog.tsx
  type GitHubRelease (line 3) | interface GitHubRelease {
  constant CACHE_KEY (line 13) | const CACHE_KEY = "claudish-releases";
  constant CACHE_TTL (line 14) | const CACHE_TTL = 5 * 60 * 1000;
  function formatRelativeDate (line 16) | function formatRelativeDate(dateStr: string): string {
  constant SECTION_COLORS (line 30) | const SECTION_COLORS: Record<string, string> = {
  function renderInline (line 40) | function renderInline(text: string): React.ReactNode[] {
  function ReleaseBody (line 88) | function ReleaseBody({ body }: { body: string }) {
  function ReleaseSkeleton (line 177) | function ReleaseSkeleton() {

FILE: landingpage/components/FeatureSection.tsx
  constant COMPARISON_ROWS (line 10) | const COMPARISON_ROWS = [

FILE: landingpage/components/SubscriptionSection.tsx
  constant SUBSCRIPTIONS (line 21) | const SUBSCRIPTIONS = [

FILE: landingpage/components/TerminalWindow.tsx
  type TerminalWindowProps (line 3) | interface TerminalWindowProps {

FILE: landingpage/components/TypingAnimation.tsx
  type TypingAnimationProps (line 3) | interface TypingAnimationProps {

FILE: landingpage/constants.ts
  constant HERO_SEQUENCE (line 3) | const HERO_SEQUENCE: TerminalLine[] = [
  constant HIGHLIGHT_FEATURES (line 104) | const HIGHLIGHT_FEATURES: Feature[] = [
  constant STANDARD_FEATURES (line 131) | const STANDARD_FEATURES: Feature[] = [
  constant MARKETING_FEATURES (line 171) | const MARKETING_FEATURES = [...HIGHLIGHT_FEATURES, ...STANDARD_FEATURES];
  constant MODEL_CARDS (line 173) | const MODEL_CARDS: ModelCard[] = [

FILE: landingpage/types.ts
  type TerminalLine (line 1) | interface TerminalLine {
  type Feature (line 21) | interface Feature {
  type ModelCard (line 31) | interface ModelCard {

FILE: packages/cli/bin/claudish.cjs
  function findBun (line 10) | function findBun() {

FILE: packages/cli/scripts/smoke-test.ts
  type CLIFlags (line 36) | interface CLIFlags {
  function parseCLIFlags (line 44) | function parseCLIFlags(): CLIFlags {
  function printDryRun (line 80) | function printDryRun(configs: SmokeProviderConfig[]): void {
  function buildFailedProviderResult (line 103) | function buildFailedProviderResult(config: SmokeProviderConfig, reason: ...
  function runProviderProbes (line 124) | async function runProviderProbes(
  function buildRunId (line 162) | function buildRunId(): string {
  function buildRunResult (line 171) | function buildRunResult(
  function main (line 190) | async function main(): Promise<void> {

FILE: packages/cli/scripts/smoke.test.ts
  function makeProbe (line 16) | function makeProbe(
  function makeProviderResult (line 23) | function makeProviderResult(probeStatuses: ProbeResult["status"][]): Pro...
  function makeConfig (line 110) | function makeConfig(authScheme: SmokeProviderConfig["authScheme"]): Smok...
  function makeVisionConfig (line 355) | function makeVisionConfig(): SmokeProviderConfig {

FILE: packages/cli/scripts/smoke/probes.ts
  constant TEST_IMAGE_BASE64 (line 19) | const TEST_IMAGE_BASE64 =
  constant TEST_IMAGE_MEDIA_TYPE (line 21) | const TEST_IMAGE_MEDIA_TYPE = "image/png";
  constant VISION_ERROR_PHRASES (line 24) | const VISION_ERROR_PHRASES = [
  function isReasoningModel (line 37) | function isReasoningModel(modelId: string): boolean {
  function buildHeaders (line 44) | function buildHeaders(config: SmokeProviderConfig): Record<string, strin...
  function callProvider (line 71) | async function callProvider(
  function runProbe (line 97) | async function runProbe(

FILE: packages/cli/scripts/smoke/providers.ts
  constant SKIP_PROVIDERS (line 14) | const SKIP_PROVIDERS = new Set([
  constant REPRESENTATIVE_MODELS (line 19) | const REPRESENTATIVE_MODELS: Record<string, string> = {
  constant SMOKE_MODEL_CAPABILITIES (line 39) | const SMOKE_MODEL_CAPABILITIES: Record<
  constant ANTHROPIC_COMPAT_PROVIDERS (line 56) | const ANTHROPIC_COMPAT_PROVIDERS = new Set([
  function getWireFormat (line 64) | function getWireFormat(providerName: string): WireFormat {
  function getAuthScheme (line 69) | function getAuthScheme(provider: RemoteProvider): SmokeProviderConfig["a...
  function getVertexToken (line 85) | function getVertexToken(): string | undefined {
  function getApiKey (line 108) | function getApiKey(provider: RemoteProvider): string | undefined {
  function getApiPath (line 126) | function getApiPath(provider: RemoteProvider): string {
  function getBaseUrl (line 142) | function getBaseUrl(provider: RemoteProvider): string {
  function discoverProviders (line 156) | function discoverProviders(filterName?: string): SmokeProviderConfig[] {

FILE: packages/cli/scripts/smoke/reporter.ts
  constant GREEN (line 10) | const GREEN = "\x1b[32m";
  constant RED (line 11) | const RED = "\x1b[31m";
  constant YELLOW (line 12) | const YELLOW = "\x1b[33m";
  constant RESET (line 13) | const RESET = "\x1b[0m";
  constant BOLD (line 14) | const BOLD = "\x1b[1m";
  constant DIM (line 15) | const DIM = "\x1b[2m";
  function color (line 19) | function color(text: string, code: string): string {
  function renderStatus (line 24) | function renderStatus(result: ProbeResult | undefined): string {
  function padEnd (line 38) | function padEnd(str: string, len: number): string {
  function printTable (line 52) | function printTable(results: ProviderResult[], quiet: boolean): void {
  function printSummary (line 103) | function printSummary(run: SmokeRunResult): void {
  function writeJsonResults (line 119) | function writeJsonResults(run: SmokeRunResult, resultsDir?: string): void {
  function buildSummary (line 134) | function buildSummary(results: ProviderResult[]): SmokeRunResult["summar...

FILE: packages/cli/scripts/smoke/types.ts
  type WireFormat (line 6) | type WireFormat = "anthropic-compat" | "openai-compat" | "ollama";
  type Capability (line 9) | type Capability = "tool_calling" | "reasoning" | "vision";
  type ProbeStatus (line 12) | type ProbeStatus = "pass" | "fail" | "skip";
  type ProbeResult (line 14) | interface ProbeResult {
  type ProviderResult (line 24) | interface ProviderResult {
  type SmokeRunResult (line 32) | interface SmokeRunResult {
  type SmokeProviderConfig (line 46) | interface SmokeProviderConfig {
  type ProbeFn (line 63) | type ProbeFn = (config: SmokeProviderConfig, signal: AbortSignal) => Pro...
  type AnthropicResponse (line 66) | interface AnthropicResponse {
  type OllamaResponse (line 77) | interface OllamaResponse {
  type OpenAIResponse (line 95) | interface OpenAIResponse {

FILE: packages/cli/src/adapters/anthropic-api-format.ts
  class AnthropicAPIFormat (line 13) | class AnthropicAPIFormat extends BaseAPIFormat {
    method constructor (line 16) | constructor(modelId: string, providerName: string) {
    method processTextContent (line 21) | processTextContent(textContent: string, _accumulatedText: string): Ada...
    method shouldHandle (line 29) | shouldHandle(modelId: string): boolean {
    method getName (line 33) | getName(): string {
    method convertMessages (line 42) | override convertMessages(claudeRequest: any, _filterFn?: any): any[] {
    method stripUnsupportedContentTypes (line 47) | private stripUnsupportedContentTypes(message: any): any {
    method convertTools (line 71) | override convertTools(claudeRequest: any, _summarize?: boolean): any[] {
    method buildPayload (line 80) | override buildPayload(claudeRequest: any, messages: any[], tools: any[...
    method getStreamFormat (line 113) | override getStreamFormat(): StreamFormat {
    method getContextWindow (line 117) | override getContextWindow(): number {
    method supportsVision (line 129) | override supportsVision(): boolean {

FILE: packages/cli/src/adapters/api-format.ts
  type APIFormat (line 16) | interface APIFormat {

FILE: packages/cli/src/adapters/base-api-format.ts
  function matchesModelFamily (line 28) | function matchesModelFamily(modelId: string, family: string): boolean {
  type ToolCall (line 36) | interface ToolCall {
  type AdapterResult (line 42) | interface AdapterResult {
  method constructor (line 60) | constructor(modelId: string) {
  method getToolNameLimit (line 86) | getToolNameLimit(): number | null {
  method getToolNameMap (line 94) | getToolNameMap(): Map<string, string> {
  method restoreToolName (line 101) | restoreToolName(name: string): string {
  method prepareRequest (line 112) | prepareRequest(request: any, originalRequest: any): any {
  method reset (line 119) | reset(): void {
  method convertMessages (line 132) | convertMessages(claudeRequest: any, filterIdentityFn?: (s: string) => st...
  method convertTools (line 140) | convertTools(claudeRequest: any, summarize = false): any[] {
  method buildPayload (line 149) | buildPayload(claudeRequest: any, messages: any[], tools: any[]): any {
  method getStreamFormat (line 172) | getStreamFormat(): StreamFormat {
  method getContextWindow (line 180) | getContextWindow(): number {
  method getPricing (line 188) | getPricing(providerName: string): ModelPricing {
  method supportsVision (line 195) | supportsVision(): boolean {
  method shouldFilterThinking (line 203) | shouldFilterThinking(): boolean {
  method truncateToolNames (line 213) | protected truncateToolNames(request: any): void {
  method truncateToolNamesInMessages (line 236) | protected truncateToolNamesInMessages(messages: any[]): void {
  class DefaultAPIFormat (line 260) | class DefaultAPIFormat extends BaseAPIFormat {
    method processTextContent (line 261) | processTextContent(textContent: string, accumulatedText: string): Adap...
    method shouldHandle (line 269) | shouldHandle(modelId: string): boolean {
    method getName (line 273) | getName(): string {
  type BaseModelAdapter (line 284) | type BaseModelAdapter = BaseAPIFormat;
  type DefaultAdapter (line 288) | type DefaultAdapter = DefaultAPIFormat;

FILE: packages/cli/src/adapters/codex-api-format.ts
  function normalizeCodexModel (line 28) | function normalizeCodexModel(modelId: string | undefined): string {
  class CodexAPIFormat (line 41) | class CodexAPIFormat extends BaseAPIFormat {
    method constructor (line 42) | constructor(modelId: string) {
    method processTextContent (line 46) | processTextContent(textContent: string, _accumulatedText: string): Ada...
    method shouldHandle (line 54) | shouldHandle(modelId: string): boolean {
    method getName (line 58) | getName(): string {
    method getStreamFormat (line 62) | override getStreamFormat(): StreamFormat {
    method getContextWindow (line 66) | override getContextWindow(): number {
    method buildPayload (line 70) | override buildPayload(claudeRequest: any, messages: any[], tools: any[...
    method convertMessagesToResponsesAPI (line 127) | private convertMessagesToResponsesAPI(messages: any[]): any[] {

FILE: packages/cli/src/adapters/deepseek-model-dialect.ts
  class DeepSeekModelDialect (line 11) | class DeepSeekModelDialect extends BaseAPIFormat {
    method processTextContent (line 12) | processTextContent(textContent: string, accumulatedText: string): Adap...
    method prepareRequest (line 23) | override prepareRequest(request: any, originalRequest: any): any {
    method shouldHandle (line 38) | shouldHandle(modelId: string): boolean {
    method getName (line 42) | getName(): string {

FILE: packages/cli/src/adapters/dialect-manager.ts
  class DialectManager (line 22) | class DialectManager {
    method constructor (line 26) | constructor(modelId: string) {
    method getAdapter (line 45) | getAdapter(): BaseAPIFormat {
    method needsTransformation (line 57) | needsTransformation(): boolean {

FILE: packages/cli/src/adapters/gemini-api-format.ts
  constant REASONING_PATTERNS (line 24) | const REASONING_PATTERNS = [
  constant REASONING_CONTINUATION_PATTERNS (line 46) | const REASONING_CONTINUATION_PATTERNS = [
  class GeminiAPIFormat (line 64) | class GeminiAPIFormat extends BaseAPIFormat {
    method constructor (line 76) | constructor(modelId: string) {
    method convertMessages (line 82) | override convertMessages(claudeRequest: any, _filterIdentityFn?: (s: s...
    method convertUserParts (line 100) | private convertUserParts(msg: any): any[] {
    method convertAssistantParts (line 173) | private convertAssistantParts(msg: any): any[] {
    method convertTools (line 223) | override convertTools(claudeRequest: any, _summarize = false): any[] {
    method buildPayload (line 230) | override buildPayload(claudeRequest: any, messages: any[], tools: any[...
    method registerToolCall (line 287) | registerToolCall(toolId: string, name: string, thoughtSignature?: stri...
    method processTextContent (line 296) | processTextContent(textContent: string, _accumulatedText: string): Ada...
    method isReasoningLine (line 344) | private isReasoningLine(line: string): boolean {
    method isReasoningContinuation (line 348) | private isReasoningContinuation(line: string): boolean {
    method getStreamFormat (line 354) | override getStreamFormat(): StreamFormat {
    method reset (line 363) | override reset(): void {
    method getContextWindow (line 369) | override getContextWindow(): number {
    method shouldHandle (line 373) | shouldHandle(modelId: string): boolean {
    method getName (line 377) | getName(): string {
    method extractThoughtSignaturesFromReasoningDetails (line 386) | extractThoughtSignaturesFromReasoningDetails(
    method getThoughtSignature (line 406) | getThoughtSignature(toolCallId: string): string | undefined {
    method hasThoughtSignature (line 411) | hasThoughtSignature(toolCallId: string): boolean {
    method getAllThoughtSignatures (line 416) | getAllThoughtSignatures(): Map<string, string> {

FILE: packages/cli/src/adapters/glm-model-dialect.ts
  class GLMModelDialect (line 14) | class GLMModelDialect extends BaseAPIFormat {
    method processTextContent (line 15) | processTextContent(textContent: string, accumulatedText: string): Adap...
    method prepareRequest (line 23) | override prepareRequest(request: any, originalRequest: any): any {
    method shouldHandle (line 33) | shouldHandle(modelId: string): boolean {
    method getName (line 41) | getName(): string {
    method getContextWindow (line 45) | override getContextWindow(): number {
    method supportsVision (line 49) | override supportsVision(): boolean {

FILE: packages/cli/src/adapters/grok-model-dialect.ts
  class GrokModelDialect (line 17) | class GrokModelDialect extends BaseAPIFormat {
    method processTextContent (line 20) | processTextContent(textContent: string, accumulatedText: string): Adap...
    method prepareRequest (line 83) | override prepareRequest(request: any, originalRequest: any): any {
    method parseXmlParameters (line 113) | private parseXmlParameters(xmlContent: string): Record<string, any> {
    method shouldHandle (line 134) | shouldHandle(modelId: string): boolean {
    method getName (line 138) | getName(): string {
    method getContextWindow (line 142) | override getContextWindow(): number {
    method reset (line 149) | reset(): void {

FILE: packages/cli/src/adapters/litellm-api-format.ts
  constant INLINE_IMAGE_MODEL_PATTERNS (line 20) | const INLINE_IMAGE_MODEL_PATTERNS = ["minimax"];
  class LiteLLMAPIFormat (line 22) | class LiteLLMAPIFormat extends DefaultAPIFormat {
    method constructor (line 27) | constructor(modelId: string, baseUrl: string) {
    method getName (line 36) | getName(): string {
    method shouldHandle (line 40) | shouldHandle(modelId: string): boolean {
    method supportsVision (line 44) | supportsVision(): boolean {
    method convertMessages (line 52) | convertMessages(claudeRequest: any, filterIdentityFn?: (s: string) => ...
    method buildPayload (line 103) | buildPayload(claudeRequest: any, messages: any[], tools: any[]): any {
    method getContextWindow (line 130) | getContextWindow(): number {
    method checkVisionSupport (line 137) | private checkVisionSupport(): boolean {

FILE: packages/cli/src/adapters/local-adapter.ts
  type SamplingParams (line 18) | interface SamplingParams {
  class LocalModelAdapter (line 26) | class LocalModelAdapter extends BaseAPIFormat {
    method constructor (line 30) | constructor(modelId: string, providerName: string) {
    method processTextContent (line 40) | processTextContent(textContent: string, accumulatedText: string): Adap...
    method shouldHandle (line 44) | shouldHandle(modelId: string): boolean {
    method getName (line 48) | getName(): string {
    method reset (line 52) | override reset(): void {
    method supportsVision (line 57) | supportsVision(): boolean {
    method convertMessages (line 63) | override convertMessages(claudeRequest: any, filterIdentityFn?: (s: st...
    method convertTools (line 91) | override convertTools(claudeRequest: any, summarize = false): any[] {
    method buildPayload (line 98) | override buildPayload(claudeRequest: any, messages: any[], tools: any[...
    method prepareRequest (line 136) | override prepareRequest(request: any, originalRequest: any): any {
    method getToolNameMap (line 153) | override getToolNameMap(): Map<string, string> {
    method getContextWindow (line 161) | override getContextWindow(): number {
    method getSamplingParams (line 167) | private getSamplingParams(): SamplingParams {
    method buildSystemGuidance (line 189) | private buildSystemGuidance(toolCount: number): string {

FILE: packages/cli/src/adapters/minimax-model-dialect.ts
  class MiniMaxModelDialect (line 15) | class MiniMaxModelDialect extends BaseAPIFormat {
    method processTextContent (line 16) | processTextContent(textContent: string, accumulatedText: string): Adap...
    method prepareRequest (line 31) | override prepareRequest(request: any, originalRequest: any): any {
    method getContextWindow (line 56) | override getContextWindow(): number {
    method supportsVision (line 65) | override supportsVision(): boolean {
    method shouldFilterThinking (line 73) | override shouldFilterThinking(): boolean {
    method shouldHandle (line 77) | shouldHandle(modelId: string): boolean {
    method getName (line 81) | getName(): string {

FILE: packages/cli/src/adapters/model-catalog.ts
  type ModelEntry (line 9) | interface ModelEntry {
  constant MODEL_CATALOG (line 28) | const MODEL_CATALOG: ModelEntry[] = [
  function lookupModel (line 114) | function lookupModel(modelId: string): ModelEntry | undefined {
  constant DEFAULT_CONTEXT_WINDOW (line 130) | const DEFAULT_CONTEXT_WINDOW = 0;
  constant DEFAULT_SUPPORTS_VISION (line 133) | const DEFAULT_SUPPORTS_VISION = true;

FILE: packages/cli/src/adapters/model-dialect.ts
  type ModelDialect (line 10) | interface ModelDialect {

FILE: packages/cli/src/adapters/ollama-api-format.ts
  class OllamaAPIFormat (line 14) | class OllamaAPIFormat extends BaseAPIFormat {
    method constructor (line 15) | constructor(modelId: string) {
    method processTextContent (line 19) | processTextContent(textContent: string, _accumulatedText: string): Ada...
    method shouldHandle (line 27) | shouldHandle(_modelId: string): boolean {
    method getName (line 31) | getName(): string {
    method convertMessages (line 39) | override convertMessages(claudeRequest: any, _filterFn?: any): any[] {
    method convertTools (line 66) | override convertTools(_claudeRequest: any, _summarize?: boolean): any[] {
    method buildPayload (line 73) | override buildPayload(_claudeRequest: any, messages: any[], _tools: an...
    method getStreamFormat (line 81) | override getStreamFormat(): StreamFormat {
    method getContextWindow (line 85) | override getContextWindow(): number {
    method supportsVision (line 89) | override supportsVision(): boolean {
    method processUserMessage (line 95) | private processUserMessage(msg: any): any {
    method processAssistantMessage (line 113) | private processAssistantMessage(msg: any): any {

FILE: packages/cli/src/adapters/openai-api-format.ts
  class OpenAIAPIFormat (line 19) | class OpenAIAPIFormat extends BaseAPIFormat {
    method constructor (line 20) | constructor(modelId: string) {
    method processTextContent (line 24) | processTextContent(textContent: string, accumulatedText: string): Adap...
    method getStreamFormat (line 32) | override getStreamFormat(): StreamFormat {
    method prepareRequest (line 39) | override prepareRequest(request: any, originalRequest: any): any {
    method shouldHandle (line 62) | shouldHandle(modelId: string): boolean {
    method getName (line 66) | getName(): string {
    method getContextWindow (line 72) | override getContextWindow(): number {
    method buildPayload (line 76) | override buildPayload(claudeRequest: any, messages: any[], tools: any[...
    method isReasoningModel (line 82) | private isReasoningModel(): boolean {
    method usesMaxCompletionTokens (line 87) | private usesMaxCompletionTokens(): boolean {
    method buildChatCompletionsPayload (line 97) | private buildChatCompletionsPayload(claudeRequest: any, messages: any[...

FILE: packages/cli/src/adapters/openrouter-api-format.ts
  class OpenRouterAPIFormat (line 18) | class OpenRouterAPIFormat extends BaseAPIFormat {
    method constructor (line 21) | constructor(modelId: string) {
    method modelSupportsReasoning (line 30) | private modelSupportsReasoning(): boolean {
    method processTextContent (line 43) | processTextContent(textContent: string, accumulatedText: string): Adap...
    method shouldHandle (line 47) | shouldHandle(modelId: string): boolean {
    method getName (line 51) | getName(): string {
    method reset (line 55) | override reset(): void {
    method convertMessages (line 62) | override convertMessages(claudeRequest: any, filterIdentityFn?: (s: st...
    method appendToSystemPrompt (line 87) | private appendToSystemPrompt(messages: any[], text: string): void {
    method convertTools (line 97) | override convertTools(claudeRequest: any, summarize = false): any[] {
    method buildPayload (line 113) | override buildPayload(claudeRequest: any, messages: any[], tools: any[...
    method prepareRequest (line 152) | override prepareRequest(request: any, originalRequest: any): any {
    method getToolNameMap (line 156) | override getToolNameMap(): Map<string, string> {
    method extractThoughtSignaturesFromReasoningDetails (line 166) | extractThoughtSignaturesFromReasoningDetails(reasoningDetails: any[]):...

FILE: packages/cli/src/adapters/qwen-model-dialect.ts
  constant QWEN_SPECIAL_TOKENS (line 13) | const QWEN_SPECIAL_TOKENS = [
  class QwenModelDialect (line 21) | class QwenModelDialect extends BaseAPIFormat {
    method processTextContent (line 22) | processTextContent(textContent: string, accumulatedText: string): Adap...
    method prepareRequest (line 55) | override prepareRequest(request: any, originalRequest: any): any {
    method shouldHandle (line 74) | shouldHandle(modelId: string): boolean {
    method getName (line 78) | getName(): string {

FILE: packages/cli/src/adapters/tool-name-utils.ts
  function hashToolName (line 14) | function hashToolName(name: string): string {
  function truncateToolName (line 35) | function truncateToolName(name: string, maxLength: number): string {

FILE: packages/cli/src/adapters/xiaomi-model-dialect.ts
  class XiaomiModelDialect (line 14) | class XiaomiModelDialect extends BaseAPIFormat {
    method processTextContent (line 15) | processTextContent(textContent: string, accumulatedText: string): Adap...
    method getToolNameLimit (line 23) | override getToolNameLimit(): number | null {
    method prepareRequest (line 27) | override prepareRequest(request: any, originalRequest: any): any {
    method shouldHandle (line 43) | shouldHandle(modelId: string): boolean {
    method getName (line 47) | getName(): string {

FILE: packages/cli/src/auth/auth-commands.ts
  type OAuthInstance (line 17) | interface OAuthInstance {
  type OAuthProvider (line 22) | interface OAuthProvider {
  constant AUTH_PROVIDERS (line 30) | const AUTH_PROVIDERS: OAuthProvider[] = [
  function getAuthStatus (line 54) | function getAuthStatus(provider: OAuthProvider): string {
  function selectProvider (line 59) | async function selectProvider(action: string): Promise<OAuthProvider> {
  function findProvider (line 71) | function findProvider(name: string): OAuthProvider | null {
  function loginCommand (line 83) | async function loginCommand(providerArg?: string): Promise<void> {
  function logoutCommand (line 107) | async function logoutCommand(providerArg?: string): Promise<void> {

FILE: packages/cli/src/auth/codex-oauth.ts
  type CodexCredentials (line 29) | interface CodexCredentials {
  type TokenResponse (line 39) | interface TokenResponse {
  constant OAUTH_CONFIG (line 50) | const OAUTH_CONFIG = {
  class CodexOAuth (line 65) | class CodexOAuth {
    method getInstance (line 75) | static getInstance(): CodexOAuth {
    method constructor (line 85) | private constructor() {
    method hasCredentials (line 94) | hasCredentials(): boolean {
    method getCredentialsPath (line 101) | private getCredentialsPath(): string {
    method login (line 110) | async login(): Promise<void> {
    method logout (line 155) | async logout(): Promise<void> {
    method getAccessToken (line 169) | async getAccessToken(): Promise<string> {
    method getAccountId (line 199) | getAccountId(): string | undefined {
    method refreshToken (line 206) | async refreshToken(): Promise<void> {
    method isTokenValid (line 219) | private isTokenValid(): boolean {
    method doRefreshToken (line 228) | private async doRefreshToken(): Promise<string> {
    method loadCredentials (line 289) | private loadCredentials(): CodexCredentials | null {
    method saveCredentials (line 317) | private saveCredentials(credentials: CodexCredentials): void {
    method generateCodeVerifier (line 342) | private generateCodeVerifier(): string {
    method generateCodeChallenge (line 349) | private async generateCodeChallenge(verifier: string): Promise<string> {
    method extractAccountId (line 359) | private extractAccountId(idToken: string): string | undefined {
    method buildAuthUrl (line 386) | private buildAuthUrl(codeChallenge: string, state: string, redirectUri...
    method startCallbackServer (line 409) | private async startCallbackServer(
    method exchangeCodeForTokens (line 528) | private async exchangeCodeForTokens(
    method openBrowser (line 571) | private async openBrowser(url: string): Promise<void> {
  function getCodexOAuth (line 597) | function getCodexOAuth(): CodexOAuth {
  function getValidCodexAccessToken (line 605) | async function getValidCodexAccessToken(): Promise<string> {

FILE: packages/cli/src/auth/gemini-oauth.ts
  type GeminiCredentials (line 28) | interface GeminiCredentials {
  type TokenResponse (line 37) | interface TokenResponse {
  constant OAUTH_CONFIG (line 70) | const OAUTH_CONFIG = {
  class GeminiOAuth (line 86) | class GeminiOAuth {
    method getInstance (line 96) | static getInstance(): GeminiOAuth {
    method constructor (line 106) | private constructor() {
    method hasCredentials (line 115) | hasCredentials(): boolean {
    method getCredentialsPath (line 122) | private getCredentialsPath(): string {
    method login (line 131) | async login(): Promise<void> {
    method logout (line 169) | async logout(): Promise<void> {
    method getAccessToken (line 183) | async getAccessToken(): Promise<string> {
    method refreshToken (line 216) | async refreshToken(): Promise<void> {
    method isTokenValid (line 229) | private isTokenValid(): boolean {
    method doRefreshToken (line 237) | private async doRefreshToken(): Promise<string> {
    method loadCredentials (line 293) | private loadCredentials(): GeminiCredentials | null {
    method saveCredentials (line 321) | private saveCredentials(credentials: GeminiCredentials): void {
    method generateCodeVerifier (line 346) | private generateCodeVerifier(): string {
    method generateCodeChallenge (line 353) | private async generateCodeChallenge(verifier: string): Promise<string> {
    method buildAuthUrl (line 361) | private buildAuthUrl(codeChallenge: string, state: string, redirectUri...
    method startCallbackServer (line 381) | private async startCallbackServer(
    method exchangeCodeForTokens (line 499) | private async exchangeCodeForTokens(
    method openBrowser (line 542) | private async openBrowser(url: string): Promise<void> {
  function getGeminiOAuth (line 568) | function getGeminiOAuth(): GeminiOAuth {
  constant CODE_ASSIST_API_BASE (line 576) | const CODE_ASSIST_API_BASE = "https://cloudcode-pa.googleapis.com/v1inte...
  type ClientMetadata (line 578) | interface ClientMetadata {
  type AllowedTier (line 585) | interface AllowedTier {
  type LoadCodeAssistResponse (line 590) | interface LoadCodeAssistResponse {
  type LROResponse (line 597) | interface LROResponse {
  function getValidAccessToken (line 609) | async function getValidAccessToken(): Promise<string> {
  constant TIER_SHORT_NAMES (line 620) | const TIER_SHORT_NAMES: Record<string, string> = {
  function getGeminiTierDisplayName (line 631) | function getGeminiTierDisplayName(): string {
  function getGeminiTierFullName (line 639) | function getGeminiTierFullName(): string {
  function setupGeminiUser (line 649) | async function setupGeminiUser(
  function callLoadCodeAssist (line 728) | async function callLoadCodeAssist(
  function callOnboardUser (line 755) | async function callOnboardUser(
  type QuotaBucket (line 788) | interface QuotaBucket {
  function retrieveUserQuota (line 801) | async function retrieveUserQuota(

FILE: packages/cli/src/auth/kimi-oauth.ts
  type KimiCredentials (line 32) | interface KimiCredentials {
  type DeviceAuthorization (line 43) | interface DeviceAuthorization {
  type TokenResponse (line 55) | interface TokenResponse {
  constant OAUTH_CONFIG (line 68) | const OAUTH_CONFIG = {
  class KimiOAuth (line 78) | class KimiOAuth {
    method getInstance (line 88) | static getInstance(): KimiOAuth {
    method constructor (line 99) | private constructor() {
    method hasCredentials (line 112) | hasCredentials(): boolean {
    method getCredentialsPath (line 119) | private getCredentialsPath(): string {
    method getDeviceIdPath (line 127) | private getDeviceIdPath(): string {
    method loadOrCreateDeviceId (line 136) | private loadOrCreateDeviceId(): string {
    method getVersion (line 182) | private getVersion(): string {
    method getPlatformHeaders (line 190) | getPlatformHeaders(): Record<string, string> {
    method login (line 204) | async login(): Promise<void> {
    method requestDeviceAuthorization (line 245) | private async requestDeviceAuthorization(): Promise<DeviceAuthorizatio...
    method pollForToken (line 291) | private async pollForToken(
    method pollForTokenWithRetry (line 346) | private async pollForTokenWithRetry(deviceCode: string, retryCount = 0...
    method openBrowser (line 390) | private async openBrowser(url: string): Promise<void> {
    method logout (line 411) | async logout(): Promise<void> {
    method getAccessToken (line 426) | async getAccessToken(): Promise<string> {
    method isTokenValid (line 456) | private isTokenValid(): boolean {
    method doRefreshToken (line 465) | private async doRefreshToken(): Promise<string> {
    method loadCredentials (line 546) | private loadCredentials(): KimiCredentials | null {
    method saveCredentials (line 580) | private saveCredentials(credentials: KimiCredentials): void {
  function getKimiOAuth (line 606) | function getKimiOAuth(): KimiOAuth {
  function getValidKimiAccessToken (line 614) | async function getValidKimiAccessToken(): Promise<string> {
  function hasKimiOAuthCredentials (line 624) | function hasKimiOAuthCredentials(): boolean {

FILE: packages/cli/src/auth/oauth-manager.ts
  type BaseCredentials (line 24) | interface BaseCredentials {
  method ensureClaudishDir (line 59) | protected static ensureClaudishDir(): string {
  method getCredentialsPath (line 67) | protected getCredentialsPath(): string {
  method loadCredentials (line 73) | protected loadCredentials(): T | null {
  method saveCredentials (line 91) | protected saveCredentials(credentials: T): void {
  method deleteCredentials (line 103) | protected deleteCredentials(): void {
  method hasCredentials (line 113) | hasCredentials(): boolean {
  method getAccessToken (line 117) | async getAccessToken(): Promise<string> {
  method refreshToken (line 140) | async refreshToken(): Promise<void> {
  method isTokenValid (line 149) | protected isTokenValid(): boolean {
  method generateCodeVerifier (line 156) | protected generateCodeVerifier(): string {
  method generateCodeChallenge (line 160) | protected generateCodeChallenge(verifier: string): string {
  method openBrowser (line 166) | protected async openBrowser(url: string, message?: string): Promise<void> {
  method logout (line 191) | async logout(): Promise<void> {

FILE: packages/cli/src/auth/oauth-registry.ts
  type OAuthProviderDescriptor (line 5) | interface OAuthProviderDescriptor {
  constant OAUTH_PROVIDERS (line 30) | const OAUTH_PROVIDERS: Record<string, OAuthProviderDescriptor> = {
  function hasValidOAuthCredentials (line 69) | function hasValidOAuthCredentials(descriptor: OAuthProviderDescriptor): ...
  function hasOAuthCredentials (line 97) | function hasOAuthCredentials(providerName: string): boolean {

FILE: packages/cli/src/auth/quota-command.ts
  constant RED (line 25) | const RED = "\x1b[31m";
  constant GRN (line 26) | const GRN = "\x1b[32m";
  constant YEL (line 27) | const YEL = "\x1b[33m";
  constant MAG (line 28) | const MAG = "\x1b[35m";
  constant CYN (line 29) | const CYN = "\x1b[36m";
  constant WHT (line 30) | const WHT = "\x1b[37m";
  constant GRY (line 31) | const GRY = "\x1b[90m";
  constant FALLBACK_CHAIN (line 34) | const FALLBACK_CHAIN = [
  type QuotaAdapter (line 46) | interface QuotaAdapter {
  constant QUOTA_ADAPTERS (line 53) | const QUOTA_ADAPTERS: QuotaAdapter[] = [
  function quotaCommand (line 72) | async function quotaCommand(provider?: string): Promise<void> {
  function geminiQuotaHandler (line 105) | async function geminiQuotaHandler(): Promise<void> {
  function codexQuotaHandler (line 208) | async function codexQuotaHandler(): Promise<void> {
  type QuotaBucket (line 336) | interface QuotaBucket {
  type VersionGroup (line 344) | interface VersionGroup {
  function groupByVersion (line 350) | function groupByVersion(buckets: QuotaBucket[]): VersionGroup[] {
  function extractVersion (line 377) | function extractVersion(modelId: string): string | undefined {
  function buildUsageBar (line 382) | function buildUsageBar(usedFraction: number, color: string, width = 24):...
  function formatRelativeReset (line 393) | function formatRelativeReset(resetTime: string): string {

FILE: packages/cli/src/auth/vertex-auth.ts
  type VertexAccessToken (line 22) | interface VertexAccessToken {
  type VertexConfig (line 27) | interface VertexConfig {
  class VertexAuthManager (line 35) | class VertexAuthManager {
    method getAccessToken (line 43) | async getAccessToken(): Promise<string> {
    method refreshToken (line 69) | async refreshToken(): Promise<void> {
    method isTokenValid (line 77) | private isTokenValid(): boolean {
    method doRefresh (line 85) | private async doRefresh(): Promise<string> {
    method tryADC (line 117) | private async tryADC(): Promise<VertexAccessToken | null> {
    method tryServiceAccount (line 151) | private async tryServiceAccount(): Promise<VertexAccessToken | null> {
  function getVertexConfig (line 190) | function getVertexConfig(): VertexConfig | null {
  function validateVertexOAuthConfig (line 206) | function validateVertexOAuthConfig(): string | null {
  function buildVertexOAuthEndpoint (line 237) | function buildVertexOAuthEndpoint(
  function getVertexAuthManager (line 279) | function getVertexAuthManager(): VertexAuthManager {

FILE: packages/cli/src/channel/e2e-channel.test.ts
  constant SERVER_ENTRY (line 27) | const SERVER_ENTRY = join(__dirname, "../index.ts");
  function runClaudeWithMcp (line 264) | async function runClaudeWithMcp(

FILE: packages/cli/src/channel/scrollback-buffer.ts
  constant ANSI_RE (line 7) | const ANSI_RE = /\x1b\[[0-9;]*[a-zA-Z]|\x1b\].*?\x07|\x1b[()][AB012]|\x1...
  class ScrollbackBuffer (line 9) | class ScrollbackBuffer {
    method constructor (line 16) | constructor(capacity = 2000) {
    method append (line 25) | append(data: string): void {
    method getLines (line 41) | getLines(n?: number): string[] {
    method totalLines (line 56) | get totalLines(): number {
    method size (line 61) | get size(): number {
    method clear (line 66) | clear(): void {

FILE: packages/cli/src/channel/session-manager.test.ts
  constant FAKE_CLAUDISH_TS (line 35) | const FAKE_CLAUDISH_TS = join(__dirname, "test-helpers", "fake-claudish....
  constant ORIGINAL_PATH (line 40) | const ORIGINAL_PATH = process.env.PATH ?? "";
  function waitUntil (line 68) | function waitUntil(predicate: () => boolean, timeoutMs = 5000, intervalM...
  function makeManager (line 81) | function makeManager(opts?: SessionManagerOptions): SessionManager {
  function quickSession (line 90) | function quickSession(

FILE: packages/cli/src/channel/session-manager.ts
  type SessionEntry (line 25) | interface SessionEntry {
  constant DEFAULT_MAX_SESSIONS (line 36) | const DEFAULT_MAX_SESSIONS = 20;
  constant DEFAULT_SCROLLBACK (line 37) | const DEFAULT_SCROLLBACK = 2000;
  constant DEFAULT_TIMEOUT (line 38) | const DEFAULT_TIMEOUT = 600;
  constant MAX_TIMEOUT (line 39) | const MAX_TIMEOUT = 3600;
  constant KILL_GRACE_MS (line 40) | const KILL_GRACE_MS = 5000;
  class SessionManager (line 42) | class SessionManager {
    method constructor (line 49) | constructor(options?: SessionManagerOptions) {
    method createSession (line 56) | createSession(opts: SessionCreateOptions): string {
    method sendInput (line 209) | sendInput(sessionId: string, text: string): boolean {
    method getOutput (line 226) | getOutput(
    method cancelSession (line 256) | cancelSession(sessionId: string): boolean {
    method listSessions (line 286) | listSessions(includeCompleted = false): SessionInfo[] {
    method getSession (line 305) | getSession(sessionId: string): SessionInfo {
    method shutdownAll (line 313) | async shutdownAll(): Promise<void> {
    method activeSessions (line 341) | private get activeSessions(): number {
    method getElapsed (line 350) | private getElapsed(startedAt: string): number {
    method setupSigint (line 354) | private setupSigint(): void {
    method cleanupSigint (line 363) | private cleanupSigint(): void {

FILE: packages/cli/src/channel/signal-watcher.ts
  constant QUIET_PERIOD_MS (line 9) | const QUIET_PERIOD_MS = 2000;
  constant TOOL_BATCH_MS (line 12) | const TOOL_BATCH_MS = 500;
  constant TOOL_PATTERNS (line 15) | const TOOL_PATTERNS = [
  constant QUESTION_PATTERNS (line 22) | const QUESTION_PATTERNS = [/\?\s*$/m, /\bchoose\b.*:/im, /\bselect\b.*:/...
  class SignalWatcher (line 24) | class SignalWatcher {
    method constructor (line 33) | constructor(
    method state (line 40) | get state(): SignalState {
    method feed (line 45) | feed(text: string): void {
    method processExited (line 80) | processExited(exitCode: number | null): void {
    method forceState (line 96) | forceState(state: SignalState, content?: string): void {
    method dispose (line 103) | dispose(): void {
    method transition (line 110) | private transition(newState: SignalState, extra?: Partial<SignalData>)...
    method detectToolUse (line 123) | private detectToolUse(text: string): string | null {
    method handleToolDetection (line 137) | private handleToolDetection(toolName: string): void {
    method clearQuietTimer (line 161) | private clearQuietTimer(): void {
    method clearTimers (line 168) | private clearTimers(): void {

FILE: packages/cli/src/channel/test-helpers/fake-claudish.ts
  function getFlag (line 18) | function getFlag(name: string): string | null {
  function hasFlag (line 24) | function hasFlag(name: string): boolean {
  function main (line 28) | async function main() {

FILE: packages/cli/src/channel/types.ts
  type SessionStatus (line 3) | type SessionStatus =
  type SignalState (line 13) | type SignalState =
  type SessionInfo (line 22) | interface SessionInfo {
  type SessionCreateOptions (line 35) | interface SessionCreateOptions {
  type ChannelEvent (line 43) | interface ChannelEvent {
  type SignalData (line 51) | interface SignalData {
  type SignalCallback (line 60) | type SignalCallback = (sessionId: string, data: SignalData) => void;
  type SessionManagerOptions (line 62) | interface SessionManagerOptions {

FILE: packages/cli/src/claude-runner.ts
  function hasNativeAnthropicMapping (line 16) | function hasNativeAnthropicMapping(config: ClaudishConfig): boolean {
  function isWindows (line 29) | function isWindows(): boolean {
  function createStatusLineScript (line 37) | function createStatusLineScript(tokenFilePath: string): string {
  function createTempSettingsFile (line 149) | function createTempSettingsFile(
  function mergeUserSettingsIfPresent (line 216) | function mergeUserSettingsIfPresent(
  function runClaudeWithProxy (line 261) | async function runClaudeWithProxy(
  function setupSignalHandlers (line 461) | function setupSignalHandlers(
  function findClaudeBinary (line 504) | async function findClaudeBinary(): Promise<string | null> {
  function checkClaudeInstalled (line 605) | async function checkClaudeInstalled(): Promise<boolean> {

FILE: packages/cli/src/cli-passthrough.test.ts
  constant MOCK_SETTINGS_PATH (line 27) | const MOCK_SETTINGS_PATH = "/mock/.claudish/settings-12345.json";
  function buildClaudeArgs (line 29) | function buildClaudeArgs(config: ClaudishConfig): string[] {
  constant MOCK_STATUS_LINE (line 69) | const MOCK_STATUS_LINE = { type: "command", command: "echo claudish", pa...
  function mergeUserSettingsLogic (line 71) | function mergeUserSettingsLogic(

FILE: packages/cli/src/cli.test.ts
  function computeModelId (line 183) | function computeModelId(config: ClaudishConfig): string | undefined {

FILE: packages/cli/src/cli.ts
  function getVersion (line 85) | function getVersion(): string {
  function clearAllModelCaches (line 97) | function clearAllModelCaches(): void {
  function parseAdvisorFlag (line 128) | function parseAdvisorFlag(value: string): {
  function parseArgs (line 163) | async function parseArgs(args: string[]): Promise<ClaudishConfig> {
  function fetchOllamaModels (line 631) | async function fetchOllamaModels(): Promise<any[]> {
  function formatModelDocPricing (line 696) | function formatModelDocPricing(pricing: ModelDoc["pricing"]): string {
  function formatModelDocContext (line 707) | function formatModelDocContext(ctx?: number): string {
  function formatModelDocCaps (line 714) | function formatModelDocCaps(caps?: ModelDoc["capabilities"]): string {
  function searchAndPrintModels (line 727) | async function searchAndPrintModels(query: string, jsonOutput: boolean):...
  function renderModelDocTable (line 799) | function renderModelDocTable(models: Array<ModelDoc & { rank?: number }>...
  function printLocalProvidersFooter (line 824) | async function printLocalProvidersFooter(): Promise<void> {
  function printTop100 (line 867) | async function printTop100(jsonOutput: boolean): Promise<void> {
  function printByProvider (line 914) | async function printByProvider(providerSlug: string, jsonOutput: boolean...
  function printRecommendedModels (line 953) | async function printRecommendedModels(jsonOutput: boolean, forceUpdate: ...
  function printVersion (line 1081) | function printVersion(): void {
  function probeModelRouting (line 1093) | async function probeModelRouting(
  function printHelp (line 1623) | function printHelp(): void {
  function printAIAgentGuide (line 1956) | function printAIAgentGuide(): void {
  function initializeClaudishSkill (line 1976) | async function initializeClaudishSkill(): Promise<void> {
  function printAvailableModels (line 2055) | function printAvailableModels(): void {

FILE: packages/cli/src/config-command.ts
  constant RESET (line 26) | const RESET = "\x1b[0m";
  constant BOLD (line 27) | const BOLD = "\x1b[1m";
  constant DIM (line 28) | const DIM = "\x1b[2m";
  constant GREEN (line 29) | const GREEN = "\x1b[32m";
  constant YELLOW (line 30) | const YELLOW = "\x1b[33m";
  constant CYAN (line 31) | const CYAN = "\x1b[36m";
  type ProviderDef (line 35) | interface ProviderDef {
  constant PROVIDERS (line 46) | const PROVIDERS: ProviderDef[] = [
  function maskKey (line 157) | function maskKey(key: string): string {
  function testProviderConnection (line 164) | async function testProviderConnection(provider: ProviderDef, key: string...
  function configureProviderKey (line 217) | async function configureProviderKey(provider: ProviderDef): Promise<void> {
  function configApiKeys (line 285) | async function configApiKeys(): Promise<void> {
  function configEndpoints (line 329) | async function configEndpoints(): Promise<void> {
  function configureProviderEndpoint (line 375) | async function configureProviderEndpoint(provider: ProviderDef): Promise...
  function configProfiles (line 438) | async function configProfiles(): Promise<void> {
  function configRouting (line 472) | async function configRouting(): Promise<void> {
  function configTelemetry (line 586) | async function configTelemetry(): Promise<void> {
  function showCurrentConfig (line 668) | function showCurrentConfig(): void {
  function configCommand (line 759) | async function configCommand(): Promise<void> {

FILE: packages/cli/src/config-schema.ts
  type BuiltinDefaultProvider (line 78) | type BuiltinDefaultProvider = z.infer<typeof BuiltinDefaultProviderSchema>;
  type CustomEndpointSimple (line 79) | type CustomEndpointSimple = z.infer<typeof CustomEndpointSimpleSchema>;
  type CustomEndpointComplex (line 80) | type CustomEndpointComplex = z.infer<typeof CustomEndpointComplexSchema>;
  type CustomEndpoint (line 81) | type CustomEndpoint = z.infer<typeof CustomEndpointSchema>;

FILE: packages/cli/src/config.ts
  constant DEFAULT_PORT_RANGE (line 3) | const DEFAULT_PORT_RANGE = { start: 3000, end: 9000 };
  constant ENV (line 6) | const ENV = {
  constant OPENROUTER_API_URL (line 39) | const OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions";
  constant OPENROUTER_HEADERS (line 40) | const OPENROUTER_HEADERS = {

FILE: packages/cli/src/default-provider.test.ts
  function makeConfig (line 9) | function makeConfig(overrides: Partial<ClaudishProfileConfig> = {}): Cla...

FILE: packages/cli/src/default-provider.ts
  type DefaultProviderSource (line 15) | type DefaultProviderSource =
  type ResolvedDefaultProvider (line 23) | interface ResolvedDefaultProvider {
  type ResolveOptions (line 32) | interface ResolveOptions {
  function resolveDefaultProvider (line 48) | function resolveDefaultProvider(opts: ResolveOptions): ResolvedDefaultPr...
  function buildLegacyHint (line 84) | function buildLegacyHint(resolved: ResolvedDefaultProvider): string | nu...

FILE: packages/cli/src/diag-output.ts
  type DiagOutput (line 11) | interface DiagOutput {
  function getClaudishDir (line 19) | function getClaudishDir(): string {
  function getDiagLogPath (line 33) | function getDiagLogPath(): string {
  class LogFileDiagOutput (line 41) | class LogFileDiagOutput implements DiagOutput {
    method constructor (line 45) | constructor() {
    method write (line 60) | write(msg: string): void {
    method cleanup (line 70) | cleanup(): void {
    method getLogPath (line 84) | getLogPath(): string {
  class NullDiagOutput (line 93) | class NullDiagOutput implements DiagOutput {
    method write (line 94) | write(_msg: string): void {
    method cleanup (line 98) | cleanup(): void {
  function createDiagOutput (line 111) | function createDiagOutput(options: {

FILE: packages/cli/src/format-translation.test.ts
  constant FIXTURES_DIR (line 22) | const FIXTURES_DIR = join(__dirname, "test-fixtures", "sse-responses");
  type ClaudeEvent (line 25) | interface ClaudeEvent {
  function fixtureToResponse (line 34) | function fixtureToResponse(fixturePath: string): Response {
  function parseClaudeSseStream (line 56) | async function parseClaudeSseStream(response: Response): Promise<ClaudeE...
  function extractText (line 98) | function extractText(events: ClaudeEvent[]): string {
  function extractToolNames (line 106) | function extractToolNames(events: ClaudeEvent[]): string[] {
  function extractStopReason (line 115) | function extractStopReason(events: ClaudeEvent[]): string | null {
  function createMockContext (line 121) | function createMockContext(): any {
  function getParser (line 141) | async function getParser() {
  function getDefaultAdapter (line 146) | async function getDefaultAdapter() {
  function getParser (line 221) | async function getParser() {
  function getConverter (line 267) | async function getConverter() {
  function getAdapter (line 414) | async function getAdapter() {
  function getSanitizer (line 790) | async function getSanitizer() {
  function getAdapter (line 981) | async function getAdapter() {
  function getParser (line 1170) | async function getParser() {
  function getParser (line 1340) | async function getParser() {
  function makeMiniMaxAdapter (line 1345) | async function makeMiniMaxAdapter() {
  function getParser (line 1533) | async function getParser() {

FILE: packages/cli/src/handlers/composed-handler.test.ts
  function makeFakeTransport (line 10) | function makeFakeTransport(): ProviderTransport {

FILE: packages/cli/src/handlers/composed-handler.ts
  type BaseModelAdapter (line 24) | type BaseModelAdapter = BaseAPIFormat;
  function extractAuthHeaders (line 46) | function extractAuthHeaders(c: Context): VisionProxyAuthHeaders {
  type ComposedHandlerOptions (line 53) | interface ComposedHandlerOptions {
  class ComposedHandler (line 70) | class ComposedHandler implements ModelHandler {
    method constructor (line 91) | constructor(
    method getAdapter (line 152) | private getAdapter(): BaseModelAdapter {
    method getModelContextWindow (line 157) | private getModelContextWindow(): number {
    method getModelSupportsVision (line 162) | private getModelSupportsVision(): boolean {
    method getActiveAdapterName (line 167) | private getActiveAdapterName(): string {
    method handle (line 173) | async handle(c: Context, payload: any): Promise<Response> {
    method handleStream (line 679) | private handleStream(
    method getTokenTracker (line 795) | getTokenTracker(): TokenTracker {
    method fetchQuotaForStatusLine (line 800) | private async fetchQuotaForStatusLine(): Promise<void> {
    method setFallbackMeta (line 820) | setFallbackMeta(chain: string[], attempts: number): void {
    method shutdown (line 824) | async shutdown(): Promise<void> {
  function getRecoveryHint (line 834) | function getRecoveryHint(status: number, errorText: string, providerName...

FILE: packages/cli/src/handlers/default-provider-e2e.test.ts
  constant PORT_BASE (line 30) | const PORT_BASE = 19200;
  function nextPort (line 32) | function nextPort(): number {
  function captureStderr (line 41) | function captureStderr(): void {
  function releaseStderr (line 78) | function releaseStderr(): string {
  constant REAL_CONFIG_PATH (line 92) | const REAL_CONFIG_PATH = join(process.env.HOME ?? tmpdir(), ".claudish",...
  function sandboxHome (line 96) | function sandboxHome(configJson?: Record<string, unknown>): string {
  function clearHomeSandbox (line 116) | function clearHomeSandbox(): void {
  function spinProxy (line 132) | async function spinProxy(opts: {
  function killProxy (line 149) | async function killProxy(): Promise<void> {
  function askProxy (line 177) | async function askProxy(
  constant HAS_OR (line 292) | const HAS_OR = !!process.env.OPENROUTER_API_KEY;
  constant HAS_LL (line 293) | const HAS_LL = !!(process.env.LITELLM_BASE_URL && process.env.LITELLM_AP...
  constant HAS_XAI (line 294) | const HAS_XAI = !!process.env.XAI_API_KEY;
  constant KNOWN_PROVIDERS (line 647) | const KNOWN_PROVIDERS = new Set([
  function fetchCatalog (line 675) | async function fetchCatalog(): Promise<any> {

FILE: packages/cli/src/handlers/fallback-handler.test.ts
  constant TEST_PORT (line 23) | const TEST_PORT = 18900 + Math.floor(Math.random() * 100);
  function ensureProxy (line 27) | async function ensureProxy(): Promise<number> {
  function sendMessage (line 53) | async function sendMessage(
  function hasAnyCredentials (line 144) | function hasAnyCredentials(): boolean {
  function mockHandler (line 391) | function mockHandler(status: number, body: string) {
  function runFallback (line 399) | async function runFallback(firstStatus: number, firstBody: string): Prom...

FILE: packages/cli/src/handlers/fallback-handler.ts
  type FallbackCandidate (line 17) | interface FallbackCandidate {
  class FallbackHandler (line 24) | class FallbackHandler implements ModelHandler {
    method constructor (line 29) | constructor(candidates: FallbackCandidate[]) {
    method handle (line 38) | async handle(c: Context, payload: any): Promise<Response> {
    method formatCombinedError (line 107) | private formatCombinedError(
    method shutdown (line 139) | async shutdown(): Promise<void> {
  function isRetryableError (line 154) | function isRetryableError(status: number, errorBody: string): boolean {
  function parseErrorMessage (line 211) | function parseErrorMessage(body: string): string {
  function truncate (line 223) | function truncate(s: string, max: number): string {

FILE: packages/cli/src/handlers/native-handler-advisor.ts
  constant ADVISOR_SERVER_TOOL_TYPE (line 30) | const ADVISOR_SERVER_TOOL_TYPE = "advisor_20260301";
  constant ADVISOR_BETA_FLAG (line 31) | const ADVISOR_BETA_FLAG = "advisor-tool-2026-03-01";
  type AdvisorSwapConfig (line 33) | interface AdvisorSwapConfig {
  function loadAdvisorSwapConfig (line 42) | function loadAdvisorSwapConfig(
  type AdvisorInfo (line 55) | interface AdvisorInfo {
  function swapAdvisorToolInBody (line 73) | function swapAdvisorToolInBody(
  function stripAdvisorBeta (line 125) | function stripAdvisorBeta(
  function logAdvisorEvent (line 147) | function logAdvisorEvent(
  function recordAdvisorEventsFromChunk (line 169) | function recordAdvisorEventsFromChunk(
  constant MAX_TRACKED (line 214) | const MAX_TRACKED = 256;
  function extractAdvisorToolUseIds (line 224) | function extractAdvisorToolUseIds(chunkText: string): void {
  function rememberAdvisorToolUseId (line 243) | function rememberAdvisorToolUseId(id: string): void {
  function _debug_getTrackedAdvisorIds (line 254) | function _debug_getTrackedAdvisorIds(): string[] {
  function _debug_resetTrackedAdvisorIds (line 259) | function _debug_resetTrackedAdvisorIds(): void {
  function rewriteAdvisorToolResults (line 272) | function rewriteAdvisorToolResults(
  function stubAdvisorAdvice (line 319) | function stubAdvisorAdvice(toolUseId: string): string {
  function findPendingAdvisorToolResults (line 340) | function findPendingAdvisorToolResults(
  function convertToOpenAIMessages (line 363) | function convertToOpenAIMessages(
  function extractBlocksAsText (line 375) | function extractBlocksAsText(content: any): string {
  constant ADVISOR_SYSTEM_PROMPT (line 399) | const ADVISOR_SYSTEM_PROMPT = `You are a strategic advisor to a coding a...
  constant COLLECTOR_SYSTEM_PROMPT (line 411) | const COLLECTOR_SYSTEM_PROMPT = `You are synthesizing advice from multip...
  function buildAdvisorRequest (line 419) | function buildAdvisorRequest(
  function callAdvisorModel (line 480) | async function callAdvisorModel(
  function isAnthropicModel (line 509) | function isAnthropicModel(parsed: ReturnType<typeof parseModelSpec>): bo...
  function callAnthropicCollector (line 518) | async function callAnthropicCollector(
  function callCollectorModel (line 551) | async function callCollectorModel(
  function fetchMultiModelAdvice (line 595) | async function fetchMultiModelAdvice(

FILE: packages/cli/src/handlers/native-handler.ts
  class NativeHandler (line 17) | class NativeHandler implements ModelHandler {
    method constructor (line 23) | constructor(apiKey?: string, advisorModels?: string[], advisorCollecto...
    method handle (line 31) | async handle(c: Context, payload: any): Promise<Response> {
    method shutdown (line 265) | async shutdown(): Promise<void> {
  function trimForLog (line 276) | function trimForLog(payload: any): any {

FILE: packages/cli/src/handlers/shared/anthropic-error.ts
  type AnthropicErrorType (line 6) | type AnthropicErrorType =
  type AnthropicErrorEnvelope (line 16) | interface AnthropicErrorEnvelope {
  function statusToErrorType (line 27) | function statusToErrorType(status: number): AnthropicErrorType {
  function wrapAnthropicError (line 47) | function wrapAnthropicError(
  function ensureAnthropicErrorFormat (line 63) | function ensureAnthropicErrorFormat(

FILE: packages/cli/src/handlers/shared/format/identity-filter.ts
  function filterIdentity (line 11) | function filterIdentity(content: string): string {

FILE: packages/cli/src/handlers/shared/format/openai-messages.ts
  function convertMessagesToOpenAI (line 11) | function convertMessagesToOpenAI(
  function processUserMessage (line 48) | function processUserMessage(msg: any, messages: any[], simpleFormat = fa...
  function processAssistantMessage (line 101) | function processAssistantMessage(msg: any, messages: any[], simpleFormat...

FILE: packages/cli/src/handlers/shared/format/openai-tools.ts
  function sanitizeSchemaForOpenAI (line 23) | function sanitizeSchemaForOpenAI(schema: any): any {
  function convertToolsToOpenAI (line 64) | function convertToolsToOpenAI(req: any, summarize = false): any[] {
  function summarizeToolDescription (line 85) | function summarizeToolDescription(name: string, description: string): st...
  function summarizeToolParameters (line 111) | function summarizeToolParameters(schema: any): any {

FILE: packages/cli/src/handlers/shared/gemini-queue.ts
  type QueuedRequest (line 19) | interface QueuedRequest {
  type QueueStats (line 28) | interface QueueStats {
  class GeminiRequestQueue (line 49) | class GeminiRequestQueue {
    method constructor (line 64) | private constructor() {
    method getInstance (line 71) | static getInstance(): GeminiRequestQueue {
    method enqueue (line 85) | async enqueue(fetchFn: () => Promise<Response>): Promise<Response> {
    method processQueue (line 115) | private async processQueue(): Promise<void> {
    method waitForNextSlot (line 166) | private async waitForNextSlot(): Promise<void> {
    method handleRateLimitResponse (line 191) | private handleRateLimitResponse(errorText: string): void {
    method handleSuccessResponse (line 233) | private handleSuccessResponse(): void {
    method getStats (line 252) | getStats(): QueueStats {

FILE: packages/cli/src/handlers/shared/gemini-schema.ts
  function sanitizeToolNameForGemini (line 21) | function sanitizeToolNameForGemini(name: string | undefined | null): str...
  function normalizeType (line 54) | function normalizeType(type: any): string {
  function sanitizeSchemaForGemini (line 78) | function sanitizeSchemaForGemini(schema: any): any {
  function convertToolsToGemini (line 151) | function convertToolsToGemini(tools: any[] | undefined): any {

FILE: packages/cli/src/handlers/shared/local-queue.ts
  type QueuedRequest (line 31) | interface QueuedRequest {
  type QueueStats (line 41) | interface QueueStats {
  class LocalModelQueue (line 69) | class LocalModelQueue {
    method constructor (line 85) | private constructor() {
    method getInstance (line 98) | static getInstance(): LocalModelQueue {
    method isEnabled (line 108) | static isEnabled(): boolean {
    method enqueue (line 126) | async enqueue(
    method processQueue (line 192) | private async processQueue(): Promise<void> {
    method executeRequest (line 219) | private async executeRequest(request: QueuedRequest): Promise<void> {
    method isOOMError (line 282) | private isOOMError(errorBody: string): boolean {
    method getMaxParallelFromEnv (line 301) | private getMaxParallelFromEnv(): number {
    method delay (line 322) | private delay(ms: number): Promise<void> {
    method getStats (line 329) | getStats(): QueueStats {

FILE: packages/cli/src/handlers/shared/openrouter-queue.ts
  type QueuedRequest (line 27) | interface QueuedRequest {
  type RateLimitState (line 36) | interface RateLimitState {
  type QueueStats (line 58) | interface QueueStats {
  class OpenRouterRequestQueue (line 83) | class OpenRouterRequestQueue {
    method constructor (line 108) | private constructor() {
    method getInstance (line 117) | static getInstance(): OpenRouterRequestQueue {
    method enqueue (line 131) | async enqueue(fetchFn: () => Promise<Response>): Promise<Response> {
    method processQueue (line 167) | private async processQueue(): Promise<void> {
    method waitForNextSlot (line 234) | private async waitForNextSlot(): Promise<void> {
    method calculateDelay (line 256) | private calculateDelay(): number {
    method parseRateLimitHeaders (line 323) | private parseRateLimitHeaders(response: Response): void {
    method handleRateLimitError (line 370) | private async handleRateLimitError(response: Response): Promise<void> {
    method handleSuccessResponse (line 419) | private handleSuccessResponse(): void {
    method getStats (line 444) | getStats(): QueueStats {

FILE: packages/cli/src/handlers/shared/remote-provider-types.ts
  type RemoteProviderConfig (line 11) | interface RemoteProviderConfig {
  type ModelPricing (line 27) | interface ModelPricing {
  type RemoteProvider (line 43) | interface RemoteProvider {
  type ResolvedRemoteProvider (line 59) | interface ResolvedRemoteProvider {
  constant PROVIDER_DEFAULTS (line 71) | const PROVIDER_DEFAULTS: Record<string, ModelPricing> = {
  constant FREE_PROVIDERS (line 81) | const FREE_PROVIDERS = new Set(["opencode-zen", "zen"]);
  constant SUBSCRIPTION_PROVIDERS (line 84) | const SUBSCRIPTION_PROVIDERS = new Set(["minimax-coding", "kimi-coding",...
  constant PROVIDER_ALIAS (line 87) | const PROVIDER_ALIAS: Record<string, string> = {
  function registerDynamicPricingLookup (line 110) | function registerDynamicPricingLookup(
  function getModelPricing (line 123) | function getModelPricing(provider: string, modelName: string): ModelPric...
  function calculateCost (line 152) | function calculateCost(

FILE: packages/cli/src/handlers/shared/stream-parsers/anthropic-sse.ts
  type AnthropicPassthroughOpts (line 16) | interface AnthropicPassthroughOpts {
  function createAnthropicPassthroughStream (line 31) | function createAnthropicPassthroughStream(

FILE: packages/cli/src/handlers/shared/stream-parsers/gemini-sse.ts
  type GeminiSseOptions (line 14) | interface GeminiSseOptions {
  function createGeminiSseStream (line 25) | function createGeminiSseStream(

FILE: packages/cli/src/handlers/shared/stream-parsers/ollama-jsonl.ts
  function createOllamaJsonlStream (line 15) | function createOllamaJsonlStream(

FILE: packages/cli/src/handlers/shared/stream-parsers/openai-responses-sse.ts
  function createResponsesStreamHandler (line 18) | function createResponsesStreamHandler(

FILE: packages/cli/src/handlers/shared/stream-parsers/openai-sse.ts
  type StreamingState (line 20) | interface StreamingState {
  type ToolState (line 34) | interface ToolState {
  function validateToolArguments (line 48) | function validateToolArguments(
  function createStreamingState (line 83) | function createStreamingState(): StreamingState {
  function createStreamingResponseHandler (line 102) | function createStreamingResponseHandler(
  function estimateTokens (line 704) | function estimateTokens(text: string): number {

FILE: packages/cli/src/handlers/shared/token-tracker.ts
  type TokenTrackerConfig (line 19) | interface TokenTrackerConfig {
  class TokenTracker (line 27) | class TokenTracker {
    method constructor (line 38) | constructor(port: number, config: TokenTrackerConfig) {
    method setActiveModelName (line 44) | setActiveModelName(name: string): void {
    method setProviderDisplayName (line 49) | setProviderDisplayName(name: string): void {
    method setQuotaRemaining (line 54) | setQuotaRemaining(fraction: number): void {
    method rewrite (line 59) | rewrite(): void {
    method update (line 67) | update(inputTokens: number, outputTokens: number): void {
    method accumulateBoth (line 84) | accumulateBoth(inputTokens: number, outputTokens: number): void {
    method updateWithDelta (line 105) | updateWithDelta(inputTokens: number, outputTokens: number): void {
    method updateWithActualCost (line 146) | updateWithActualCost(
    method updateLocal (line 171) | updateLocal(inputTokens: number, outputTokens: number): void {
    method setContextWindow (line 181) | setContextWindow(contextWindow: number): void {
    method getTotalCost (line 186) | getTotalCost(): number {
    method getInputTokens (line 191) | getInputTokens(): number {
    method getOutputTokens (line 196) | getOutputTokens(): number {
    method getPricing (line 200) | private getPricing(): ModelPricing {
    method getDisplayName (line 204) | private getDisplayName(): string {
    method writeFile (line 213) | private writeFile(inputTokens: number, outputTokens: number, isEstimat...

FILE: packages/cli/src/handlers/shared/tool-call-recovery.ts
  type ExtractedToolCall (line 13) | interface ExtractedToolCall {
  type ToolSchema (line 19) | interface ToolSchema {
  function extractToolCallsFromText (line 33) | function extractToolCallsFromText(text: string): ExtractedToolCall[] {
  function inferMissingParameters (line 313) | function inferMissingParameters(
  function generateRetryPrompt (line 499) | function generateRetryPrompt(
  function canRepairToolCall (line 530) | function canRepairToolCall(
  function getToolCallingGuidance (line 551) | function getToolCallingGuidance(): string {
  function validateAndRepairToolCall (line 579) | function validateAndRepairToolCall(

FILE: packages/cli/src/handlers/shared/web-search-detector.ts
  constant WEB_SEARCH_NAMES (line 9) | const WEB_SEARCH_NAMES = new Set([
  function isWebSearchToolCall (line 18) | function isWebSearchToolCall(toolName: string): boolean {
  function warnWebSearchUnsupported (line 25) | function warnWebSearchUnsupported(toolName: string, modelName: string): ...

FILE: packages/cli/src/handlers/types.ts
  type ModelHandler (line 3) | interface ModelHandler {

FILE: packages/cli/src/index.ts
  function loadStoredApiKeys (line 17) | function loadStoredApiKeys(): void {
  function handlePromptExit (line 51) | function handlePromptExit(err: unknown): void {
  function runCli (line 160) | async function runCli() {

FILE: packages/cli/src/logger.ts
  constant FLUSH_INTERVAL_MS (line 12) | const FLUSH_INTERVAL_MS = 100;
  constant MAX_BUFFER_SIZE (line 13) | const MAX_BUFFER_SIZE = 50;
  function flushLogBuffer (line 22) | function flushLogBuffer(): void {
  function flushAlwaysOnBuffer (line 39) | function flushAlwaysOnBuffer(): void {
  function scheduleFlush (line 49) | function scheduleFlush(): void {
  function rotateOldLogs (line 78) | function rotateOldLogs(dir: string, keep: number): void {
  function structuralRedact (line 97) | function structuralRedact(jsonStr: string): string {
  constant CONTENT_KEYS (line 108) | const CONTENT_KEYS = new Set([
  function redactDeep (line 118) | function redactDeep(val: any, key?: string): any {
  function isStructuralLogWorthy (line 144) | function isStructuralLogWorthy(msg: string): boolean {
  function redactLogLine (line 168) | function redactLogLine(message: string, timestamp: string): string {
  function initLogger (line 183) | function initLogger(
  function log (line 243) | function log(message: string, forceConsole = false): void {
  function logStderr (line 279) | function logStderr(message: string): void {
  function setDiagOutput (line 294) | function setDiagOutput(output: DiagOutput | null): void {
  function setStderrQuiet (line 303) | function setStderrQuiet(quiet: boolean): void {
  function getLogFilePath (line 310) | function getLogFilePath(): string | null {
  function getAlwaysOnLogPath (line 317) | function getAlwaysOnLogPath(): string | null {
  function isLoggingEnabled (line 324) | function isLoggingEnabled(): boolean {
  function maskCredential (line 332) | function maskCredential(credential: string): string {
  function setLogLevel (line 345) | function setLogLevel(level: "debug" | "info" | "minimal"): void {
  function getLogLevel (line 355) | function getLogLevel(): "debug" | "info" | "minimal" {
  function truncateContent (line 362) | function truncateContent(content: string | any, maxLength: number = 200)...
  function logStructured (line 375) | function logStructured(label: string, data: Record<string, any>): void {

FILE: packages/cli/src/mcp-server.ts
  constant CLAUDISH_CACHE_DIR (line 53) | const CLAUDISH_CACHE_DIR = join(homedir(), ".claudish");
  constant ALL_MODELS_CACHE_PATH (line 54) | const ALL_MODELS_CACHE_PATH = join(CLAUDISH_CACHE_DIR, "all-models.json");
  constant CACHE_MAX_AGE_DAYS (line 55) | const CACHE_MAX_AGE_DAYS = 2;
  constant INSTRUCTIONS (line 58) | const INSTRUCTIONS = `Claudish MCP server provides access to external AI...
  type ToolGroup (line 86) | type ToolGroup = "low-level" | "agentic" | "channel";
  type ToolDefinition (line 88) | interface ToolDefinition {
  function loadAllModels (line 105) | async function loadAllModels(forceRefresh = false): Promise<any[]> {
  function getProxy (line 148) | async function getProxy(): Promise<ProxyServer> {
  function parseAnthropicSse (line 171) | function parseAnthropicSse(raw: string): {
  function runPromptViaProxy (line 208) | async function runPromptViaProxy(
  function fuzzyScore (line 248) | function fuzzyScore(text: string, query: string): number {
  function formatTeamResult (line 264) | function formatTeamResult(
  function sanitize (line 304) | function sanitize(text: string | undefined): string {
  function defineTools (line 317) | function defineTools(sessionManager: SessionManager): ToolDefinition[] {
  function resolveToolGroups (line 1030) | function resolveToolGroups(mode: string): Set<ToolGroup> {
  function main (line 1046) | async function main() {
  function startMcpServer (line 1142) | function startMcpServer() {

FILE: packages/cli/src/middleware/gemini-thought-signature.ts
  class GeminiThoughtSignatureMiddleware (line 30) | class GeminiThoughtSignatureMiddleware implements ModelMiddleware {
    method shouldHandle (line 49) | shouldHandle(modelId: string): boolean {
    method onInit (line 53) | onInit(): void {
    method beforeRequest (line 66) | beforeRequest(context: RequestContext): void {
    method afterResponse (line 153) | afterResponse(context: NonStreamingResponseContext): void {
    method afterStreamChunk (line 192) | afterStreamChunk(context: StreamChunkContext): void {
    method afterStreamComplete (line 229) | afterStreamComplete(metadata: Map<string, any>): void {

FILE: packages/cli/src/middleware/manager.ts
  class MiddlewareManager (line 19) | class MiddlewareManager {
    method register (line 27) | register(middleware: ModelMiddleware): void {
    method initialize (line 42) | async initialize(): Promise<void> {
    method getActiveMiddlewares (line 69) | private getActiveMiddlewares(modelId: string): ModelMiddleware[] {
    method getActiveNames (line 77) | getActiveNames(modelId: string): string[] {
    method beforeRequest (line 84) | async beforeRequest(context: RequestContext): Promise<void> {
    method afterResponse (line 112) | async afterResponse(context: NonStreamingResponseContext): Promise<voi...
    method afterStreamChunk (line 140) | async afterStreamChunk(context: StreamChunkContext): Promise<void> {
    method afterStreamComplete (line 170) | async afterStreamComplete(modelId: string, metadata: Map<string, any>)...

FILE: packages/cli/src/middleware/types.ts
  type RequestContext (line 11) | interface RequestContext {
  type NonStreamingResponseContext (line 28) | interface NonStreamingResponseContext {
  type StreamChunkContext (line 39) | interface StreamChunkContext {
  type ModelMiddleware (line 62) | interface ModelMiddleware {

FILE: packages/cli/src/model-catalog.test.ts
  constant MINIMAX_API_KEY (line 21) | const MINIMAX_API_KEY = process.env.MINIMAX_CODING_API_KEY || process.en...
  constant SKIP_REAL_API (line 22) | const SKIP_REAL_API = !MINIMAX_API_KEY;
  constant MINIMAX_API_BASE (line 24) | const MINIMAX_API_BASE = "https://api.minimax.io/anthropic/v1/messages";
  function buildMinimaxPayload (line 366) | function buildMinimaxPayload(claudeRequest: any, modelId = "MiniMax-M2.7...

FILE: packages/cli/src/model-loader.ts
  type RecommendedModelEntry (line 20) | interface RecommendedModelEntry {
  type RecommendedModelsDoc (line 51) | interface RecommendedModelsDoc {
  type ModelDoc (line 63) | interface ModelDoc {
  type ModelMetadata (line 97) | interface ModelMetadata {
  constant FIREBASE_BASE_URL (line 112) | const FIREBASE_BASE_URL = "https://us-central1-claudish-6da10.cloudfunct...
  constant FIREBASE_RECOMMENDED_URL (line 113) | const FIREBASE_RECOMMENDED_URL = `${FIREBASE_BASE_URL}?catalog=recommend...
  constant RECOMMENDED_MODELS_CACHE_PATH (line 115) | const RECOMMENDED_MODELS_CACHE_PATH = join(
  constant RECOMMENDED_CACHE_MAX_AGE_HOURS (line 120) | const RECOMMENDED_CACHE_MAX_AGE_HOURS = 12;
  constant RECOMMENDED_FETCH_TIMEOUT_MS (line 121) | const RECOMMENDED_FETCH_TIMEOUT_MS = 5000;
  constant SEARCH_FETCH_TIMEOUT_MS (line 122) | const SEARCH_FETCH_TIMEOUT_MS = 10000;
  function getBundledRecommendedModelsPath (line 128) | function getBundledRecommendedModelsPath(): string {
  constant FIREBASE_SLUG_TO_PROVIDER_NAME (line 145) | const FIREBASE_SLUG_TO_PROVIDER_NAME: Record<string, string> = {
  type RecommendedModelGroup (line 161) | interface RecommendedModelGroup {
  function groupRecommendedModels (line 177) | function groupRecommendedModels(
  function collectRoutingPrefixes (line 215) | function collectRoutingPrefixes(
  function parsePriceAvg (line 237) | function parsePriceAvg(s?: string): number {
  function parseCtx (line 245) | function parseCtx(s?: string): number {
  function normalizePricingDisplay (line 259) | function normalizePricingDisplay(raw?: string): string {
  type QuickPicks (line 270) | interface QuickPicks {
  function computeQuickPicks (line 278) | function computeQuickPicks(primaries: RecommendedModelEntry[]): QuickPic...
  function getRecommendedModels (line 345) | async function getRecommendedModels(
  function getRecommendedModelsSync (line 408) | function getRecommendedModelsSync(): RecommendedModelsDoc {
  function warmRecommendedModels (line 432) | async function warmRecommendedModels(): Promise<RecommendedModelsDoc | n...
  function isFreshEnough (line 440) | function isFreshEnough(doc: RecommendedModelsDoc): boolean {
  function loadBundledRecommendedModels (line 447) | function loadBundledRecommendedModels(): RecommendedModelsDoc {
  function searchModels (line 470) | async function searchModels(query: string, limit = 50): Promise<ModelDoc...
  function searchModelsByProvider (line 488) | async function searchModelsByProvider(
  function getModelByIdFromFirebase (line 512) | async function getModelByIdFromFirebase(modelId: string): Promise<ModelD...
  type Top100Entry (line 535) | interface Top100Entry extends ModelDoc {
  type Top100Response (line 557) | interface Top100Response {
  function getTop100Models (line 578) | async function getTop100Models(): Promise<Top100Response> {
  type ProviderListEntry (line 597) | interface ProviderListEntry {
  function getProviderList (line 606) | async function getProviderList(): Promise<ProviderListEntry[]> {
  function getModelsByProvider (line 623) | async function getModelsByProvider(provider: string, limit = 200): Promi...
  function loadModelInfo (line 644) | function loadModelInfo(): Record<OpenRouterModel, ModelMetadata> {
  function getAvailableModels (line 676) | function getAvailableModels(): OpenRouterModel[] {
  type LiteLLMModel (line 694) | interface LiteLLMModel {
  type LiteLLMCache (line 707) | interface LiteLLMCache {
  constant LITELLM_CACHE_MAX_AGE_HOURS (line 712) | const LITELLM_CACHE_MAX_AGE_HOURS = 24;
  function fetchLiteLLMModels (line 717) | async function fetchLiteLLMModels(

FILE: packages/cli/src/model-selector.ts
  type ModelInfo (line 25) | interface ModelInfo {
  constant RECOMMENDED_PROVIDER_SOURCE_MAP (line 45) | const RECOMMENDED_PROVIDER_SOURCE_MAP: Record<
  constant RECOMMENDED_PROVIDER_LABEL_MAP (line 57) | const RECOMMENDED_PROVIDER_LABEL_MAP: Record<string, string> = {
  function getRecommendedModelSource (line 66) | function getRecommendedModelSource(provider: string): ModelInfo["source"] {
  function getRecommendedProviderLabel (line 70) | function getRecommendedProviderLabel(provider: string): string {
  function loadRecommendedModels (line 79) | async function loadRecommendedModels(forceRefresh = false): Promise<Mode...
  function parseContextString (line 102) | function parseContextString(ctx?: string): number {
  type PickerProvider (line 111) | interface PickerProvider {
  constant FIREBASE_PROVIDER_LABEL_MAP (line 117) | const FIREBASE_PROVIDER_LABEL_MAP: Record<string, string> = {
  function formatFirebaseProviderLabel (line 144) | function formatFirebaseProviderLabel(slug: string): string {
  function formatContextLength (line 160) | function formatContextLength(ctx?: number): string {
  function formatAveragePricing (line 166) | function formatAveragePricing(pricing?: ModelDoc["pricing"]): ModelInfo[...
  function modelDocToModelInfo (line 192) | function modelDocToModelInfo(model: ModelDoc): ModelInfo {
  function dedupeModels (line 212) | function dedupeModels(models: ModelInfo[]): ModelInfo[] {
  function buildPickerProviders (line 223) | function buildPickerProviders(entries: ProviderListEntry[]): PickerProvi...
  function buildPickerProvidersFromModels (line 231) | function buildPickerProvidersFromModels(models: ModelInfo[]): PickerProv...
  function matchesProvider (line 253) | function matchesProvider(model: ModelInfo, providerSlug: string): boolean {
  function filterModelsLocally (line 257) | function filterModelsLocally(
  function getXAIContextWindow (line 287) | function getXAIContextWindow(modelId: string): { context: string; contex...
  function fetchXAIModels (line 314) | async function fetchXAIModels(): Promise<ModelInfo[]> {
  function getGeminiPricing (line 379) | function getGeminiPricing(modelId: string): { input: string; output: str...
  function fetchGeminiModels (line 434) | async function fetchGeminiModels(): Promise<ModelInfo[]> {
  function getFreeModels (line 492) | async function getFreeModels(): Promise<ModelInfo[]> {
  function getAllModelsForSearch (line 501) | async function getAllModelsForSearch(forceUpdate = false): Promise<Model...
  function formatModelChoice (line 594) | function formatModelChoice(model: ModelInfo, showSource = false): string {
  constant PROVIDER_FILTER_ALIASES (line 637) | const PROVIDER_FILTER_ALIASES: Record<string, string> = {
  function parseProviderFilter (line 679) | function parseProviderFilter(
  function fuzzyMatch (line 737) | function fuzzyMatch(text: string, query: string): number {
  type ModelSelectorOptions (line 768) | interface ModelSelectorOptions {
  function selectModel (line 778) | async function selectModel(options: ModelSelectorOptions = {}): Promise<...
  constant ALL_PROVIDER_CHOICES (line 944) | const ALL_PROVIDER_CHOICES: Array<{
  function getProviderChoices (line 1028) | function getProviderChoices() {
  constant PROVIDER_MODEL_PREFIX (line 1039) | const PROVIDER_MODEL_PREFIX: Record<string, string> = {
  constant PROVIDER_SOURCE_FILTER (line 1061) | const PROVIDER_SOURCE_FILTER: Record<string, string> = {
  function getKnownModels (line 1081) | function getKnownModels(provider: string): ModelInfo[] {
  function filterModelsByProvider (line 1267) | function filterModelsByProvider(allModels: ModelInfo[], provider: string...
  function selectModelFromProvider (line 1284) | async function selectModelFromProvider(
  function selectModelsForProfile (line 1403) | async function selectModelsForProfile(): Promise<{
  function promptForApiKey (line 1468) | async function promptForApiKey(): Promise<string> {
  function promptForProfileName (line 1491) | async function promptForProfileName(existing: string[] = []): Promise<st...
  function promptForProfileDescription (line 1515) | async function promptForProfileDescription(): Promise<string> {
  function selectProfile (line 1526) | async function selectProfile(
  function confirmAction (line 1544) | async function confirmAction(message: string): Promise<boolean> {

FILE: packages/cli/src/port-manager.ts
  function findAvailablePort (line 7) | async function findAvailablePort(startPort = 3000, endPort = 9000): Prom...
  function isPortAvailable (line 28) | async function isPortAvailable(port: number): Promise<boolean> {

FILE: packages/cli/src/probe/probe-results-printer.ts
  constant ANSI_RE (line 39) | const ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]/g;
  function stripAnsi (line 41) | function stripAnsi(s: string): string {
  function visibleLength (line 46) | function visibleLength(s: string): number {
  function padVisible (line 51) | function padVisible(
  function truncate (line 63) | function truncate(s: string, max: number): string {
  function wordWrap (line 71) | function wordWrap(text: string, maxWidth: number): string[] {
  type ChainEntry (line 104) | interface ChainEntry {
  type WiringInfo (line 114) | interface WiringInfo {
  type ModelResult (line 124) | interface ModelResult {
  type Writer (line 135) | type Writer = (s: string) => boolean;
  constant MIN_CARD_WIDTH (line 137) | const MIN_CARD_WIDTH = 60;
  constant CARD_PADDING_LEFT (line 138) | const CARD_PADDING_LEFT = 2;
  constant CARD_PADDING_RIGHT (line 139) | const CARD_PADDING_RIGHT = 2;
  function summaryColor (line 141) | function summaryColor(live: number, total: number): string {
  function statusColor (line 147) | function statusColor(state: string): string {
  function shortStatusLabel (line 153) | function shortStatusLabel(probe: ProbeResult | undefined, hasCreds: bool...
  function renderBorderTop (line 181) | function renderBorderTop(title: string, summary: string, width: number):...
  function renderBorderBottom (line 202) | function renderBorderBottom(width: number): string {
  function renderBlankLine (line 206) | function renderBlankLine(width: number): string {
  function renderTextLine (line 217) | function renderTextLine(body: string, width: number, bg?: string): string {
  function renderRow (line 244) | function renderRow(
  function renderSepRow (line 298) | function renderSepRow(widths: number[], width: number): string {
  type RowData (line 326) | interface RowData {
  function buildRowData (line 338) | function buildRowData(result: ModelResult, isLiveProbe: boolean): RowDat...
  function buildDirectRowData (line 390) | function buildDirectRowData(result: ModelResult): RowData[] {
  function computeColumnWidths (line 416) | function computeColumnWidths(rows: RowData[]): number[] {
  function computeCardWidth (line 431) | function computeCardWidth(
  function formatContextWindow (line 461) | function formatContextWindow(ctx: number): string {
  function buildKeyLine (line 467) | function buildKeyLine(activeEntry?: ChainEntry, directKeyVar?: string): ...
  function buildWireLine (line 482) | function buildWireLine(wiring: WiringInfo, activeProvider?: string): str...
  type CardLayout (line 493) | interface CardLayout {
  function buildCardLayout (line 504) | function buildCardLayout(
  function computeRequiredWidth (line 562) | function computeRequiredWidth(
  function renderCard (line 577) | function renderCard(
  function printProbeResults (line 686) | function printProbeResults(

FILE: packages/cli/src/probe/probe-tui-app.tsx
  type ProbeStepState (line 19) | interface ProbeStepState {
  type ProbeLinkState (line 24) | interface ProbeLinkState {
  type ProbeAppState (line 38) | interface ProbeAppState {
  class ProbeStore (line 49) | class ProbeStore {
    method constructor (line 53) | constructor(initial: ProbeAppState) {
    method getState (line 57) | getState(): ProbeAppState {
    method setState (line 61) | setState(updater: (prev: ProbeAppState) => ProbeAppState): void {
    method subscribe (line 66) | subscribe(fn: () => void): () => void {
  function useProbeStore (line 74) | function useProbeStore(store: ProbeStore): ProbeAppState {
  function useAnimationFrame (line 81) | function useAnimationFrame(active: boolean): number {
  constant ANIM_FRAMES (line 93) | const ANIM_FRAMES = ["\u2593", "\u2592", "\u2591", "\u2592"];
  constant BAR_WIDTH (line 94) | const BAR_WIDTH = 20;
  function formatElapsed (line 96) | function formatElapsed(ms: number): string {
  function padEndSafe (line 103) | function padEndSafe(s: string, n: number): string {
  function stripAnsi (line 108) | function stripAnsi(text: string): string {
  function Banner (line 114) | function Banner() {
  function StepIndicator (line 182) | function StepIndicator({ step }: { step: ProbeStepState }) {
  function ProgressBar (line 207) | function ProgressBar({
  function ModelGroup (line 280) | function ModelGroup({
  function ProbeApp (line 330) | function ProbeApp({ store }: { store: ProbeStore }) {

FILE: packages/cli/src/probe/probe-tui-runtime.tsx
  type ProbeRuntime (line 13) | interface ProbeRuntime {
  function startProbeTui (line 18) | async function startProbeTui(

FILE: packages/cli/src/profile-commands.ts
  constant RESET (line 46) | const RESET = "\x1b[0m";
  constant BOLD (line 47) | const BOLD = "\x1b[1m";
  constant DIM (line 48) | const DIM = "\x1b[2m";
  constant GREEN (line 49) | const GREEN = "\x1b[32m";
  constant YELLOW (line 50) | const YELLOW = "\x1b[33m";
  constant CYAN (line 51) | const CYAN = "\x1b[36m";
  constant MAGENTA (line 52) | const MAGENTA = "\x1b[35m";
  function parseScopeFlag (line 59) | function parseScopeFlag(args: string[]): {
  function resolveScope (line 82) | async function resolveScope(scopeFlag: ProfileScope | undefined): Promis...
  function scopeBadge (line 107) | function scopeBadge(scope: ProfileScope, shadowed?: boolean): string {
  function initCommand (line 123) | async function initCommand(scopeFlag?: ProfileScope): Promise<void> {
  function profileListCommand (line 177) | async function profileListCommand(scopeFilter?: ProfileScope): Promise<v...
  function profileAddCommand (line 212) | async function profileAddCommand(scopeFlag?: ProfileScope): Promise<void> {
  function profileRemoveCommand (line 242) | async function profileRemoveCommand(name?: string, scopeFlag?: ProfileSc...
  function profileUseCommand (line 328) | async function profileUseCommand(name?: string, scopeFlag?: ProfileScope...
  function profileShowCommand (line 394) | async function profileShowCommand(name?: string, scopeFlag?: ProfileScop...
  function profileEditCommand (line 447) | async function profileEditCommand(name?: string, scopeFlag?: ProfileScop...
  function printProfile (line 559) | function printProfile(
  function printProfileWithScope (line 584) | function printProfileWithScope(profile: ProfileWithScope): void {
  function printModelMapping (line 603) | function printModelMapping(models: ModelMapping): void {
  function profileCommand (line 617) | async function profileCommand(args: string[]): Promise<void> {
  function printProfileHelp (line 658) | function printProfileHelp(): void {

FILE: packages/cli/src/profile-config.ts
  constant CONFIG_DIR (line 17) | const CONFIG_DIR = join(homedir(), ".claudish");
  constant CONFIG_FILE (line 18) | const CONFIG_FILE = join(CONFIG_DIR, "config.json");
  constant LOCAL_CONFIG_FILENAME (line 19) | const LOCAL_CONFIG_FILENAME = ".claudish.json";
  type ProfileScope (line 21) | type ProfileScope = "local" | "global";
  type ModelMapping (line 27) | interface ModelMapping {
  type Profile (line 37) | interface Profile {
  type ProfileWithScope (line 48) | interface ProfileWithScope extends Profile {
  type RoutingEntry (line 58) | type RoutingEntry = string;
  type RoutingRules (line 65) | type RoutingRules = Record<string, RoutingEntry[]>;
  type TelemetryConsent (line 72) | interface TelemetryConsent {
  type StatsConsent (line 91) | interface StatsConsent {
  type ClaudishProfileConfig (line 107) | interface ClaudishProfileConfig {
  constant DEFAULT_CONFIG (line 150) | const DEFAULT_CONFIG: ClaudishProfileConfig = {
  function ensureConfigDir (line 169) | function ensureConfigDir(): void {
  function loadConfig (line 179) | function loadConfig(): ClaudishProfileConfig {
  function saveConfig (line 233) | function saveConfig(config: ClaudishProfileConfig): void {
  function configExists (line 241) | function configExists(): boolean {
  function getConfigPath (line 248) | function getConfigPath(): string {
  function getLocalConfigPath (line 257) | function getLocalConfigPath(): string {
  function localConfigExists (line 264) | function localConfigExists(): boolean {
  function isProjectDirectory (line 271) | function isProjectDirectory(): boolean {
  function loadLocalConfig (line 282) | function loadLocalConfig(): ClaudishProfileConfig | null {
  function saveLocalConfig (line 312) | function saveLocalConfig(config: ClaudishProfileConfig): void {
  function loadConfigForScope (line 318) | function loadConfigForScope(scope: ProfileScope): ClaudishProfileConfig {
  function saveConfigForScope (line 325) | function saveConfigForScope(config: ClaudishProfileConfig, scope: Profil...
  function configExistsForScope (line 336) | function configExistsForScope(scope: ProfileScope): boolean {
  function getConfigPathForScope (line 346) | function getConfigPathForScope(scope: ProfileScope): string {
  function getProfile (line 359) | function getProfile(name: string, scope?: ProfileScope): Profile | undef...
  function getDefaultProfile (line 385) | function getDefaultProfile(scope?: ProfileScope): Profile {
  function getProfileNames (line 426) | function getProfileNames(scope?: ProfileScope): string[] {
  function setProfile (line 449) | function setProfile(profile: Profile, scope: ProfileScope = "global"): v...
  function deleteProfile (line 469) | function deleteProfile(name: string, scope: ProfileScope = "global"): bo...
  function setDefaultProfile (line 499) | function setDefaultProfile(name: string, scope: ProfileScope = "global")...
  function getModelMapping (line 515) | function getModelMapping(profileName?: string): ModelMapping {
  function createProfile (line 528) | function createProfile(
  function listProfiles (line 550) | function listProfiles(): Profile[] {
  function listAllProfiles (line 561) | function listAllProfiles(): ProfileWithScope[] {
  function getApiKey (line 597) | function getApiKey(envVar: string): string | undefined {
  function setApiKey (line 605) | function setApiKey(envVar: string, value: string): void {
  function removeApiKey (line 615) | function removeApiKey(envVar: string): void {
  function getEndpoint (line 628) | function getEndpoint(name: string): string | undefined {
  function setEndpoint (line 636) | function setEndpoint(name: string, value: string): void {
  function removeEndpoint (line 646) | function removeEndpoint(name: string): void {

FILE: packages/cli/src/providers/all-models-cache.test.ts
  function makeTmpCachePath (line 25) | function makeTmpCachePath(): { path: string; dir: string; cleanup: () =>...

FILE: packages/cli/src/providers/all-models-cache.ts
  type SlimModelEntry (line 27) | interface SlimModelEntry {
  type DiskCacheV2 (line 38) | interface DiskCacheV2 {
  constant ALL_MODELS_CACHE_PATH (line 46) | const ALL_MODELS_CACHE_PATH = join(homedir(), ".claudish", "all-models.j...
  function readAllModelsCache (line 59) | function readAllModelsCache(path: string = ALL_MODELS_CACHE_PATH): DiskC...
  function writeAllModelsCache (line 98) | function writeAllModelsCache(

FILE: packages/cli/src/providers/api-key-map.ts
  constant API_KEY_MAP (line 5) | const API_KEY_MAP: Record<string, { envVar: string; aliases?: string[] }...

FILE: packages/cli/src/providers/api-key-provenance.ts
  type KeyLayer (line 19) | interface KeyLayer {
  type KeyProvenance (line 25) | interface KeyProvenance {
  function maskKey (line 33) | function maskKey(key: string | undefined | null): string | null {
  function resolveApiKeyProvenance (line 45) | function resolveApiKeyProvenance(envVar: string, aliases?: string[]): Ke...
  function formatProvenanceLog (line 117) | function formatProvenanceLog(p: KeyProvenance): string {
  function formatProvenanceProbe (line 127) | function formatProvenanceProbe(p: KeyProvenance, indent: string = "    "...
  function readDotenvKey (line 150) | function readDotenvKey(envVars: string[]): string | null {
  function readConfigKey (line 164) | function readConfigKey(envVar: string): string | null {

FILE: packages/cli/src/providers/auto-route.ts
  type AutoRouteResult (line 10) | interface AutoRouteResult {
  type AutoRouteReason (line 18) | type AutoRouteReason =
  function readLiteLLMCacheSync (line 25) | function readLiteLLMCacheSync(baseUrl: string): Array<{ id: string; name...
  function checkOAuthForProvider (line 40) | function checkOAuthForProvider(nativeProvider: string, modelName: string...
  function checkApiKeyForProvider (line 52) | function checkApiKeyForProvider(nativeProvider: string, modelName: strin...
  type ProviderHintInfo (line 86) | interface ProviderHintInfo {
  constant PROVIDER_HINT_MAP (line 93) | const PROVIDER_HINT_MAP: Record<string, ProviderHintInfo> = {
  function getAutoRouteHint (line 117) | function getAutoRouteHint(modelName: string, nativeProvider: string): st...
  function autoRoute (line 148) | function autoRoute(modelName: string, nativeProvider: string): AutoRoute...
  type FallbackRoute (line 203) | interface FallbackRoute {
  constant PROVIDER_TO_PREFIX (line 220) | const PROVIDER_TO_PREFIX: Record<string, string> = (() => {
  constant DISPLAY_NAMES (line 231) | const DISPLAY_NAMES: Record<string, string> = (() => {
  type SubscriptionAlternative (line 249) | interface SubscriptionAlternative {
  constant SUBSCRIPTION_ALTERNATIVES (line 256) | const SUBSCRIPTION_ALTERNATIVES: Record<string, SubscriptionAlternative>...
  function readZenModelCacheSync (line 298) | function readZenModelCacheSync(): Set<string> | null {
  function isZenCompatibleModel (line 315) | function isZenCompatibleModel(modelName: string): boolean {
  function warmZenModelCache (line 325) | async function warmZenModelCache(): Promise<void> {
  function readZenGoModelCacheSync (line 351) | function readZenGoModelCacheSync(): Set<string> | null {
  function isZenGoCompatibleModel (line 368) | function isZenGoCompatibleModel(modelName: string): boolean {
  function warmZenGoModelCache (line 379) | async function warmZenGoModelCache(): Promise<void> {
  function hasProviderCredentials (line 401) | function hasProviderCredentials(provider: string): boolean {
  function getDefaultProviderRoute (line 418) | function getDefaultProviderRoute(
  function getFallbackChain (line 473) | function getFallbackChain(

FILE: packages/cli/src/providers/catalog-resolvers/litellm.ts
  function getCachePath (line 13) | function getCachePath(): string | null {
  class LiteLLMCatalogResolver (line 31) | class LiteLLMCatalogResolver implements ModelCatalogResolver {
    method resolveSync (line 34) | resolveSync(userInput: string): string | null {
    method warmCache (line 60) | async warmCache(): Promise<void> {
    method isCacheWarm (line 76) | isCacheWarm(): boolean {
    method ensureReady (line 80) | async ensureReady(_timeoutMs: number): Promise<void> {
    method _getModelIds (line 86) | private _getModelIds(): string[] | null {

FILE: packages/cli/src/providers/catalog-resolvers/openrouter.test.ts
  function entry (line 14) | function entry(
  constant SAMPLE_CATALOG (line 23) | const SAMPLE_CATALOG = [
  function createResolverWithCache (line 55) | function createResolverWithCache(data: typeof SAMPLE_CATALOG): OpenRoute...

FILE: packages/cli/src/providers/catalog-resolvers/openrouter.ts
  constant FIREBASE_CATALOG_URL (line 10) | const FIREBASE_CATALOG_URL =
  type DiskCache (line 14) | type DiskCache = DiskCacheV2;
  class OpenRouterCatalogResolver (line 37) | class OpenRouterCatalogResolver implements ModelCatalogResolver {
    method resolveSync (line 40) | resolveSync(userInput: string): string | null {
    method warmCache (line 99) | async warmCache(): Promise<void> {
    method isCacheWarm (line 106) | isCacheWarm(): boolean {
    method ensureReady (line 110) | async ensureReady(timeoutMs: number): Promise<void> {
    method _getOpenRouterExternalId (line 129) | private _getOpenRouterExternalId(entry: SlimModelEntry): string | null {
    method _getEntries (line 142) | private _getEntries(): SlimModelEntry[] | null {
    method _fetchAndCache (line 167) | private async _fetchAndCache(): Promise<void> {

FILE: packages/cli/src/providers/catalog-resolvers/static-fallback.ts
  constant OPENROUTER_VENDOR_MAP (line 6) | const OPENROUTER_VENDOR_MAP: Record<string, string> = {
  function staticOpenRouterFallback (line 33) | function staticOpenRouterFallback(userInput: string): string | null {

FILE: packages/cli/src/providers/custom-endpoints-loader.test.ts
  function makeConfig (line 18) | function makeConfig(

FILE: packages/cli/src/providers/custom-endpoints-loader.ts
  type LoadResult (line 47) | interface LoadResult {
  function loadCustomEndpoints (line 58) | function loadCustomEndpoints(config: ClaudishProfileConfig): LoadResult {
  function buildProviderDefinition (line 90) | function buildProviderDefinition(
  function buildProviderProfile (line 136) | function buildProviderProfile(ep: CustomEndpoint): ProviderProfile {
  function buildSimpleHandler (line 148) | function buildSimpleHandler(
  function buildComplexHandler (line 193) | function buildComplexHandler(
  function resolveCustomEndpointApiKey (line 266) | function resolveCustomEndpointApiKey(ep: CustomEndpoint): string {
  function stripTrailingSlash (line 273) | function stripTrailingSlash(url: string): string {
  function sanitizeEnvName (line 277) | function sanitizeEnvName(name: string): string {

FILE: packages/cli/src/providers/model-catalog-resolver.ts
  type ModelCatalogResolver (line 22) | interface ModelCatalogResolver {
  type ModelResolutionResult (line 63) | interface ModelResolutionResult {
  constant RESOLVER_REGISTRY (line 76) | const RESOLVER_REGISTRY = new Map<string, ModelCatalogResolver>();
  function registerResolver (line 78) | function registerResolver(resolver: ModelCatalogResolver): void {
  function getResolver (line 82) | function getResolver(provider: string): ModelCatalogResolver | null {
  function resolveModelNameSync (line 97) | function resolveModelNameSync(
  function logResolution (line 127) | function logResolution(
  function ensureCatalogReady (line 146) | async function ensureCatalogReady(
  function warmAllCatalogs (line 161) | async function warmAllCatalogs(providers?: string[]): Promise<void> {

FILE: packages/cli/src/providers/model-parser.ts
  type ParsedModel (line 48) | interface ParsedModel {
  constant PROVIDER_SHORTCUTS (line 75) | const PROVIDER_SHORTCUTS: Record<string, string> = _getShortcuts();
  constant LOCAL_PROVIDERS (line 80) | const LOCAL_PROVIDERS = {
  method has (line 81) | has(name: string): boolean {
  constant DIRECT_API_PROVIDERS (line 89) | const DIRECT_API_PROVIDERS = {
  method has (line 90) | has(name: string): boolean {
  constant NATIVE_MODEL_PATTERNS (line 98) | const NATIVE_MODEL_PATTERNS = _getNativeModelPatterns();
  constant LEGACY_PREFIX_PATTERNS (line 103) | const LEGACY_PREFIX_PATTERNS = _getLegacyPrefixPatterns();
  function parseModelSpec (line 115) | function parseModelSpec(modelSpec: string): ParsedModel {
  function isLocalProviderName (line 226) | function isLocalProviderName(provider: string): boolean {
  function isDirectApiProvider (line 233) | function isDirectApiProvider(provider: string): boolean {
  function getLegacySyntaxWarning (line 240) | function getLegacySyntaxWarning(parsed: ParsedModel): string | null {
  function formatModelSpec (line 255) | function formatModelSpec(provider: string, model: string, concurrency?: ...

FILE: packages/cli/src/providers/probe-live.ts
  type ProbeState (line 15) | type ProbeState =
  type ProbeResult (line 26) | interface ProbeResult {
  constant OAUTH_PROVIDERS (line 42) | const OAUTH_PROVIDERS = new Set(["vertex", "gemini-codeassist"]);
  constant PROBE_PROMPT (line 43) | const PROBE_PROMPT = "ping";
  constant PROBE_MAX_TOKENS (line 44) | const PROBE_MAX_TOKENS = 1;
  type ProbeLinkInput (line 46) | interface ProbeLinkInput {
  function probeLink (line 53) | async function probeLink(
  function annotateOAuthHint (line 124) | function annotateOAuthHint(
  function safeReadBody (line 153) | async function safeReadBody(response: Response): Promise<string> {
  function classifyHttpError (line 162) | function classifyHttpError(
  function extractErrorMessage (line 208) | function extractErrorMessage(body: string): string | undefined {
  function consumeProbeStream (line 233) | async function consumeProbeStream(
  type SseVerdict (line 289) | type SseVerdict = "live" | Omit<ProbeResult, "latencyMs"> | null;
  function interpretSseEvent (line 291) | function interpretSseEvent(rawEvent: string): SseVerdict {
  function isContentEvent (line 332) | function isContentEvent(parsed: any, eventType: string): boolean {
  function describeProbeState (line 347) | function describeProbeState(result: ProbeResult): string {
  function isReadyState (line 372) | function isReadyState(state: ProbeState): boolean {
  function isFailureState (line 376) | function isFailureState(state: ProbeState): boolean {

FILE: packages/cli/src/providers/provider-definitions.ts
  type TransportType (line 22) | type TransportType =
  type TokenStrategy (line 36) | type TokenStrategy = "delta-aware" | "accumulate-both" | undefined;
  type ProviderCapabilities (line 38) | interface ProviderCapabilities {
  type ProviderDefinition (line 46) | interface ProviderDefinition {
  constant BUILTIN_PROVIDERS (line 99) | const BUILTIN_PROVIDERS: ProviderDefinition[] = [
  function ensureProviderByNameCache (line 664) | function ensureProviderByNameCache(): Map<string, ProviderDefinition> {
  function getShortcuts (line 682) | function getShortcuts(): Record<string, string> {
  function getLegacyPrefixPatterns (line 706) | function getLegacyPrefixPatterns(): Array<{
  function getNativeModelPatterns (line 734) | function getNativeModelPatterns(): Array<{ pattern: RegExp; provider: st...
  function getProviderByName (line 756) | function getProviderByName(name: string): ProviderDefinition | undefined {
  function getApiKeyInfo (line 766) | function getApiKeyInfo(providerName: string): {
  function getDisplayName (line 788) | function getDisplayName(providerName: string): string {
  function getEffectiveBaseUrl (line 796) | function getEffectiveBaseUrl(def: ProviderDefinition): string {
  function isLocalTransport (line 810) | function isLocalTransport(providerName: string): boolean {
  function isDirectApiProvider (line 830) | function isDirectApiProvider(providerName: string): boolean {
  function toRemoteProvider (line 849) | function toRemoteProvider(def: ProviderDefinition): RemoteProvider {
  function getAllProviders (line 879) | function getAllProviders(): ProviderDefinition[] {
  function getShortestPrefix (line 889) | function getShortestPrefix(providerName: string): string {
  function getApiKeyEnvVars (line 898) | function getApiKeyEnvVars(
  function isProviderAvailable (line 921) | function isProviderAvailable(def: ProviderDefinition): boolean {
  function isProviderAvailableByName (line 956) | function isProviderAvailableByName(providerName: string): boolean {

FILE: packages/cli/src/providers/provider-profiles.ts
  type BaseModelAdapter (line 21) | type BaseModelAdapter = BaseAPIFormat;
  type ProfileContext (line 54) | interface ProfileContext {
  type ProviderProfile (line 75) | interface ProviderProfile {
  method createHandler (line 90) | createHandler(ctx) {
  method createHandler (line 103) | createHandler(ctx) {
  method createHandler (line 117) | createHandler(ctx) {
  method createHandler (line 134) | createHandler(ctx) {
  method createHandler (line 149) | createHandler(ctx) {
  method createHandler (line 163) | createHandler(ctx) {
  method createHandler (line 190) | createHandler(ctx) {
  method createHandler (line 236) | createHandler(ctx) {
  method createHandler (line 250) | createHandler(ctx) {
  method createHandler (line 280) | createHandler(ctx) {
  constant PROVIDER_PROFILES (line 351) | const PROVIDER_PROFILES: Record<string, ProviderProfile> = {
  function createHandlerForProvider (line 382) | function createHandlerForProvider(ctx: ProfileContext): ModelHandler | n...

FILE: packages/cli/src/providers/provider-registry.ts
  type LocalProvider (line 13) | interface LocalProvider {
  type ResolvedProvider (line 21) | interface ResolvedProvider {
  type UrlParsedModel (line 28) | interface UrlParsedModel {
  function getRegisteredProviders (line 68) | function getRegisteredProviders(): LocalProvider[] {
  function resolveProvider (line 77) | function resolveProvider(modelId: string): ResolvedProvider | null {
  function isLocalProvider (line 127) | function isLocalProvider(modelId: string): boolean {
  function parseUrlModel (line 151) | function parseUrlModel(modelId: string): UrlParsedModel | null {
  function createUrlProvider (line 191) | function createUrlProvider(parsed: UrlParsedModel): LocalProvider {

FILE: packages/cli/src/providers/provider-resolver.ts
  type ProviderCategory (line 48) | type ProviderCategory =
  type ProviderResolution (line 58) | interface ProviderResolution {
  type ApiKeyInfo (line 92) | interface ApiKeyInfo {
  function getApiKeyInfoForProvider (line 104) | function getApiKeyInfoForProvider(providerName: string): ApiKeyInfo {
  constant API_KEY_INFO (line 125) | const API_KEY_INFO = new Proxy<Record<string, ApiKeyInfo>>(
  method get (line 128) | get(_target, prop: string) {
  method has (line 131) | has() {
  constant PROVIDER_DISPLAY_NAMES (line 140) | const PROVIDER_DISPLAY_NAMES = new Proxy<Record<string, string>>(
  method get (line 143) | get(_target, prop: string) {
  function isApiKeyAvailable (line 154) | function isApiKeyAvailable(info: ApiKeyInfo): boolean {
  function resolveModelProvider (line 208) | function resolveModelProvider(modelId: string | undefined): ProviderReso...
  function validateApiKeysForModels (line 424) | function validateApiKeysForModels(models: (string | undefined)[]): Provi...
  function getMissingKeyResolutions (line 434) | function getMissingKeyResolutions(resolutions: ProviderResolution[]): Pr...
  function getMissingKeyError (line 444) | function getMissingKeyError(resolution: ProviderResolution): string {
  function getMissingKeysError (line 533) | function getMissingKeysError(resolutions: ProviderResolution[]): string {
  function requiresOpenRouterKey (line 578) | function requiresOpenRouterKey(modelId: string | undefined): boolean {
  function isLocalModel (line 592) | function isLocalModel(modelId: string | undefined): boolean {

FILE: packages/cli/src/providers/remote-provider-registry.ts
  function resolveRemoteProvider (line 54) | function resolveRemoteProvider(modelId: string): ResolvedRemoteProvider ...
  function hasRemoteProviderPrefix (line 101) | function hasRemoteProviderPrefix(modelId: string): boolean {
  function getRemoteProviderType (line 109) | function getRemoteProviderType(modelId: string): string | null {
  function validateRemoteProviderApiKey (line 118) | function validateRemoteProviderApiKey(provider: RemoteProvider): string ...
  function getRegisteredRemoteProviders (line 157) | function getRegisteredRemoteProviders(): RemoteProvider[] {

FILE: packages/cli/src/providers/routing-rules.ts
  function loadRoutingRules (line 13) | function loadRoutingRules(): RoutingRules | null {
  function validateRoutingRules (line 28) | function validateRoutingRules(rules: RoutingRules): void {
  function matchRoutingRule (line 50) | function matchRoutingRule(modelName: string, rules: RoutingRules): Routi...
  function buildRoutingChain (line 74) | function buildRoutingChain(
  function globMatch (line 114) | function globMatch(pattern: string, value: string): boolean {

FILE: packages/cli/src/providers/runtime-providers.test.ts
  function makeDef (line 16) | function makeDef(name: string, overrides: Partial<ProviderDefinition> = ...
  function makeProfile (line 32) | function makeProfile(): ProviderProfile {

FILE: packages/cli/src/providers/runtime-providers.ts
  function registerRuntimeProvider (line 25) | function registerRuntimeProvider(def: ProviderDefinition): void {
  function registerRuntimeProfile (line 32) | function registerRuntimeProfile(name: string, profile: ProviderProfile):...
  function getRuntimeProviders (line 40) | function getRuntimeProviders(): ReadonlyMap<string, ProviderDefinition> {
  function getRuntimeProfiles (line 48) | function getRuntimeProfiles(): ReadonlyMap<string, ProviderProfile> {
  function clearRuntimeRegistry (line 56) | function clearRuntimeRegistry(): void {

FILE: packages/cli/src/providers/transport/anthropic-compat.test.ts
  constant TEST_API_KEY (line 19) | const TEST_API_KEY = "test-key-abc123";

FILE: packages/cli/src/providers/transport/anthropic-compat.ts
  class AnthropicProviderTransport (line 17) | class AnthropicProviderTransport implements ProviderTransport {
    method constructor (line 25) | constructor(provider: RemoteProvider, apiKey: string) {
    method getEndpoint (line 32) | getEndpoint(): string {
    method getHeaders (line 36) | async getHeaders(): Promise<Record<string, string>> {
    method formatDisplayName (line 79) | private static formatDisplayName(name: string): string {

FILE: packages/cli/src/providers/transport/gemini-apikey.ts
  class GeminiProviderTransport (line 16) | class GeminiProviderTransport implements ProviderTransport {
    method constructor (line 25) | constructor(provider: RemoteProvider, modelName: string, apiKey: strin...
    method getEndpoint (line 31) | getEndpoint(_model?: string): string {
    method getHeaders (line 36) | async getHeaders(): Promise<Record<string, string>> {
    method enqueueRequest (line 46) | async enqueueRequest(fetchFn: () => Promise<Response>): Promise<Respon...

FILE: packages/cli/src/providers/transport/gemini-codeassist.ts
  constant CODE_ASSIST_BASE (line 25) | const CODE_ASSIST_BASE = "https://cloudcode-pa.googleapis.com";
  constant CODE_ASSIST_ENDPOINT (line 26) | const CODE_ASSIST_ENDPOINT = `${CODE_ASSIST_BASE}/v1internal:streamGener...
  constant CODE_ASSIST_FALLBACK_CHAIN (line 32) | const CODE_ASSIST_FALLBACK_CHAIN = [
  constant MAX_RETRY_ATTEMPTS (line 41) | const MAX_RETRY_ATTEMPTS = 3;
  constant DEFAULT_RATE_LIMIT_DELAY_MS (line 43) | const DEFAULT_RATE_LIMIT_DELAY_MS = 10_000;
  function buildGeminiCliUserAgent (line 49) | function buildGeminiCliUserAgent(model?: string): string {
  function createActivityRequestId (line 56) | function createActivityRequestId(): string {
  type QuotaClassification (line 61) | interface QuotaClassification {
  function classify429 (line 77) | function classify429(responseBody: string): QuotaClassification | null {
  function parseRetryDelay (line 141) | function parseRetryDelay(value: any): number | undefined {
  class GeminiCodeAssistProviderTransport (line 156) | class GeminiCodeAssistProviderTransport implements ProviderTransport {
    method displayName (line 159) | get displayName(): string {
    method constructor (line 178) | constructor(modelName: string) {
    method getActiveModelName (line 186) | getActiveModelName(): string | undefined {
    method getEndpoint (line 190) | getEndpoint(): string {
    method getHeaders (line 194) | async getHeaders(): Promise<Record<string, string>> {
    method refreshAuth (line 206) | async refreshAuth(): Promise<void> {
    method transformPayload (line 224) | transformPayload(payload: any): any {
    method buildEnvelope (line 233) | private buildEnvelope(innerPayload: any, model: string): any {
    method enqueueRequest (line 256) | async enqueueRequest(fetchFn: () => Promise<Response>): Promise<Respon...
    method handleCapacityExhausted (line 317) | private async handleCapacityExhausted(
    method logQuotaInfo (line 387) | private async logQuotaInfo(): Promise<void> {
    method getQuotaRemaining (line 420) | async getQuotaRemaining(modelName: string): Promise<number | undefined> {

FILE: packages/cli/src/providers/transport/litellm.ts
  constant MODEL_EXTRA_HEADERS (line 17) | const MODEL_EXTRA_HEADERS: Array<{ pattern: string; headers: Record<stri...
  class LiteLLMProviderTransport (line 21) | class LiteLLMProviderTransport implements ProviderTransport {
    method constructor (line 30) | constructor(baseUrl: string, apiKey: string, modelName: string) {
    method overrideStreamFormat (line 40) | overrideStreamFormat(): StreamFormat {
    method getEndpoint (line 44) | getEndpoint(): string {
    method getHeaders (line 48) | async getHeaders(): Promise<Record<string, string>> {
    method getExtraPayloadFields (line 55) | getExtraPayloadFields(): Record<string, any> {
    method getExtraHeaders (line 70) | private getExtraHeaders(): Record<string, string> | null {

FILE: packages/cli/src/providers/transport/local.ts
  constant DISPLAY_NAMES (line 29) | const DISPLAY_NAMES: Record<string, string> = {
  class LocalTransport (line 37) | class LocalTransport implements ProviderTransport {
    method constructor (line 49) | constructor(config: LocalProviderConfig, modelName: string, options?: ...
    method getEndpoint (line 73) | getEndpoint(): string {
    method getHeaders (line 77) | async getHeaders(): Promise<Record<string, string>> {
    method getRequestInit (line 81) | getRequestInit(): Record<string, any> {
    method getExtraPayloadFields (line 89) | getExtraPayloadFields(): Record<string, any> {
    method enqueueRequest (line 99) | async enqueueRequest(fetchFn: () => Promise<Response>): Promise<Respon...
    method refreshAuth (line 108) | async refreshAuth(): Promise<void> {
    method getContextWindow (line 119) | getContextWindow(): number {
    method getConfig (line 124) | getConfig(): LocalProviderConfig {
    method checkHealth (line 130) | private async checkHealth(): Promise<boolean> {
    method fetchContextWindow (line 180) | private async fetchContextWindow(): Promise<void> {
    method fetchOllamaContextWindow (line 196) | private async fetchOllamaContextWindow(): Promise<void> {
    method fetchLMStudioContextWindow (line 236) | private async fetchLMStudioContextWindow(): Promise<void> {
    method getConnectionErrorMessage (line 279) | private getConnectionErrorMessage(): string {

FILE: packages/cli/src/providers/transport/ollamacloud.ts
  class OllamaProviderTransport (line 11) | class OllamaProviderTransport implements ProviderTransport {
    method constructor (line 19) | constructor(provider: RemoteProvider, apiKey: string) {
    method getEndpoint (line 24) | getEndpoint(): string {
    method getHeaders (line 28) | async getHeaders(): Promise<Record<string, string>> {

FILE: packages/cli/src/providers/transport/openai-codex.ts
  function buildOAuthHeaders (line 22) | function buildOAuthHeaders(token: string, accountId?: string): Record<st...
  constant CHATGPT_API_URL (line 39) | const CHATGPT_API_URL = "https://chatgpt.com/backend-api/codex";
  class OpenAICodexTransport (line 41) | class OpenAICodexTransport extends OpenAIProviderTransport {
    method getHeaders (line 42) | override async getHeaders(): Promise<Record<string, string>> {
    method getEndpoint (line 54) | getEndpoint(): string {
    method tryOAuthHeaders (line 76) | private async tryOAuthHeaders(): Promise<Record<string, string> | null> {
    method transformPayload (line 107) | transformPayload(payload: any): any {

FILE: packages/cli/src/providers/transport/openai.ts
  class OpenAIProviderTransport (line 13) | class OpenAIProviderTransport implements ProviderTransport {
    method constructor (line 22) | constructor(provider: RemoteProvider, modelName: string, apiKey: strin...
    method getEndpoint (line 35) | getEndpoint(): string {
    method getHeaders (line 42) | async getHeaders(): Promise<Record<string, string>> {
    method enqueueRequest (line 54) | async enqueueRequest(fetchFn: () => Promise<Response>): Promise<Respon...
    method formatDisplayName (line 98) | static formatDisplayName(name: string): string {
  class OpenAITimeoutError (line 108) | class OpenAITimeoutError extends Error {
    method constructor (line 109) | constructor(baseUrl: string) {
  class OpenAIConnectionError (line 115) | class OpenAIConnectionError extends Error {
    method constructor (line 116) | constructor(baseUrl: string, code: string) {

FILE: packages/cli/src/providers/transport/openrouter.ts
  constant OPENROUTER_API_URL (line 18) | const OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions";
  class OpenRouterProviderTransport (line 20) | class OpenRouterProviderTransport implements ProviderTransport {
    method constructor (line 28) | constructor(apiKey: string, _modelId?: string) {
    method overrideStreamFormat (line 37) | overrideStreamFormat(): StreamFormat {
    method getEndpoint (line 41) | getEndpoint(): string {
    method getHeaders (line 45) | async getHeaders(): Promise<Record<string, string>> {
    method enqueueRequest (line 53) | async enqueueRequest(fetchFn: () => Promise<Response>): Promise<Respon...
    method getContextWindow (line 62) | getContextWindow(): number {

FILE: packages/cli/src/providers/transport/poe.ts
  constant POE_API_URL (line 12) | const POE_API_URL = "https://api.poe.com/v1/chat/completions";
  class PoeProvider (line 14) | class PoeProvider implements ProviderTransport {
    method constructor (line 21) | constructor(apiKey: string) {
    method getEndpoint (line 25) | getEndpoint(): string {
    method getHeaders (line 29) | async getHeaders(): Promise<Record<string, string>> {

FILE: packages/cli/src/providers/transport/types.ts
  type StreamFormat (line 9) | type StreamFormat =
  type ProviderTransport (line 23) | interface ProviderTransport {

FILE: packages/cli/src/providers/transport/vertex-oauth.ts
  type ParsedVertexModel (line 23) | interface ParsedVertexModel {
  function parseVertexModel (line 33) | function parseVertexModel(modelId: string): ParsedVertexModel {
  class VertexProviderTransport (line 41) | class VertexProviderTransport implements ProviderTransport {
    method constructor (line 50) | constructor(config: VertexConfig, parsed: ParsedVertexModel) {
    method getEndpoint (line 64) | getEndpoint(): string {
    method getHeaders (line 73) | async getHeaders(): Promise<Record<string, string>> {
    method getRequestInit (line 79) | getRequestInit(): Record<string, any> {
    method refreshAuth (line 85) | async refreshAuth(): Promise<void> {
    method forceRefreshAuth (line 94) | async forceRefreshAuth(): Promise<void> {
    method transformPayload (line 105) | transformPayload(payload: any): any {
    method getParsed (line 114) | getParsed(): ParsedVertexModel {

FILE: packages/cli/src/proxy-server.ts
  function getEffectiveDefaultProvider (line 54) | function getEffectiveDefaultProvider(): string {
  type ProxyServerOptions (line 64) | interface ProxyServerOptions {
  function createProxyServer (line 72) | async function createProxyServer(

FILE: packages/cli/src/services/pricing-cache.ts
  constant CACHE_DIR (line 30) | const CACHE_DIR = join(homedir(), ".claudish");
  constant CACHE_FILE (line 31) | const CACHE_FILE = join(CACHE_DIR, "pricing-cache.json");
  constant CACHE_TTL_MS (line 32) | const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
  constant PROVIDER_TO_OR_PREFIX (line 41) | const PROVIDER_TO_OR_PREFIX: Record<string, string[]> = {
  function getDynamicPricingSync (line 60) | function getDynamicPricingSync(
  function warmPricingCache (line 104) | async function warmPricingCache(): Promise<void> {
  function loadDiskCache (line 128) | function loadDiskCache(): boolean {

FILE: packages/cli/src/services/vision-proxy.ts
  constant VISION_MODEL (line 13) | const VISION_MODEL = "claude-sonnet-4-20250514";
  constant MAX_TOKENS_PER_IMAGE (line 14) | const MAX_TOKENS_PER_IMAGE = 1024;
  constant VISION_ENDPOINT (line 15) | const VISION_ENDPOINT = "https://api.anthropic.com/v1/messages";
  constant TIMEOUT_MS (line 16) | const TIMEOUT_MS = 30_000;
  constant DESCRIPTION_PROMPT (line 18) | const DESCRIPTION_PROMPT = `Describe this image in detail for a model th...
  type VisionProxyAuthHeaders (line 33) | interface VisionProxyAuthHeaders {
  type OpenAIImageBlock (line 41) | interface OpenAIImageBlock {
  function parseDataUrl (line 52) | function parseDataUrl(dataUrl: string): { mediaType: string; data: strin...
  function describeImage (line 74) | async function describeImage(
  function describeImages (line 164) | async function describeImages(

FILE: packages/cli/src/stats-buffer.test.ts
  constant CLAUDISH_DIR (line 11) | const CLAUDISH_DIR = join(homedir(), ".claudish");
  constant BUFFER_FILE (line 12) | const BUFFER_FILE = join(CLAUDISH_DIR, "stats-buffer.json");
  constant BACKUP_FILE (line 13) | const BACKUP_FILE = join(CLAUDISH_DIR, "stats-buffer.json.bak");
  function makeEvent (line 15) | function makeEvent(overrides: Partial<StatsEvent> = {}): StatsEvent {

FILE: packages/cli/src/stats-buffer.ts
  constant BUFFER_MAX_BYTES (line 25) | const BUFFER_MAX_BYTES = 64 * 1024;
  constant FLUSH_EVERY_N_EVENTS (line 26) | const FLUSH_EVERY_N_EVENTS = 10;
  constant FLUSH_EVERY_MS (line 27) | const FLUSH_EVERY_MS = 60_000;
  constant CLAUDISH_DIR (line 29) | const CLAUDISH_DIR = join(homedir(), ".claudish");
  constant BUFFER_FILE (line 30) | const BUFFER_FILE = join(CLAUDISH_DIR, "stats-buffer.json");
  type BufferFile (line 32) | interface BufferFile {
  function ensureDir (line 47) | function ensureDir(): void {
  function readFromDisk (line 56) | function readFromDisk(): StatsEvent[] {
  function enforceSizeCap (line 72) | function enforceSizeCap(events: StatsEvent[]): StatsEvent[] {
  function writeToDisk (line 87) | function writeToDisk(events: StatsEvent[]): void {
  function flushToDisk (line 106) | function flushToDisk(): void {
  function scheduleFlushed (line 119) | function scheduleFlushed(): void {
  function appendEvent (line 137) | function appendEvent(event: StatsEvent): void {
  function readBuffer (line 161) | function readBuffer(): StatsEvent[] {
  function clearBuffer (line 173) | function clearBuffer(): void {
  function flushBufferToDisk (line 189) | function flushBufferToDisk(): void {
  function getBufferStats (line 200) | function getBufferStats(): { events: number; bytes: number } {
  function syncFlushOnExit (line 214) | function syncFlushOnExit(): void {

FILE: packages/cli/src/stats-otlp.test.ts
  constant SAMPLE_RESOURCE (line 10) | const SAMPLE_RESOURCE: OtlpResource = {
  constant SAMPLE_EVENT (line 19) | const SAMPLE_EVENT: StatsEvent = {

FILE: packages/cli/src/stats-otlp.ts
  type StatsEvent (line 17) | interface StatsEvent {
  type StatsConsent (line 64) | interface StatsConsent {
  type OtlpStringAttr (line 79) | interface OtlpStringAttr {
  type OtlpIntAttr (line 84) | interface OtlpIntAttr {
  type OtlpDoubleAttr (line 89) | interface OtlpDoubleAttr {
  type OtlpBoolAttr (line 94) | interface OtlpBoolAttr {
  type OtlpArrayAttr (line 99) | interface OtlpArrayAttr {
  type OtlpAttr (line 104) | type OtlpAttr = OtlpStringAttr | OtlpIntAttr | OtlpDoubleAttr | OtlpBool...
  type OtlpLogRecord (line 106) | interface OtlpLogRecord {
  type OtlpResource (line 114) | interface OtlpResource {
  function stringAttr (line 125) | function stringAttr(key: string, value: string): OtlpStringAttr {
  function intAttr (line 129) | function intAttr(key: string, value: number): OtlpIntAttr {
  function doubleAttr (line 133) | function doubleAttr(key: string, value: number): OtlpDoubleAttr {
  function boolAttr (line 137) | function boolAttr(key: string, value: boolean): OtlpBoolAttr {
  function arrayAttr (line 141) | function arrayAttr(key: string, values: string[]): OtlpArrayAttr {
  function buildResource (line 158) | function buildResource(res: OtlpResource): OtlpAttr[] {
  function eventToLogRecord (line 184) | function eventToLogRecord(event: StatsEvent): OtlpLogRecord {
  function formatOtlpBatch (line 239) | function formatOtlpBatch(events: StatsEvent[], resource: OtlpResource): ...

FILE: packages/cli/src/stats.test.ts
  constant CLAUDISH_DIR (line 6) | const CLAUDISH_DIR = join(homedir(), ".claudish");
  constant CONFIG_FILE (line 7) | const CONFIG_FILE = join(CLAUDISH_DIR, "config.json");
  function backupFile (line 9) | function backupFile(path: string): string | null {
  function restoreFile (line 23) | function restoreFile(path: string, backup: string | null): void {

FILE: packages/cli/src/stats.ts
  constant STATS_ENDPOINT (line 37) | const STATS_ENDPOINT = "https://claudish.com/v1/stats";
  constant FLUSH_INTERVAL_MS (line 38) | const FLUSH_INTERVAL_MS = 24 * 60 * 60 * 1000;
  constant MONTHLY_INTERVAL_MS (line 39) | const MONTHLY_INTERVAL_MS = 30 * 24 * 60 * 60 * 1000;
  constant SEND_TIMEOUT_MS (line 40) | const SEND_TIMEOUT_MS = 5000;
  function getVersion (line 71) | function getVersion(): string {
  function detectTimezone (line 77) | function detectTimezone(): string {
  function isStatsDisabledByEnv (line 85) | function isStatsDisabledByEnv(): boolean {
  function initStats (line 96) | function initStats(config: ClaudishConfig): void {
  function recordStats (line 135) | function recordStats(partial: Partial<StatsEvent>): void {
  function checkAndFlush (line 197) | function checkAndFlush(): void {
  function flushStats (line 224) | async function flushStats(): Promise<void> {
  function showMonthlyBanner (line 290) | function showMonthlyBanner(): void {
  function handleStatsCommand (line 350) | async function handleStatsCommand(subcommand: string): Promise<void> {

FILE: packages/cli/src/team-cli.ts
  function getFlag (line 14) | function getFlag(args: string[], flag: string): string | undefined {
  function hasFlag (line 20) | function hasFlag(args: string[], flag: string): boolean {
  function printStatus (line 26) | function printStatus(status: TeamStatus): void {
  function printHelp (line 44) | function printHelp(): void {
  function teamCommand (line 78) | async function teamCommand(args: string[]): Promise<void> {

FILE: packages/cli/src/team-grid.e2e-helpers.ts
  function findMagmuxForTest (line 34) | function findMagmuxForTest(): string {
  type PtyRunOptions (line 58) | interface PtyRunOptions {
  type PtyHandle (line 66) | interface PtyHandle {
  function runInPty (line 89) | function runInPty(opts: PtyRunOptions): PtyHandle {
  function stripAnsi (line 154) | function stripAnsi(input: string): string {
  function shellQuote (line 172) | function shellQuote(arg: string): string {
  function tclBrace (line 184) | function tclBrace(s: string): string {
  type MagmuxEvent (line 206) | interface MagmuxEvent {
  type MagmuxSubscription (line 211) | interface MagmuxSubscription {
  type MagmuxSocketBaseline (line 223) | interface MagmuxSocketBaseline {
  function snapshotMagmuxSockets (line 234) | function snapshotMagmuxSockets(): MagmuxSocketBaseline {
  function findNewestMagmuxSocket (line 255) | async function findNewestMagmuxSocket(
  function subscribeToMagmuxSocket (line 301) | async function subscribeToMagmuxSocket(
  function writeGridfile (line 434) | function writeGridfile(lines: string[]): {

FILE: packages/cli/src/team-grid.e2e.test.ts
  constant E2E_TIMEOUT (line 33) | const E2E_TIMEOUT = 150_000;
  function devClaudishCommand (line 251) | function devClaudishCommand(model: string, prompt: string): string {

FILE: packages/cli/src/team-grid.ts
  type RouteInfo (line 25) | interface RouteInfo {
  function resolveRouteInfo (line 31) | function resolveRouteInfo(modelId: string): RouteInfo {
  constant BANNER_BG_COLORS (line 105) | const BANNER_BG_COLORS = [
  function pickBannerColor (line 119) | function pickBannerColor(model: string, used: Set<number>): string {
  function buildPaneHeader (line 131) | function buildPaneHeader(model: string, prompt: string, bg: string): str...
  function findMagmuxBinary (line 173) | function findMagmuxBinary(): string {
  type PaneResult (line 226) | interface PaneResult {
  type MagmuxResultsEvent (line 241) | interface MagmuxResultsEvent {
  function subscribeToMagmux (line 254) | async function subscribeToMagmux(
  function buildTeamStatus (line 316) | function buildTeamStatus(
  function runWithGrid (line 390) | async function runWithGrid(

FILE: packages/cli/src/team-orchestrator.test.ts
  function getOrchestrator (line 31) | async function getOrchestrator() {
  function makeTempDir (line 38) | function makeTempDir(): string {
  function readJson (line 43) | function readJson<T>(filePath: string): T {
  type ManifestModelEntry (line 49) | interface ManifestModelEntry {
  type TeamManifest (line 54) | interface TeamManifest {
  type ModelStatus (line 60) | interface ModelStatus {
  type TeamStatus (line 68) | interface TeamStatus {
  function getShuffle (line 457) | async function getShuffle() {
  function getBuilder (line 487) | async function getBuilder() {
  function getAggregate (line 527) | async function getAggregate() {
  function getParser (line 726) | async function getParser() {
  function writeResponse (line 731) | function writeResponse(filename: string, content: string) {
  function makeVoteBlock (line 735) | function makeVoteBlock(

FILE: packages/cli/src/team-orchestrator.ts
  type TeamManifest (line 14) | interface TeamManifest {
  type ModelStatus (line 20) | interface ModelStatus {
  type TeamStatus (line 28) | interface TeamStatus {
  type TeamRunOptions (line 33) | interface TeamRunOptions {
  type TeamJudgeOptions (line 39) | interface TeamJudgeOptions {
  type VoteResult (line 44) | interface VoteResult {
  type TeamVerdict (line 53) | interface TeamVerdict {
  function validateSessionPath (line 73) | function validateSessionPath(sessionPath: string): string {
  constant SENTINEL_MODELS (line 88) | const SENTINEL_MODELS = new Set([
  function isSentinelModel (line 100) | function isSentinelModel(model: string): boolean {
  function setupSession (line 113) | function setupSession(sessionPath: string, models: string[], input?: str...
  function runModels (line 197) | async function runModels(
  function judgeResponses (line 362) | async function judgeResponses(
  function getStatus (line 411) | function getStatus(sessionPath: string): TeamStatus {
  function fisherYatesShuffle (line 417) | function fisherYatesShuffle<T>(arr: T[]): T[] {
  function getDefaultJudgeModels (line 425) | function getDefaultJudgeModels(sessionPath: string): string[] {
  function buildJudgePrompt (line 432) | function buildJudgePrompt(input: string, responses: Record<string, strin...
  function parseJudgeVotes (line 462) | function parseJudgeVotes(judgePath: string, responseIds: string[]): Vote...
  function aggregateVerdict (line 513) | function aggregateVerdict(votes: VoteResult[], responseIds: string[]): T...
  function formatVerdict (line 538) | function formatVerdict(verdict: TeamVerdict, sessionPath: string): string {

FILE: packages/cli/src/team-timeout-repro.test.ts
  function makeFakeClaudish (line 38) | function makeFakeClaudish(delayMs: number = 50): string {

FILE: packages/cli/src/telemetry.test.ts
  constant CONFIG_PATH (line 15) | const CONFIG_PATH = join(homedir(), ".claudish", "config.json");
  constant BACKUP_PATH (line 16) | const BACKUP_PATH = join(homedir(), ".claudish", "config.json.telemetry-...
  function backupConfig (line 18) | function backupConfig() {
  function restoreConfig (line 25) | function restoreConfig() {

FILE: packages/cli/src/telemetry.ts
  constant TELEMETRY_ENDPOINT (line 24) | const TELEMETRY_ENDPOINT = "https://claudish.com/v1/report";
  constant MAX_REPORT_BYTES (line 27) | const MAX_REPORT_BYTES = 4096;
  constant KNOWN_PUBLIC_HOSTS (line 34) | const KNOWN_PUBLIC_HOSTS = new Set([
  constant PUBLIC_PROVIDERS (line 48) | const PUBLIC_PROVIDERS = new Set([
  type TelemetryConsent (line 96) | interface TelemetryConsent {
  type ErrorContext (line 116) | interface ErrorContext {
  type TelemetryReport (line 147) | interface TelemetryReport {
  function getVersion (line 189) | function getVersion(): string {
  function detectRuntime (line 199) | function detectRuntime(): string {
  function detectInstallMethod (line 211) | function detectInstallMethod(): string {
  function sanitizeMessage (line 245) | function sanitizeMessage(msg: string): string {
  function sanitizeModelId (line 316) | function sanitizeModelId(modelId: string, providerName: string): string {
  function classifyError (line 335) | function classifyError(
  function buildReport (line 413) | function buildReport(ctx: ErrorContext): TelemetryReport {
  function enforceReportSize (line 473) | function enforceReportSize(report: TelemetryReport): string | null {
  function sendReport (line 495) | async function sendReport(report: TelemetryReport): Promise<void> {
  function showConsentPromptAsync (line 525) | function showConsentPromptAsync(ctx: ErrorContext): void {
  function runConsentPrompt (line 550) | async function runConsentPrompt(ctx: ErrorContext): Promise<void> {
  function initTelemetry (line 620) | function initTelemetry(config: ClaudishConfig): void {
  function setClaudeCodeRunning (line 653) | function setClaudeCodeRunning(running: boolean): void {
  function reportError (line 666) | function reportError(ctx: ErrorContext): void {
  function handleTelemetryCommand (line 709) | async function handleTelemetryCommand(subcommand: string): Promise<void> {

FILE: packages/cli/src/test-fixtures/extract-sse-from-log.ts
  type Turn (line 56) | interface Turn {

FILE: packages/cli/src/transform.ts
  constant DROP_KEYS (line 8) | const DROP_KEYS = [
  type DroppedParams (line 29) | interface DroppedParams {
  function sanitizeRoot (line 36) | function sanitizeRoot(req: any): DroppedParams {
  function mapTools (line 71) | function mapTools(req: any): void {
  function mapToolChoice (line 107) | function mapToolChoice(req: any): void {
  function extractTextContent (line 145) | function extractTextContent(content: any): string {
  function transformMessages (line 183) | function transformMessages(req: any): void {
  function removeUriFormat (line 311) | function removeUriFormat(schema: any): any {
  function transformOpenAIToClaude (line 349) | function transformOpenAIToClaude(claudeRequestInput: any): {

FILE: packages/cli/src/tui/App.tsx
  constant VERSION (line 21) | const VERSION = "v5.16";
  constant COMMON_MODELS (line 24) | const COMMON_MODELS = [
  constant PROVIDER_PREFIXES (line 45) | const PROVIDER_PREFIXES = PROVIDERS.map((p) => ({
  type Tab (line 51) | type Tab = "providers" | "profiles" | "routing" | "privacy";
  type Mode (line 52) | type Mode =
  type ProbeMode (line 66) | type ProbeMode = "idle" | "input" | "running" | "done";
  type ProbeEntry (line 68) | interface ProbeEntry {
  function bytesHuman (line 78) | function bytesHuman(b: number): string {
  function App (line 84) | function App() {

FILE: packages/cli/src/tui/index.tsx
  function startConfigTui (line 6) | async function startConfigTui(): Promise<void> {

FILE: packages/cli/src/tui/providers.ts
  type ProviderDef (line 8) | interface ProviderDef {
  constant SKIP (line 20) | const SKIP = new Set(["qwen", "native-anthropic"]);
  function toProviderDef (line 22) | function toProviderDef(def: ProviderDefinition): ProviderDef {
  constant PROVIDERS (line 35) | const PROVIDERS: ProviderDef[] = getAllProviders()
  function maskKey (line 42) | function maskKey(key: string | undefined): string {

FILE: packages/cli/src/tui/test-provider.ts
  type TestResult (line 16) | type TestResult =
  constant TIMEOUT_MS (line 24) | const TIMEOUT_MS = 10_000;
  function resolveBaseUrl (line 29) | function resolveBaseUrl(def: ProviderDefinition): string {
  type ApiFamily (line 42) | type ApiFamily = "openai" | "anthropic" | "gemini" | "ollamacloud" | "un...
  function getApiFamily (line 44) | function getApiFamily(def: ProviderDefinition): ApiFamily {
  function testOpenAI (line 66) | async function testOpenAI(baseUrl: string, apiKey: string): Promise<Test...
  function testAnthropic (line 89) | async function testAnthropic(
  function testGemini (line 126) | async function testGemini(baseUrl: string, apiKey: string): Promise<Test...
  function testOllamaCloud (line 142) | async function testOllamaCloud(baseUrl: string, apiKey: string): Promise...
  function testProviderKey (line 165) | async function testProviderKey(providerName: string, apiKey: string): Pr...

FILE: packages/cli/src/types.ts
  type OpenRouterModel (line 4) | type OpenRouterModel = string;
  type ClaudishConfig (line 7) | interface ClaudishConfig {
  type AnthropicMessage (line 59) | interface AnthropicMessage {
  type ContentBlock (line 64) | interface ContentBlock {
  type AnthropicRequest (line 74) | interface AnthropicRequest {
  type AnthropicResponse (line 84) | interface AnthropicResponse {
  type OpenRouterMessage (line 98) | interface OpenRouterMessage {
  type OpenRouterRequest (line 103) | interface OpenRouterRequest {
  type OpenRouterResponse (line 112) | interface OpenRouterResponse {
  type ProxyServer (line 130) | interface ProxyServer {
  type ModelHandler (line 137) | interface ModelHandler {
  type RequestContext (line 142) | interface RequestContext {
  type StreamChunkContext (line 148) | interface StreamChunkContext {
  type NonStreamingResponseContext (line 155) | interface NonStreamingResponseContext {
  type ModelMiddleware (line 160) | interface ModelMiddleware {
  type IssueSeverity (line 175) | type IssueSeverity = "error" | "warning" | "info";
  type ValidationIssue (line 177) | interface ValidationIssue {
  type ValidationReport (line 185) | interface ValidationReport {

FILE: packages/cli/src/update-checker.ts
  constant NPM_REGISTRY_URL (line 15) | const NPM_REGISTRY_URL = "https://registry.npmjs.org/claudish/latest";
  constant CACHE_MAX_AGE_MS (line 17) | const CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
  constant RESET (line 20) | const RESET = "\x1b[0m";
  constant BOLD (line 21) | const BOLD = "\x1b[1m";
  constant GREEN (line 22) | const GREEN = "\x1b[32m";
  constant CYAN (line 23) | const CYAN = "\x1b[36m";
  constant DIM (line 24) | const DIM = "\x1b[2m";
  type UpdateCache (line 26) | interface UpdateCache {
  function getCacheFilePath (line 37) | function getCacheFilePath(): string {
  function readCache (line 63) | function readCache(): UpdateCache | null {
  function writeCache (line 79) | function writeCache(latestVersion: string | null): void {
  function isCacheValid (line 95) | function isCacheValid(cache: UpdateCache): boolean {
  function clearCache (line 103) | function clearCache(): void {
  function compareVersions (line 118) | function compareVersions(v1: string, v2: string): number {
  function fetchLatestVersion (line 134) | async function fetchLatestVersion(): Promise<string | null> {
  function checkForUpdates (line 167) | async function checkForUpdates(

FILE: packages/cli/src/update-command.ts
  constant RESET (line 17) | const RESET = "\x1b[0m";
  constant BOLD (line 18) | const BOLD = "\x1b[1m";
  constant GREEN (line 19) | const GREEN = "\x1b[32m";
  constant YELLOW (line 20) | const YELLOW = "\x1b[33m";
  constant CYAN (line 21) | const CYAN = "\x1b[36m";
  constant RED (line 22) | const RED = "\x1b[31m";
  constant MAGENTA (line 23) | const MAGENTA = "\x1b[35m";
  constant DIM (line 24) | const DIM = "\x1b[2m";
  type InstallationInfo (line 26) | interface InstallationInfo {
  type GitHubRelease (line 31) | interface GitHubRelease {
  type ChangelogItem (line 37) | interface ChangelogItem {
  type ChangelogEntry (line 42) | interface ChangelogEntry {
  function detectInstallationMethod (line 51) | function detectInstallationMethod(): InstallationInfo {
  function getUpdateCommand (line 80) | function getUpdateCommand(method: InstallationInfo["method"]): string {
  function executeUpdate (line 96) | async function executeUpdate(command: string): Promise<boolean> {
  constant SECTION_TYPE_MAP (line 113) | const SECTION_TYPE_MAP: Record<string, ChangelogItem["type"] | null> = {
  function parseRelease (line 130) | function parseRelease(r: GitHubRelease): ChangelogEntry {
  function fetchChangelog (line 189) | async function fetchChangelog(
  function itemStyle (line 239) | function itemStyle(type: ChangelogItem["type"]): { symbol: string; color...
  function displayChangelog (line 257) | function displayChangelog(entries: ChangelogEntry[]): void {
  function printManualInstructions (line 299) | function printManualInstructions(): void {
  function updateCommand (line 310) | async function updateCommand(): Promise<void> {

FILE: packages/cli/src/utils.ts
  function fuzzyScore (line 6) | function fuzzyScore(text: string, query: string): number {
  function formatCurrency (line 64) | function formatCurrency(amount: number): string {

FILE: packages/cli/src/version.ts
  constant VERSION (line 2) | const VERSION = "7.0.3";

FILE: packages/cli/src/zai-glm.e2e.test.ts
  constant HAVE_ZAI (line 25) | const HAVE_ZAI = !!process.env.ZAI_API_KEY;
  constant HAVE_GC (line 26) | const HAVE_GC = !!process.env.GLM_CODING_API_KEY || !!process.env.ZAI_CO...
  constant HAVE_GLM (line 27) | const HAVE_GLM = !!process.env.ZHIPU_API_KEY || !!process.env.GLM_API_KEY;
  constant TEST_PROMPT (line 29) | const TEST_PROMPT = "Reply with exactly the word: ok";
  constant TEST_MODEL (line 30) | const TEST_MODEL = "glm-4.6";
  constant TEST_TIMEOUT (line 33) | const TEST_TIMEOUT = 60_000;

FILE: packages/macos-bridge/scripts/full-test.js
  constant BRIDGE_DIR (line 14) | const BRIDGE_DIR = new URL("..", import.meta.url).pathname;
  function runAppleScript (line 16) | function runAppleScript(script) {
  function main (line 24) | async function main() {

FILE: packages/macos-bridge/scripts/simple-test.js
  constant BRIDGE_DIR (line 9) | const BRIDGE_DIR = new URL("..", import.meta.url).pathname;
  function main (line 11) | async function main() {

FILE: packages/macos-bridge/scripts/test-cycletls.ts
  function testClaudeBootstrap (line 9) | async function testClaudeBootstrap() {

FILE: packages/macos-bridge/src/auth.ts
  class AuthManager (line 14) | class AuthManager {
    method constructor (line 18) | constructor() {
    method generateToken (line 27) | private generateToken(): string {
    method hashToken (line 35) | private hashToken(token: string): string {
    method getToken (line 43) | getToken(): string {
    method validateToken (line 50) | validateToken(providedToken: string): boolean {
    method middleware (line 61) | middleware() {
    method getMaskedToken (line 93) | getMaskedToken(): string {

FILE: packages/macos-bridge/src/bridge.test.ts
  constant BASE_URL (line 14) | const BASE_URL = "http://127.0.0.1";

FILE: packages/macos-bridge/src/certificate-manager.ts
  type CertKeyPair (line 6) | interface CertKeyPair {
  constant MAX_LEAF_CERT_CACHE_SIZE (line 12) | const MAX_LEAF_CERT_CACHE_SIZE = 100;
  class CertificateManager (line 23) | class CertificateManager {
    method constructor (line 29) | constructor(certDir: string) {
    method initialize (line 36) | async initialize(): Promise<void> {
    method getCACertPEM (line 85) | getCACertPEM(): string {
    method getCACertFingerprint (line 95) | getCACertFingerprint(): string {
    method getCertForDomain (line 109) | async getCertForDomain(domain: string): Promise<CertKeyPair> {
    method preGenerateCerts (line 139) | async preGenerateCerts(domains: string[]): Promise<void> {
    method hasCA (line 146) | hasCA(): boolean {
    method getCAMetadata (line 153) | getCAMetadata(): { fingerprint: string; validFrom: Date; validTo: Date...
    method getLeafCertCount (line 167) | getLeafCertCount(): number {
    method getCertDir (line 174) | getCertDir(): string {
    method generateCA (line 181) | private async generateCA(): Promise<void> {
    method saveCA (line 229) | private async saveCA(certPath: string, keyPath: string): Promise<void> {
    method generateLeafCert (line 251) | private async generateLeafCert(domain: string): Promise<CertKeyPair> {
    method fileExists (line 320) | private async fileExists(filePath: string): Promise<boolean> {

FILE: packages/macos-bridge/src/config-manager.ts
  function createDefaultConfig (line 12) | function createDefaultConfig(): BridgeConfig {
  class ConfigManager (line 34) | class ConfigManager {
    method constructor (line 37) | constructor() {
    method getConfig (line 44) | getConfig(): BridgeConfig {
    method updateConfig (line 51) | updateConfig(updates: Partial<BridgeConfig>): BridgeConfig {
    method setConfig (line 87) | setConfig(config: BridgeConfig): void {
    method getMappingForApp (line 94) | getMappingForApp(appName: string): AppModelMapping | undefined {
    method setMappingForApp (line 101) | setMappingForApp(appName: string, mapping: AppModelMapping): void {
    method removeMappingForApp (line 108) | removeMappingForApp(appName: string): void {
    method getModelMapping (line 116) | getModelMapping(appName: string, originalModel: string): string | unde...
    method setModelMapping (line 127) | setModelMapping(appName: string, originalModel: string, targetModel: s...
    method removeModelMapping (line 140) | removeModelMapping(appName: string, originalModel: string): void {
    method isEnabled (line 149) | isEnabled(): boolean {
    method setEnabled (line 156) | setEnabled(enabled: boolean): void {
    method getConfiguredApps (line 163) | getConfiguredApps(): string[] {
    method exportConfig (line 170) | exportConfig(): string {
    method importConfig (line 177) | importConfig(jsonString: string): void {

FILE: packages/macos-bridge/src/connect-handler.ts
  type TrafficEntry (line 14) | interface TrafficEntry {
  type TrafficCallback (line 30) | type TrafficCallback = (entry: TrafficEntry) => void;
  type ModelTracker (line 35) | interface ModelTracker {
  type CapturedAuth (line 47) | interface CapturedAuth {
  type RoutingConfig (line 59) | interface RoutingConfig {
  class CONNECTHandler (line 77) | class CONNECTHandler {
    method constructor (line 137) | constructor(
    method setCycleTLSManager (line 152) | setCycleTLSManager(manager: CycleTLSManager): void {
    method setApiKeys (line 159) | setApiKeys(apiKeys: ApiKeys): void {
    method getModelTracker (line 166) | getModelTracker(): ModelTracker {
    method getConversationModel (line 173) | getConversationModel(conversationId: string): string | null {
    method getConversationModels (line 180) | getConversationModels(): Record<string, string> {
    method getCapturedAuth (line 191) | getCapturedAuth(): CapturedAuth {
    method hasAuth (line 198) | hasAuth(): boolean {
    method setRoutingConfig (line 207) | setRoutingConfig(config: RoutingConfig): void {
    method getRoutingConfig (line 218) | getRoutingConfig(): RoutingConfig {
    method getLogs (line 225) | getLogs(): LogEntry[] {
    method clearLogs (line 232) | clearLogs(): void {
    method getRoutingTarget (line 240) | getRoutingTarget(model: string): string | null {
    method shouldRouteConversation (line 250) | shouldRouteConversation(conversationId: string): {
    method fetchConversations (line 270) | async fetchConversations(): Promise<Array<{ uuid: string; model: strin...
    method handle (line 330) | handle(req: http.IncomingMessage, clientSocket: net.Socket, head: Buff...
    method parseConnectRequest (line 359) | private parseConnectRequest(req: http.IncomingMessage): {
    method upgradeTLS (line 385) | private async upgradeTLS(
    method captureAuthFromRequest (line 461) | private captureAuthFromRequest(data: Buffer | string, path: string): v...
    method extractModelFromPath (line 517) | private extractModelFromPath(path: string): string | null {
    method extractConversationFromPath (line 526) | private extractConversationFromPath(path: string): string | null {
    method trackModelUsage (line 534) | private trackModelUsage(
    method parseResponseLine (line 586) | private parseResponseLine(data: Buffer | string): {
    method shouldCaptureResponse (line 620) | private shouldCaptureResponse(path: string): boolean {
    method decompressBody (line 628) | private async decompressBody(data: Buffer, encoding: string): Promise<...
    method transformToAnthropicFormat (line 650) | transformToAnthropicFormat(
    method shouldRouteRequest (line 727) | shouldRouteRequest(
    method forwardStreamingRequest (line 786) | private async forwardStreamingRequest(
    method forwardViaNativeTLS (line 858) | private async forwardViaNativeTLS(
    method forwardWithMessageInjection (line 949) | private async forwardWithMessageInjection(
    method buildHTTPResponse (line 1116) | private buildHTTPResponse(
    method forwardViaCycleTLS (line 1140) | private async forwardViaCycleTLS(
    method getStatusText (line 1252) | private getStatusText(statusCode: number): string {
    method handleDecryptedHTTP (line 1280) | private handleDecryptedHTTP(tlsSocket: tls.TLSSocket, hostname?: strin...
    method analyzeResponse (line 1603) | private async analyzeResponse(responseBuffer: Buffer[], contentEncodin...
    method handleInterceptedRequest (line 1666) | private async handleInterceptedRequest(
    method streamErrorAsResponse (line 1731) | private streamErrorAsResponse(
    method saveCompletionRequestDebug (line 1828) | private saveCompletionRequestDebug(
    method callProviderAPI (line 1847) | private async callProviderAPI(targetModel: string, anthropicRequest: u...
    method streamTransformedResponse (line 1936) | private async streamTransformedResponse(
    method writeErrorResponse (line 2314) | private writeErrorResponse(tlsSocket: tls.TLSSocket, err: unknown): vo...
    method respondError (line 2336) | private respondError(socket: net.Socket, message: string): void {

FILE: packages/macos-bridge/src/cycletls-manager.ts
  type CycleTLSClient (line 10) | type CycleTLSClient = Awaited<ReturnType<typeof initCycleTLS>>;
  type RequestOptions (line 12) | interface RequestOptions {
  type Response (line 18) | interface Response {
  class CycleTLSManager (line 24) | class CycleTLSManager {
    method initialize (line 39) | async initialize(): Promise<void> {
    method request (line 59) | async request(url: string, options: RequestOptions): Promise<Response> {
    method shutdown (line 201) | async shutdown(): Promise<void> {
    method isInitialized (line 215) | isInitialized(): boolean {
    method getStats (line 222) | getStats(): { requestCount: number; errorCount: number } {

FILE: packages/macos-bridge/src/detection.ts
  type AppPattern (line 13) | interface AppPattern {
  constant KNOWN_APPS (line 21) | const KNOWN_APPS: AppPattern[] = [
  function extractPlatform (line 78) | function extractPlatform(userAgent: string): string | undefined {
  function detectUserAgent (line 97) | function detectUserAgent(userAgent: string): UserAgentDetection {
  function isClaudeDesktop (line 165) | function isClaudeDesktop(userAgent: string): boolean {
  function getClaudeDesktopVersion (line 172) | function getClaudeDesktopVersion(userAgent: string): string | undefined {
  type RequestHeaders (line 180) | interface RequestHeaders {
  function detectFromHeaders (line 194) | function detectFromHeaders(headers: RequestHeaders): UserAgentDetection {

FILE: packages/macos-bridge/src/http-parser.ts
  type ParsedHTTPRequest (line 11) | interface ParsedHTTPRequest {
  class HTTPRequestParser (line 23) | class HTTPRequestParser {
    method feed (line 36) | feed(chunk: Buffer): void {
    method tryParseHeaders (line 53) | private tryParseHeaders(): void {
    method isComplete (line 119) | isComplete(): boolean {
    method parse (line 154) | parse(): ParsedHTTPRequest | null {
    method decodeChunkedBody (line 190) | private decodeChunkedBody(chunkedData: Buffer): Buffer {
    method getState (line 221) | getState(): { method: string | null; contentLength: number | null; bod...
    method reset (line 233) | reset(): void {
    method getHeaders (line 247) | getHeaders(): Record<string, string> {
    method getRequestLine (line 254) | getRequestLine(): { method: string; path: string; httpVersion: string ...

FILE: packages/macos-bridge/src/https-proxy-server.ts
  constant MAX_CONTEXT_CACHE_SIZE (line 8) | const MAX_CONTEXT_CACHE_SIZE = 100;
  type HTTPSProxyServerOptions (line 10) | interface HTTPSProxyServerOptions {
  type ConnectHandler (line 16) | type ConnectHandler = (
  class HTTPSProxyServer (line 22) | class HTTPSProxyServer {
    method constructor (line 31) | constructor(
    method setConnectHandler (line 42) | setConnectHandler(handler: ConnectHandler): void {
    method start (line 51) | async start(port = 0): Promise<number> {
    method stop (line 119) | async stop(): Promise<void> {
    method getPort (line 143) | getPort(): number {
    method getServer (line 150) | getServer(): https.Server | null {
    method handleSNI (line 157) | private async handleSNI(

FILE: packages/macos-bridge/src/index.ts
  function main (line 22) | async function main() {

FILE: packages/macos-bridge/src/process-manager.ts
  class ProcessManager (line 20) | class ProcessManager {
    method constructor (line 25) | constructor(dataDir?: string) {
    method acquire (line 40) | async acquire(): Promise<boolean> {
    method updatePidFile (line 110) | async updatePidFile(port: number): Promise<void> {
    method release (line 133) | async release(): Promise<void> {
    method isLocked (line 154) | isLocked(): boolean {
    method findZombies (line 170) | async findZombies(): Promise<ProcessInfo[]> {
    method cleanupZombies (line 206) | async cleanupZombies(): Promise<number> {
    method getProcessInfo (line 248) | private async getProcessInfo(pid: number): Promise<ProcessInfo | null> {
    method parseProcessLine (line 275) | private parseProcessLine(line: string): ProcessInfo | null {
    method isClaudishBridge (line 310) | private isClaudishBridge(command: string): boolean {
    method isProcessAlive (line 321) | private isProcessAlive(pid: number): boolean {
    method killProcess (line 335) | private async killProcess(pid: number, signal: string): Promise<boolea...
    method waitForProcessExit (line 360) | private async waitForProcessExit(pid: number, timeout: number): Promis...
    method findPortOwner (line 377) | async findPortOwner(port: number): Promise<number | null> {
    method isPortInUse (line 391) | async isPortInUse(port: number): Promise<boolean> {
    method validatePort (line 399) | async validatePort(port: number): Promise<boolean> {
    method healthCheck (line 424) | async healthCheck(): Promise<boolean> {
    method readPidFile (line 455) | private readPidFile(): PidFileData | null {
    method recoverFromCrash (line 480) | async recoverFromCrash(): Promise<{ recovered: boolean; message: strin...

FILE: packages/macos-bridge/src/routing-middleware.ts
  type RoutingContext (line 34) | interface RoutingContext {
  type Handler (line 45) | interface Handler {
  class RoutingMiddleware (line 53) | class RoutingMiddleware {
    method constructor (line 59) | constructor(
    method createHandlerForModel (line 70) | private createHandlerForModel(model: string): Handler {
    method getHandlerForModel (line 161) | private getHandlerForModel(model: string): Handler {
    method resolveTargetModel (line 174) | private resolveTargetModel(appName: string, requestedModel: string): s...
    method updateDetectedApp (line 199) | private updateDetectedApp(name: string, confidence: number, userAgent:...
    method computeCost (line 221) | private computeCost(model: string, inputTokens: number, outputTokens: ...
    method logRequest (line 253) | private logRequest(
    method parseTokenUsage (line 286) | private parseTokenUsage(data: unknown): { inputTokens: number; outputT...
    method handleStreamingResponse (line 305) | private async handleStreamingResponse(
    method handle (line 367) | handle() {
    method getLogs (line 467) | getLogs(): LogEntry[] {
    method getDetectedApps (line 474) | getDetectedApps(): DetectedApp[] {
    method clearLogs (line 481) | clearLogs(): void {
    method shutdown (line 488) | async shutdown(): Promise<void> {

FILE: packages/macos-bridge/src/server.ts
  type BridgeStartResult (line 37) | interface BridgeStartResult {
  class BridgeServer (line 45) | class BridgeServer {
    method constructor (line 64) | constructor() {
    method setupRoutes (line 80) | private setupRoutes(): void {
    method writeDebugLog (line 914) | private writeDebugLog(entry: RawTrafficEntry, extra?: string): void {
    method forwardToRealServer (line 924) | private forwardToRealServer(
    method cleanupStaleLockFile (line 974) | private cleanupStaleLockFile(): void {
    method start (line 1014) | async start(port = 8899): Promise<BridgeStartResult> {
    method stop (line 1088) | async stop(): Promise<void> {
    method getToken (line 1136) | getToken(): string {

FILE: packages/macos-bridge/src/types.ts
  type ApiKeys (line 8) | interface ApiKeys {
  type AppModelMapping (line 21) | interface AppModelMapping {
  type BridgeConfig (line 33) | interface BridgeConfig {
  type BridgeStartOptions (line 45) | interface BridgeStartOptions {
  type DetectedApp (line 53) | interface DetectedApp {
  type ProxyStatus (line 64) | interface ProxyStatus {
  type ProxyEnableResponse (line 79) | interface ProxyEnableResponse {
  type LogEntry (line 89) | interface LogEntry {
  type RawTrafficEntry (line 105) | interface RawTrafficEntry {
  type LogFilter (line 121) | interface LogFilter {
  type LogResponse (line 131) | interface LogResponse {
  type HealthResponse (line 141) | interface HealthResponse {
  type UserAgentDetection (line 150) | interface UserAgentDetection {
  type ApiResponse (line 160) | interface ApiResponse<T = unknown> {
  type ProcessInfo (line 169) | interface ProcessInfo {
  type PidFileData (line 178) | interface PidFileData {

FILE: scripts/generate-manifest.ts
  type PlatformInfo (line 14) | interface PlatformInfo {
  type Manifest (line 19) | interface Manifest {
  constant PLATFORM_MAP (line 25) | const PLATFORM_MAP: Record<string, string> = {
  function computeSha256 (line 32) | function computeSha256(filePath: string): string {
  function generateManifest (line 37) | function generateManifest(version: string, releaseDir: string): Manifest {

FILE: scripts/update-models.ts
  constant MODELS_JSON_PATH (line 15) | const MODELS_JSON_PATH = join(import.meta.dir, "../packages/cli/recommen...
  constant TOP_WEEKLY_PROGRAMMING_MODELS (line 23) | const TOP_WEEKLY_PROGRAMMING_MODELS = [
  function updateModels (line 32) | async function updateModels(): Promise<void> {
Condensed preview — 352 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,940K chars).
[
  {
    "path": ".github/ISSUE_TRIAGE.md",
    "chars": 3314,
    "preview": "# Issue Triage Bot Setup\n\nThe Claudish project uses an automated issue triage bot powered by [Claude Code](https://githu"
  },
  {
    "path": ".github/prompts/issue-comment-system.md",
    "chars": 2908,
    "preview": "# Claudish Issue Comment Reply Agent\n\nYou are responding to a follow-up comment on a GitHub issue where you (claudish-bo"
  },
  {
    "path": ".github/prompts/issue-triage-system.md",
    "chars": 6521,
    "preview": "# Claudish Issue Triage Agent\n\nYou are triaging GitHub issues for the Claudish CLI tool.\n\n## Project Context\n\nClaudish ("
  },
  {
    "path": ".github/release.yml",
    "chars": 472,
    "preview": "changelog:\n  exclude:\n    labels:\n      - skip-changelog\n    authors:\n      - github-actions[bot]\n  categories:\n    - ti"
  },
  {
    "path": ".github/workflows/claude-code.yml",
    "chars": 1158,
    "preview": "name: Claude Code PR Assistant\n\non:\n  pull_request:\n    types: [opened, synchronize, reopened]\n  pull_request_review_com"
  },
  {
    "path": ".github/workflows/issue-triage.yml",
    "chars": 10184,
    "preview": "name: Issue Triage\n\non:\n  issues:\n    types: [opened]\n  issue_comment:\n    types: [created]\n  workflow_dispatch:\n    inp"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 13906,
    "preview": "name: Release\n\non:\n  push:\n    tags:\n      - 'v*'\n\npermissions:\n  contents: write\n  id-token: write  # Required for npm "
  },
  {
    "path": ".github/workflows/smoke-test.yml",
    "chars": 1442,
    "preview": "name: Smoke Tests\n\non:\n  schedule:\n    - cron: \"0 6 * * *\" # Daily at 06:00 UTC\n  workflow_dispatch: # Manual trigger\n\nj"
  },
  {
    "path": ".gitignore",
    "chars": 879,
    "preview": "# Dependencies\nnode_modules/\n\n# Build output\ndist/\nbuild/\n\n# Environment files\n.env\n.env.local\n.env.*.local\n\n# IDE\n.idea"
  },
  {
    "path": "AI_AGENT_GUIDE.md",
    "chars": 19577,
    "preview": "# Claudish AI Agent Usage Guide\n\n**Version:** 2.2.0\n**Target Audience:** AI Agents running within Claude Code\n**Purpose:"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 74459,
    "preview": "# Changelog\n\nAll notable changes to [Claudish](https://github.com/MadAppGang/claudish).\n\n## [7.0.3] - 2026-04-21\n\n### Bu"
  },
  {
    "path": "CLAUDE.md",
    "chars": 15419,
    "preview": "# Claudish - Development Notes\n\n## Release Process\n\n**Releases are handled by CI/CD** - do NOT manually run `npm publish"
  },
  {
    "path": "README.md",
    "chars": 50565,
    "preview": "<div align=\"center\">\n\n# 🔮 Claudish\n\n### Claude Code. Any Model.\n\n[![npm version](https://img.shields.io/npm/v/claudish.s"
  },
  {
    "path": "apps/.gitignore",
    "chars": 83,
    "preview": "# Swift build artifacts\n.build/\n.swiftpm/\n*.xcodeproj/\n*.xcworkspace/\nDerivedData/\n"
  },
  {
    "path": "apps/ClaudishProxy/Package.swift",
    "chars": 557,
    "preview": "// swift-tools-version: 5.9\n// The swift-tools-version declares the minimum version of Swift required to build this pack"
  },
  {
    "path": "apps/ClaudishProxy/Sources/ApiKeyManager.swift",
    "chars": 10392,
    "preview": "import Foundation\nimport Security\n\n/// Manages API keys with secure Keychain storage\n///\n/// Responsibilities:\n/// - Sto"
  },
  {
    "path": "apps/ClaudishProxy/Sources/BridgeManager.swift",
    "chars": 24428,
    "preview": "import Foundation\nimport Combine\n\n/// Manages the claudish-bridge Node.js process and HTTP communication\n///\n/// Respons"
  },
  {
    "path": "apps/ClaudishProxy/Sources/CertificateManager.swift",
    "chars": 10580,
    "preview": "import Foundation\nimport Security\n\n/// Manages certificate installation and keychain operations for HTTPS interception\n@"
  },
  {
    "path": "apps/ClaudishProxy/Sources/ClaudishProxyApp.swift",
    "chars": 30049,
    "preview": "import SwiftUI\nimport AppKit\n\n/// App version and metadata\nenum AppInfo {\n    static let version = \"1.0.0\"\n    static le"
  },
  {
    "path": "apps/ClaudishProxy/Sources/ModelProvider.swift",
    "chars": 11624,
    "preview": "import Foundation\nimport SwiftUI\n\n// MARK: - Model Types\n\n/// Provider category for models\nenum ModelProviderType: Strin"
  },
  {
    "path": "apps/ClaudishProxy/Sources/Models.swift",
    "chars": 13687,
    "preview": "import Foundation\n\n// MARK: - API Response Types\n\n/// Health check response from bridge\nstruct HealthResponse: Codable {"
  },
  {
    "path": "apps/ClaudishProxy/Sources/ProcessManager.swift",
    "chars": 9534,
    "preview": "import Foundation\nimport Combine\n\n/// Manages spawning and lifecycle of proxied Claude Desktop instances\n///\n/// Instead"
  },
  {
    "path": "apps/ClaudishProxy/Sources/ProfileManager.swift",
    "chars": 10284,
    "preview": "import Foundation\nimport SwiftUI\nimport Combine\n\n/// Manager for model profiles with storage and bridge integration\n@Mai"
  },
  {
    "path": "apps/ClaudishProxy/Sources/ProfilePicker.swift",
    "chars": 3728,
    "preview": "import SwiftUI\n\n/// Profile picker for menu bar dropdown\nstruct ProfilePicker: View {\n    @ObservedObject var profileMan"
  },
  {
    "path": "apps/ClaudishProxy/Sources/ProfilesSettingsView.swift",
    "chars": 33643,
    "preview": "import SwiftUI\nimport UniformTypeIdentifiers\n\n/// Wrapper for sheet binding - nil means new profile, non-nil means edit\n"
  },
  {
    "path": "apps/ClaudishProxy/Sources/SettingsView.swift",
    "chars": 39402,
    "preview": "import SwiftUI\n\n/// Settings window for configuring model mappings\nstruct SettingsView: View {\n    @ObservedObject var b"
  },
  {
    "path": "apps/ClaudishProxy/Sources/StatsDatabase.swift",
    "chars": 13637,
    "preview": "import Foundation\nimport SQLite3\n\n/// SQLite database manager for persistent stats storage\n/// Location: ~/Library/Appli"
  },
  {
    "path": "apps/ClaudishProxy/Sources/StatsPanel.swift",
    "chars": 9099,
    "preview": "import SwiftUI\n\n// MARK: - Components\n\nstruct DropdownSelector: View {\n    @Binding var selection: StatsManager.StatsPer"
  },
  {
    "path": "apps/ClaudishProxy/Sources/Theme.swift",
    "chars": 4509,
    "preview": "import SwiftUI\n\n/// Theme colors and styling constants for ClaudishProxy\n/// Based on the dark theme design from stats-p"
  },
  {
    "path": "apps/ClaudishProxy/Sources/UnifiedModelPicker.swift",
    "chars": 13984,
    "preview": "import SwiftUI\n\n/// Unified picker for profiles and models with search\nstruct UnifiedModelPicker: View {\n    @ObservedOb"
  },
  {
    "path": "biome.json",
    "chars": 882,
    "preview": "{\n  \"$schema\": \"https://biomejs.dev/schemas/1.9.4/schema.json\",\n  \"vcs\": {\n    \"enabled\": true,\n    \"clientKind\": \"git\","
  },
  {
    "path": "cliff.toml",
    "chars": 1550,
    "preview": "# git-cliff configuration for automatic changelog generation\n# https://git-cliff.org/docs/configuration\n\n[changelog]\nhea"
  },
  {
    "path": "design-references/stats-panel-style.md",
    "chars": 17143,
    "preview": "# Stats Panel Design Specification\n\n**Purpose**: Design reference for implementing credit usage and statistics panels in"
  },
  {
    "path": "docs/advanced/automation.md",
    "chars": 4887,
    "preview": "# Automation\n\n**Claudish in scripts, pipelines, and CI/CD.**\n\nSingle-shot mode makes Claudish perfect for automation. He"
  },
  {
    "path": "docs/advanced/cost-tracking.md",
    "chars": 3261,
    "preview": "# Cost Tracking\n\n**Know what you're spending. No surprises.**\n\nOpenRouter charges per token. Claudish can help you track"
  },
  {
    "path": "docs/advanced/environment.md",
    "chars": 4292,
    "preview": "# Environment Variables\n\n**Every knob you can turn. Complete reference.**\n\n---\n\n## Required\n\n### `OPENROUTER_API_KEY`\n\nY"
  },
  {
    "path": "docs/advanced/mtm-to-magmux-migration.md",
    "chars": 15131,
    "preview": "# Migrating from MTM to magmux\n\n**Version**: v6.5.0\n**Last updated**: 2026-04-01\n**Status**: Steps 1-3 complete. magmux "
  },
  {
    "path": "docs/ai-integration/for-agents.md",
    "chars": 5989,
    "preview": "# Claudish for AI Agents\n\n**How Claude Code sub-agents should use Claudish. Technical reference.**\n\nThis guide is for AI"
  },
  {
    "path": "docs/api-key-architecture.md",
    "chars": 6720,
    "preview": "# API Key Validation Architecture\n\nThis document describes the centralized API key validation system implemented in Clau"
  },
  {
    "path": "docs/api-reference.md",
    "chars": 38810,
    "preview": "# API Reference\n\nClaudish exposes a Firebase Cloud Functions HTTP API for model catalog data and telemetry, plus an MCP "
  },
  {
    "path": "docs/getting-started/quick-start.md",
    "chars": 4179,
    "preview": "# Quick Start Guide\n\n**From zero to running in 3 minutes. No fluff.**\n\n---\n\n## Prerequisites\n\nYou need two things:\n\n1. *"
  },
  {
    "path": "docs/index.md",
    "chars": 6634,
    "preview": "# Claudish Documentation\n\n**Run Claude Code with any AI model. Simple as that.**\n\nYou've got Claude Code. It's brilliant"
  },
  {
    "path": "docs/models/choosing-models.md",
    "chars": 5805,
    "preview": "# Choosing the Right Model\n\n**Different models, different strengths. Here's how to pick.**\n\nOpenRouter gives you access "
  },
  {
    "path": "docs/models/model-mapping.md",
    "chars": 5525,
    "preview": "# Model Mapping\n\n**Different models for different roles. Advanced optimization.**\n\nClaude Code uses different model \"tie"
  },
  {
    "path": "docs/settings-reference.md",
    "chars": 39136,
    "preview": "# Claudish Settings Reference\n\n**Session**: dev-research-claudish-settings-20260316-012741-6e25c3bb\n**Date**: 2026-03-16"
  },
  {
    "path": "docs/three-layer-architecture.md",
    "chars": 21420,
    "preview": "# Three-layer adapter architecture\n\n**Version**: v5.14.0+\n**Last updated**: 2026-03-22\n\nClaudish proxies Claude Code req"
  },
  {
    "path": "docs/troubleshooting.md",
    "chars": 7211,
    "preview": "# Troubleshooting\n\n**Something broken? Let's fix it.**\n\n---\n\n## Installation Issues\n\n### \"command not found: claudish\"\n\n"
  },
  {
    "path": "docs/usage/interactive-mode.md",
    "chars": 3636,
    "preview": "# Interactive Mode\n\n**The full Claude Code experience, different brain.**\n\nThis is how most people use Claudish. You pic"
  },
  {
    "path": "docs/usage/magmux.md",
    "chars": 6979,
    "preview": "# Magmux\n\n**A minimal terminal multiplexer for running AI models side by side.**\n\nMagmux splits your terminal into panes"
  },
  {
    "path": "docs/usage/mcp-server.md",
    "chars": 14401,
    "preview": "# MCP Server Mode\n\n**Use any claudish model as a tool inside Claude Code.**\n\nClaudish isn't just a CLI. It's also an MCP"
  },
  {
    "path": "docs/usage/monitor-mode.md",
    "chars": 3302,
    "preview": "# Monitor Mode\n\n**See exactly what Claude Code is doing under the hood.**\n\nMonitor mode is different. Instead of routing"
  },
  {
    "path": "docs/usage/single-shot-mode.md",
    "chars": 3814,
    "preview": "# Single-Shot Mode\n\n**One task. One result. Exit.**\n\nInteractive sessions are great for exploration. But sometimes you j"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/README.md",
    "chars": 9941,
    "preview": "# Tool Replacement via API Proxy — Claude Code Extension Technique\n\n**Status**: Active (Stage 2 PoC validated, Stage 2.1"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/claudish-patch/native-handler-advisor.test.ts",
    "chars": 8700,
    "preview": "import { afterEach, describe, expect, it } from \"bun:test\";\nimport {\n  _debug_getTrackedAdvisorIds,\n  _debug_resetTracke"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/claudish-patch/native-handler-advisor.ts",
    "chars": 11998,
    "preview": "/**\n * Advisor-tool transformer for NativeHandler (monitor mode).\n *\n * PURPOSE — experimental\n * ======================"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/claudish-patch/native-handler.patch",
    "chars": 7420,
    "preview": "diff --git a/packages/cli/src/handlers/native-handler.ts b/packages/cli/src/handlers/native-handler.ts\nindex 405c9ce..03"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/evidence/evidence-index.ndjson",
    "chars": 626,
    "preview": "{\"ts\":\"2026-04-14T11:52:21.848Z\",\"n\":3,\"method\":\"POST\",\"path\":\"/v1/messages\",\"hasAdvisor\":false,\"betaHeader\":\"interleave"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/evidence/evidence-req-advisor-enabled.json",
    "chars": 340513,
    "preview": "{\n  \"method\": \"POST\",\n  \"url\": \"http://127.0.0.1:8787/v1/messages?beta=true\",\n  \"pathname\": \"/v1/messages\",\n  \"headers\":"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/evidence/evidence-resp-advisor-enabled.ndjson",
    "chars": 160191,
    "preview": "{\"event\":\"message_start\",\"data\":{\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-6\",\"id\":\"msg_01Ez6qHzzSBGx1T"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/evidence/evidence-stage1-swap.ndjson",
    "chars": 439746,
    "preview": "{\"ts\":\"2026-04-15T02:24:14.469Z\",\"kind\":\"request_body\",\"swapApplied\":false,\"model\":\"claude-haiku-4-5-20251001\",\"body\":{\""
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/evidence/evidence-stage2-rewrite.ndjson",
    "chars": 440156,
    "preview": "{\"ts\":\"2026-04-15T06:32:18.882Z\",\"kind\":\"request_body\",\"swapApplied\":false,\"rewrittenIds\":[],\"model\":\"claude-haiku-4-5-2"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/evidence/evidence-stage2-ui-transcript.txt",
    "chars": 21206,
    "preview": "  Failure modes:\n\n  ┌──────────────┬─────────────────────────────────────┬─────────────────────────────────────┐\n  │   F"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/journal/2026-04-10-to-15-investigation.md",
    "chars": 6029,
    "preview": "# Investigation Journal: 2026-04-10 → 2026-04-15\n\n## Day 1 (April 10): Research Phase\n\n**Goal**: Understand Anthropic's "
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/poc/01-recording-proxy.ts",
    "chars": 8186,
    "preview": "#!/usr/bin/env bun\n/**\n * PoC Phase 1: Recording Proxy\n *\n * Minimal passthrough proxy that:\n *   1. Receives requests o"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/poc/02-mock-advisor-proxy.ts",
    "chars": 14767,
    "preview": "#!/usr/bin/env bun\n/**\n * PoC Phase 2: Mock Advisor Proxy\n *\n * This proxy does NOT forward to Anthropic. It fabricates "
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/poc/03-sdk-validation.ts",
    "chars": 4237,
    "preview": "#!/usr/bin/env bun\n/**\n * PoC Phase 2b: Validate mock proxy against the real Anthropic SDK\n *\n * This is the strongest v"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/poc/04-multi-turn-validation.ts",
    "chars": 4887,
    "preview": "#!/usr/bin/env bun\n/**\n * PoC Phase 2c: Multi-turn round-trip validation\n *\n * Per the Anthropic advisor docs, clients M"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/poc/05-tool-loop-proxy.ts",
    "chars": 20585,
    "preview": "#!/usr/bin/env bun\n/**\n * PoC Phase 3: Tool-Loop Advisor Replacement Proxy\n *\n * This is the real proof of concept for t"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/poc/06-sdk-e2e-validation.ts",
    "chars": 11939,
    "preview": "#!/usr/bin/env bun\n/**\n * PoC Phase 3b: End-to-end validation with the real Anthropic SDK\n *\n * The strongest validation"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/poc/README.md",
    "chars": 8250,
    "preview": "# Advisor-Replacement Proxy — Proof of Concept\n\nThis directory contains a working proof-of-concept validating that a pro"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/research/01-advisor-pattern-research.md",
    "chars": 23159,
    "preview": "# Research Report: Claude Advisor Tool Pattern + Claudish Integration\n\n**Session**: dev-research-advisor-tool-claudish-2"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/research/01-research-plan.md",
    "chars": 15330,
    "preview": "# Research Plan: Advisor Tool Pattern + Claudish Integration\n\n**Session:** dev-research-advisor-tool-claudish-20260410-1"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/research/02-proxy-replacement-architecture.md",
    "chars": 14602,
    "preview": "# Research Report: Transparent Advisor Tool Replacement via API Proxy\n\n**Session**: dev-research-advisor-proxy-replaceme"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/research/03-how-to-enable-advisor.md",
    "chars": 11314,
    "preview": "# How to Enable the Native Claude Code Advisor Tool\n\n**Validated 2026-04-14** with Claude Code 2.1.107, `claude-sonnet-4"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/research/04-real-test-results.md",
    "chars": 8269,
    "preview": "# Real-Claude-Code Test Results (2026-04-14)\n\n## TL;DR\n\nRan real Claude Code 2.1.107 through the recording proxy. Captur"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/research/05-stage1-tool-swap.md",
    "chars": 6927,
    "preview": "# Stage 1 Results — Advisor Tool Swap Validation\n\n**Date**: 2026-04-15\n**Claude Code**: v2.1.108\n**Executor model**: cla"
  },
  {
    "path": "experiments/tool-replacement-proxy-2026-04/research/06-stage2-tool-result-rewrite.md",
    "chars": 8237,
    "preview": "# Stage 2 Results — Approach 1 PoC Works End-to-End\n\n**Date**: 2026-04-15\n**Claude Code**: v2.1.109\n**Executor**: Opus 4"
  },
  {
    "path": "install.sh",
    "chars": 3939,
    "preview": "#!/bin/bash\n# claudish installer\n# Usage: curl -fsSL https://raw.githubusercontent.com/MadAppGang/claudish/main/install."
  },
  {
    "path": "landingpage/.firebaserc",
    "chars": 56,
    "preview": "{\n  \"projects\": {\n    \"default\": \"claudish-6da10\"\n  }\n}\n"
  },
  {
    "path": "landingpage/.gitignore",
    "chars": 1232,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase c"
  },
  {
    "path": "landingpage/App.tsx",
    "chars": 6081,
    "preview": "import React from \"react\";\nimport HeroSection from \"./components/HeroSection\";\nimport SubscriptionSection from \"./compon"
  },
  {
    "path": "landingpage/README.md",
    "chars": 529,
    "preview": "# Claudish Landing Page\n\nThe marketing site for [Claudish](https://github.com/MadAppGang/claudish) - run Claude Code wit"
  },
  {
    "path": "landingpage/components/BlockLogo.tsx",
    "chars": 3283,
    "preview": "import React from \"react\";\n\n// Grid definition: 1 = filled block, 0 = empty space\nconst LETTERS: Record<string, number[]"
  },
  {
    "path": "landingpage/components/BridgeDiagram.tsx",
    "chars": 7086,
    "preview": "import React, { useState, useEffect } from \"react\";\n\nexport const BridgeDiagram: React.FC = () => {\n  const [modelIndex,"
  },
  {
    "path": "landingpage/components/Changelog.tsx",
    "chars": 13860,
    "preview": "import React, { useEffect, useState } from \"react\";\n\ninterface GitHubRelease {\n  id: number;\n  tag_name: string;\n  name:"
  },
  {
    "path": "landingpage/components/FeatureSection.tsx",
    "chars": 23318,
    "preview": "import type React from \"react\";\nimport { useEffect, useState } from \"react\";\nimport { HIGHLIGHT_FEATURES, STANDARD_FEATU"
  },
  {
    "path": "landingpage/components/HeroSection.tsx",
    "chars": 16648,
    "preview": "import type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { HERO_SEQUENCE } from \"../c"
  },
  {
    "path": "landingpage/components/MultiModelAnimation.tsx",
    "chars": 15686,
    "preview": "import React, { useState, useEffect, useRef } from \"react\";\nimport { TerminalWindow } from \"./TerminalWindow\";\n\nexport c"
  },
  {
    "path": "landingpage/components/SmartRouting.tsx",
    "chars": 25585,
    "preview": "import React, { useState, useEffect, useRef } from \"react\";\nimport { TerminalWindow } from \"./TerminalWindow\";\nimport { "
  },
  {
    "path": "landingpage/components/SubscriptionSection.tsx",
    "chars": 7397,
    "preview": "import {\n  Bot,\n  Brain,\n  Check,\n  Cloud,\n  Zap as FastIcon,\n  HardDrive,\n  MessageSquareCode,\n  Moon,\n  ShieldCheck,\n "
  },
  {
    "path": "landingpage/components/SupportSection.tsx",
    "chars": 3767,
    "preview": "import React from \"react\";\n\nconst SupportSection: React.FC = () => {\n  return (\n    <section className=\"py-16 bg-[#08080"
  },
  {
    "path": "landingpage/components/TerminalWindow.tsx",
    "chars": 1340,
    "preview": "import React from \"react\";\n\ninterface TerminalWindowProps {\n  children: React.ReactNode;\n  className?: string;\n  title?:"
  },
  {
    "path": "landingpage/components/TypingAnimation.tsx",
    "chars": 936,
    "preview": "import React, { useState, useEffect } from \"react\";\n\ninterface TypingAnimationProps {\n  text: string;\n  speed?: number;\n"
  },
  {
    "path": "landingpage/components/VisionSection.tsx",
    "chars": 6669,
    "preview": "import type React from \"react\";\nimport { TerminalWindow } from \"./TerminalWindow\";\n\nexport const VisionSection: React.FC"
  },
  {
    "path": "landingpage/constants.ts",
    "chars": 5907,
    "preview": "import type { Feature, ModelCard, TerminalLine } from \"./types\";\n\nexport const HERO_SEQUENCE: TerminalLine[] = [\n  // 1."
  },
  {
    "path": "landingpage/firebase.json",
    "chars": 590,
    "preview": "{\n  \"hosting\": {\n    \"public\": \"dist\",\n    \"ignore\": [\"firebase.json\", \"**/.*\", \"**/node_modules/**\"],\n    \"rewrites\": ["
  },
  {
    "path": "landingpage/firebase.ts",
    "chars": 661,
    "preview": "import { initializeApp } from \"firebase/app\";\nimport { getAnalytics, isSupported } from \"firebase/analytics\";\n\nconst fir"
  },
  {
    "path": "landingpage/index.html",
    "chars": 9354,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "landingpage/index.tsx",
    "chars": 404,
    "preview": "import React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport App from \"./App\";\nimport \"./firebase\"; // Ini"
  },
  {
    "path": "landingpage/metadata.json",
    "chars": 157,
    "preview": "{\n  \"name\": \"Claudish\",\n  \"description\": \"A landing page for Claudish - the universal model wrapper for Claude Code CLI."
  },
  {
    "path": "landingpage/package.json",
    "chars": 534,
    "preview": "{\n  \"name\": \"claudish\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n "
  },
  {
    "path": "landingpage/pnpm-workspace.yaml",
    "chars": 71,
    "preview": "onlyBuiltDependencies:\n  - '@firebase/util'\n  - esbuild\n  - protobufjs\n"
  },
  {
    "path": "landingpage/public/site.webmanifest",
    "chars": 438,
    "preview": "{\n  \"name\": \"Claudish\",\n  \"short_name\": \"Claudish\",\n  \"icons\": [\n    {\n      \"src\": \"/web-app-manifest-192x192.png\",\n   "
  },
  {
    "path": "landingpage/tsconfig.json",
    "chars": 491,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"experimentalDecorators\": true,\n    \"useDefineForClassFields\": fals"
  },
  {
    "path": "landingpage/types.ts",
    "chars": 732,
    "preview": "export interface TerminalLine {\n  id: string;\n  type:\n    | \"input\"\n    | \"output\"\n    | \"success\"\n    | \"info\"\n    | \"a"
  },
  {
    "path": "landingpage/vite.config.ts",
    "chars": 549,
    "preview": "import path from \"path\";\nimport { defineConfig, loadEnv } from \"vite\";\nimport react from \"@vitejs/plugin-react\";\n\nexport"
  },
  {
    "path": "package.json",
    "chars": 1969,
    "preview": "{\n  \"name\": \"claudish-monorepo\",\n  \"version\": \"7.0.3\",\n  \"private\": true,\n  \"description\": \"Monorepo for Claudish - Run "
  },
  {
    "path": "packages/.gitignore",
    "chars": 40,
    "preview": "# Build outputs\n*/dist/\n*/node_modules/\n"
  },
  {
    "path": "packages/cli/.gitignore",
    "chars": 17,
    "preview": ".claudish-team-*\n"
  },
  {
    "path": "packages/cli/AI_AGENT_GUIDE.md",
    "chars": 19561,
    "preview": "# Claudish AI Agent Usage Guide\n\n**Version:** 7.0.0\n**Target Audience:** AI Agents running within Claude Code\n**Purpose:"
  },
  {
    "path": "packages/cli/bin/claudish.cjs",
    "chars": 1401,
    "preview": "#!/usr/bin/env node\n\n// Launcher script: checks for Bun runtime before starting claudish.\n// Claudish uses Bun-specific "
  },
  {
    "path": "packages/cli/package.json",
    "chars": 2214,
    "preview": "{\n  \"name\": \"claudish\",\n  \"version\": \"7.0.3\",\n  \"description\": \"Run Claude Code with any model - OpenRouter, Ollama, LM "
  },
  {
    "path": "packages/cli/recommended-models.json",
    "chars": 7265,
    "preview": "{\n  \"version\": \"1.2.0\",\n  \"lastUpdated\": \"2026-03-16\",\n  \"source\": \"https://openrouter.ai/models?categories=programming&"
  },
  {
    "path": "packages/cli/scripts/generate-version.ts",
    "chars": 615,
    "preview": "/**\n * Generate version.ts from package.json\n * Run before bundling so the version is baked into compiled binaries.\n */\n"
  },
  {
    "path": "packages/cli/scripts/smoke/probes.ts",
    "chars": 17647,
    "preview": "/**\n * Smoke test probe implementations.\n *\n * Three probes: tool calling, reasoning, vision.\n * Each returns a ProbeRes"
  },
  {
    "path": "packages/cli/scripts/smoke/providers.ts",
    "chars": 7095,
    "preview": "/**\n * Provider discovery for smoke tests.\n *\n * Imports from the main source tree to reuse base URLs, auth schemes,\n * "
  },
  {
    "path": "packages/cli/scripts/smoke/reporter.ts",
    "chars": 4508,
    "preview": "/**\n * Terminal table and JSON file output for smoke test results.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:f"
  },
  {
    "path": "packages/cli/scripts/smoke/types.ts",
    "chars": 2663,
    "preview": "/**\n * Smoke test types and interfaces\n */\n\n// Wire format classification\nexport type WireFormat = \"anthropic-compat\" | "
  },
  {
    "path": "packages/cli/scripts/smoke-test.ts",
    "chars": 7175,
    "preview": "#!/usr/bin/env bun\n/**\n * Claudish Smoke Test Suite\n *\n * Validates all available providers by running tool calling, rea"
  },
  {
    "path": "packages/cli/scripts/smoke.test.ts",
    "chars": 15719,
    "preview": "/**\n * Black-box unit tests for the claudish smoke test framework.\n *\n * Tests are based on expected behavior (requireme"
  },
  {
    "path": "packages/cli/skills/claudish-usage/SKILL.md",
    "chars": 35886,
    "preview": "---\nname: claudish-usage\ndescription: CRITICAL - Guide for using Claudish CLI ONLY through sub-agents to run Claude Code"
  },
  {
    "path": "packages/cli/src/adapters/anthropic-api-format.ts",
    "chars": 4547,
    "preview": "/**\n * AnthropicAPIFormat — Layer 1 wire format for Anthropic Messages API.\n *\n * Identity transform for providers that "
  },
  {
    "path": "packages/cli/src/adapters/api-format.ts",
    "chars": 1318,
    "preview": "/**\n * APIFormat — translates between Claude API format and target model's wire format.\n *\n * Each implementation repres"
  },
  {
    "path": "packages/cli/src/adapters/base-api-format.ts",
    "chars": 9230,
    "preview": "/**\n * Base class for API format implementations (Layer 1) and model dialect\n * implementations (Layer 2).\n *\n * Differe"
  },
  {
    "path": "packages/cli/src/adapters/codex-api-format.ts",
    "chars": 6487,
    "preview": "/**\n * CodexAPIFormat — Layer 1 wire format for the OpenAI Responses API (Codex models).\n *\n * The Codex Responses API i"
  },
  {
    "path": "packages/cli/src/adapters/deepseek-model-dialect.ts",
    "chars": 1434,
    "preview": "/**\n * DeepSeekModelDialect — Layer 2 dialect for DeepSeek models.\n *\n * Handles DeepSeek-specific quirks:\n * - Strips u"
  },
  {
    "path": "packages/cli/src/adapters/dialect-manager.ts",
    "chars": 2241,
    "preview": "/**\n * DialectManager — selects the appropriate Layer 2 ModelDialect for a given model.\n *\n * This allows ComposedHandle"
  },
  {
    "path": "packages/cli/src/adapters/gemini-api-format.ts",
    "chars": 15400,
    "preview": "/**\n * GeminiAPIFormat — Layer 1 wire format for Google Gemini generateContent API.\n *\n * Handles Gemini-specific transf"
  },
  {
    "path": "packages/cli/src/adapters/glm-model-dialect.ts",
    "chars": 1649,
    "preview": "/**\n * GLMModelDialect — Layer 2 dialect for Zhipu AI GLM models.\n *\n * Handles GLM-specific quirks:\n * - Context window"
  },
  {
    "path": "packages/cli/src/adapters/grok-model-dialect.ts",
    "chars": 4772,
    "preview": "/**\n * GrokModelDialect — Layer 2 dialect for xAI Grok models.\n *\n * Translates xAI XML function calls to Claude Code to"
  },
  {
    "path": "packages/cli/src/adapters/index.ts",
    "chars": 604,
    "preview": "/**\n * Model format and dialect implementations\n */\n\nexport { BaseAPIFormat, DefaultAPIFormat } from \"./base-api-format."
  },
  {
    "path": "packages/cli/src/adapters/litellm-api-format.ts",
    "chars": 5015,
    "preview": "/**\n * LiteLLMAPIFormat — Layer 1 wire format for LiteLLM proxy.\n *\n * Handles LiteLLM-specific model transforms:\n * - I"
  },
  {
    "path": "packages/cli/src/adapters/local-adapter.ts",
    "chars": 8975,
    "preview": "/**\n * LocalModelAdapter — adapter for local OpenAI-compatible providers.\n *\n * Wraps a model-specific adapter (Qwen, De"
  },
  {
    "path": "packages/cli/src/adapters/minimax-model-dialect.ts",
    "chars": 3097,
    "preview": "/**\n * MiniMaxModelDialect — Layer 2 dialect for MiniMax models.\n *\n * Handles MiniMax-specific quirks:\n * - Context win"
  },
  {
    "path": "packages/cli/src/adapters/model-catalog.test.ts",
    "chars": 7657,
    "preview": "/**\n * Tests for the centralized model-catalog.ts lookupModel() function.\n */\n\nimport { describe, test, expect } from \"b"
  },
  {
    "path": "packages/cli/src/adapters/model-catalog.ts",
    "chars": 5799,
    "preview": "/**\n * Centralized model metadata catalog.\n *\n * Eliminates scattered hardcoded model metadata across adapter files.\n * "
  },
  {
    "path": "packages/cli/src/adapters/model-dialect.ts",
    "chars": 1050,
    "preview": "/**\n * ModelDialect — translates model-specific dialect differences.\n *\n * Each model family has its own dialect: contex"
  },
  {
    "path": "packages/cli/src/adapters/ollama-api-format.ts",
    "chars": 3842,
    "preview": "/**\n * OllamaAPIFormat — Layer 1 wire format for OllamaCloud API.\n *\n * Converts Claude messages to OllamaCloud's simple"
  },
  {
    "path": "packages/cli/src/adapters/openai-api-format.ts",
    "chars": 4544,
    "preview": "/**\n * OpenAIAPIFormat — Layer 1 wire format for OpenAI Chat Completions API.\n *\n * Handles:\n * - Context window detecti"
  },
  {
    "path": "packages/cli/src/adapters/openrouter-api-format.ts",
    "chars": 6068,
    "preview": "/**\n * OpenRouterAPIFormat — Layer 1 wire format for OpenRouter API.\n *\n * Wraps a model-specific dialect (Grok, Gemini,"
  },
  {
    "path": "packages/cli/src/adapters/qwen-model-dialect.ts",
    "chars": 2573,
    "preview": "/**\n * QwenModelDialect — Layer 2 dialect for Qwen (Alibaba) models.\n *\n * Handles Qwen-specific quirks:\n * - Strips spe"
  },
  {
    "path": "packages/cli/src/adapters/tool-name-utils.ts",
    "chars": 1607,
    "preview": "/**\n * Tool name truncation utilities for model adapters\n *\n * Some model APIs (e.g., OpenAI) impose a maximum length on"
  },
  {
    "path": "packages/cli/src/adapters/xiaomi-model-dialect.ts",
    "chars": 1638,
    "preview": "/**\n * XiaomiModelDialect — Layer 2 dialect for Xiaomi (MiMo) models.\n *\n * Handles Xiaomi-specific quirks:\n * - 64-char"
  },
  {
    "path": "packages/cli/src/auth/auth-commands.ts",
    "chars": 3680,
    "preview": "/**\n * Unified login/logout subcommands for OAuth providers.\n *\n * Usage:\n *   claudish login [provider]   - Interactive"
  },
  {
    "path": "packages/cli/src/auth/codex-oauth.ts",
    "chars": 18394,
    "preview": "/**\n * OpenAI Codex OAuth Authentication Manager\n *\n * Handles OAuth2 PKCE flow for OpenAI Codex API access via ChatGPT "
  },
  {
    "path": "packages/cli/src/auth/gemini-oauth.ts",
    "chars": 25086,
    "preview": "/**\n * Gemini OAuth Authentication Manager\n *\n * Handles OAuth2 PKCE flow for Gemini Code Assist API access.\n * Supports"
  },
  {
    "path": "packages/cli/src/auth/kimi-oauth.ts",
    "chars": 19100,
    "preview": "/**\n * Kimi OAuth Authentication Manager\n *\n * Handles Device Authorization Grant (RFC 8628) for Kimi/Moonshot AI API ac"
  },
  {
    "path": "packages/cli/src/auth/oauth-manager.ts",
    "chars": 6255,
    "preview": "/**\n * OAuthManager — shared base class for all OAuth providers.\n *\n * Handles:\n * - Credential file I/O with 0600 permi"
  },
  {
    "path": "packages/cli/src/auth/oauth-registry.ts",
    "chars": 3582,
    "preview": "import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";"
  },
  {
    "path": "packages/cli/src/auth/quota-command.ts",
    "chars": 15483,
    "preview": "/**\n * Quota/usage subcommand for OAuth providers.\n *\n * Usage:\n *   claudish quota [provider]   - Show quota usage for "
  },
  {
    "path": "packages/cli/src/auth/vertex-auth.ts",
    "chars": 8056,
    "preview": "/**\n * Vertex AI OAuth Authentication Manager\n *\n * Handles OAuth2 token generation for full Vertex AI access.\n * Suppor"
  },
  {
    "path": "packages/cli/src/channel/e2e-channel.test.ts",
    "chars": 13979,
    "preview": "/**\n * E2E tests for channel mode using real Claude Code.\n *\n * Spawns `claude -p` with `--mcp-config` pointing at our M"
  },
  {
    "path": "packages/cli/src/channel/index.ts",
    "chars": 346,
    "preview": "export { ScrollbackBuffer } from \"./scrollback-buffer.js\";\nexport { SignalWatcher } from \"./signal-watcher.js\";\nexport {"
  },
  {
    "path": "packages/cli/src/channel/scrollback-buffer.test.ts",
    "chars": 2955,
    "preview": "import { describe, test, expect } from \"bun:test\";\nimport { ScrollbackBuffer } from \"./scrollback-buffer.js\";\n\ndescribe("
  },
  {
    "path": "packages/cli/src/channel/scrollback-buffer.ts",
    "chars": 2145,
    "preview": "// ─── ScrollbackBuffer ────────────────────────────────────────────────────────\n//\n// In-memory ring buffer for PTY out"
  },
  {
    "path": "packages/cli/src/channel/session-manager.test.ts",
    "chars": 15329,
    "preview": "/**\n * Unit tests for SessionManager.\n *\n * SessionManager normally spawns `claudish`, but here we intercept by\n * prepe"
  },
  {
    "path": "packages/cli/src/channel/session-manager.ts",
    "chars": 11326,
    "preview": "// ─── SessionManager ──────────────────────────────────────────────────────────\n//\n// Manages the lifecycle of channel "
  },
  {
    "path": "packages/cli/src/channel/signal-watcher.test.ts",
    "chars": 4518,
    "preview": "import { describe, test, expect, beforeEach, afterEach } from \"bun:test\";\nimport { SignalWatcher } from \"./signal-watche"
  },
  {
    "path": "packages/cli/src/channel/signal-watcher.ts",
    "chars": 5418,
    "preview": "// ─── SignalWatcher ────────────────────────────────────────────────────────────\n//\n// Per-session state machine that d"
  },
  {
    "path": "packages/cli/src/channel/test-helpers/fake-claudish.ts",
    "chars": 1787,
    "preview": "#!/usr/bin/env bun\n/**\n * Fake claudish binary for session-manager unit tests.\n *\n * Behavior is controlled via CLI flag"
  },
  {
    "path": "packages/cli/src/channel/types.ts",
    "chars": 1379,
    "preview": "// ─── Channel Mode Types ──────────────────────────────────────────────────────\n\nexport type SessionStatus =\n  | \"start"
  },
  {
    "path": "packages/cli/src/claude-runner.ts",
    "chars": 23904,
    "preview": "import type { ChildProcess } from \"node:child_process\";\nimport { spawn } from \"node:child_process\";\nimport { writeFileSy"
  },
  {
    "path": "packages/cli/src/cli-passthrough.test.ts",
    "chars": 18411,
    "preview": "/**\n * E2E tests for the flag passthrough feature.\n *\n * Validates the complete flow: parseArgs → arg-building logic (as"
  },
  {
    "path": "packages/cli/src/cli.test.ts",
    "chars": 11735,
    "preview": "/**\n * Black box tests for parseArgs() in cli.ts.\n *\n * Tests are derived solely from requirements and API contracts:\n *"
  },
  {
    "path": "packages/cli/src/cli.ts",
    "chars": 79003,
    "preview": "import { VERSION } from \"./version.js\";\nimport { ENV } from \"./config.js\";\nimport type { ClaudishConfig } from \"./types."
  },
  {
    "path": "packages/cli/src/config-command.ts",
    "chars": 24593,
    "preview": "/**\n * Claudish Config TUI\n *\n * Interactive configuration menu for claudish. Allows users to:\n *   - Set/remove API key"
  },
  {
    "path": "packages/cli/src/config-schema.test.ts",
    "chars": 4214,
    "preview": "import { describe, expect, test } from \"bun:test\";\nimport {\n  BuiltinDefaultProviderSchema,\n  CustomEndpointComplexSchem"
  },
  {
    "path": "packages/cli/src/config-schema.ts",
    "chars": 3035,
    "preview": "/**\n * Config schemas for the LiteLLM-demotion refactor (Phase 1).\n *\n * Defines:\n *   - BuiltinDefaultProviderSchema — "
  },
  {
    "path": "packages/cli/src/config.ts",
    "chars": 2492,
    "preview": "// Claudish configuration constants\n\nexport const DEFAULT_PORT_RANGE = { start: 3000, end: 9000 };\n\n// Environment varia"
  },
  {
    "path": "packages/cli/src/default-provider.test.ts",
    "chars": 4893,
    "preview": "import { describe, expect, test } from \"bun:test\";\nimport {\n  buildLegacyHint,\n  resolveDefaultProvider,\n  type Resolved"
  },
  {
    "path": "packages/cli/src/default-provider.ts",
    "chars": 3505,
    "preview": "/**\n * Pure resolver for the effective default provider used when a bare model name\n * is supplied without an explicit `"
  },
  {
    "path": "packages/cli/src/diag-output.ts",
    "chars": 3226,
    "preview": "import { createWriteStream, mkdirSync, writeFileSync, unlinkSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nim"
  },
  {
    "path": "packages/cli/src/format-translation.test.ts",
    "chars": 57967,
    "preview": "/**\n * Format Translation Integration Tests\n *\n * Tests the SSE stream parser pipeline by replaying real (or seed) SSE f"
  },
  {
    "path": "packages/cli/src/glm-adapter.test.ts",
    "chars": 8844,
    "preview": "/**\n * E2E tests for GLM dialect and three-layer adapter architecture.\n *\n * Validates:\n * 1. GLMModelDialect model dete"
  },
  {
    "path": "packages/cli/src/handlers/composed-handler.test.ts",
    "chars": 1988,
    "preview": "import { describe, expect, test } from \"bun:test\";\nimport type { ProviderTransport } from \"../providers/transport/types."
  },
  {
    "path": "packages/cli/src/handlers/composed-handler.ts",
    "chars": 37175,
    "preview": "/**\n * ComposedHandler — composes a ProviderTransport + ModelAdapter to implement ModelHandler.\n *\n * This is the univer"
  },
  {
    "path": "packages/cli/src/handlers/default-provider-e2e.test.ts",
    "chars": 30365,
    "preview": "/**\n * Phase 5 end-to-end tests for the LiteLLM-demotion refactor.\n *\n * Black-box tests. The proxy is invoked in-proces"
  },
  {
    "path": "packages/cli/src/handlers/fallback-handler.test.ts",
    "chars": 18984,
    "preview": "/**\n * E2E tests for the provider fallback mechanism.\n *\n * These tests use REAL API tokens and hit actual provider endp"
  },
  {
    "path": "packages/cli/src/handlers/fallback-handler.ts",
    "chars": 7829,
    "preview": "/**\n * FallbackHandler — tries multiple providers in priority order.\n *\n * When the primary provider fails with a retrya"
  },
  {
    "path": "packages/cli/src/handlers/native-handler-advisor.test.ts",
    "chars": 15863,
    "preview": "import { afterEach, describe, expect, it } from \"bun:test\";\nimport {\n  _debug_getTrackedAdvisorIds,\n  _debug_resetTracke"
  },
  {
    "path": "packages/cli/src/handlers/native-handler-advisor.ts",
    "chars": 22697,
    "preview": "/**\n * Advisor-tool transformer for NativeHandler (monitor mode).\n *\n * PURPOSE — experimental\n * ======================"
  },
  {
    "path": "packages/cli/src/handlers/native-handler.ts",
    "chars": 11234,
    "preview": "import type { Context } from \"hono\";\nimport type { ModelHandler } from \"./types.js\";\nimport { log, maskCredential } from"
  },
  {
    "path": "packages/cli/src/handlers/shared/anthropic-error.test.ts",
    "chars": 4809,
    "preview": "import { describe, it, expect } from \"bun:test\";\nimport {\n  statusToErrorType,\n  wrapAnthropicError,\n  ensureAnthropicEr"
  },
  {
    "path": "packages/cli/src/handlers/shared/anthropic-error.ts",
    "chars": 2541,
    "preview": "/**\n * Anthropic error envelope wrapper.\n * All proxy error responses MUST use this format.\n */\n\nexport type AnthropicEr"
  },
  {
    "path": "packages/cli/src/handlers/shared/format/identity-filter.ts",
    "chars": 820,
    "preview": "/**\n * Identity filter for Claude-specific markers in system prompts.\n *\n * Removes or replaces Claude-specific identity"
  },
  {
    "path": "packages/cli/src/handlers/shared/format/openai-messages.ts",
    "chars": 5497,
    "preview": "/**\n * OpenAI message format conversion utilities.\n *\n * Converts Claude/Anthropic message format to OpenAI message form"
  },
  {
    "path": "packages/cli/src/handlers/shared/format/openai-tools.ts",
    "chars": 4384,
    "preview": "/**\n * OpenAI tool schema conversion utilities.\n *\n * Converts Claude/Anthropic tool definitions to OpenAI function form"
  },
  {
    "path": "packages/cli/src/handlers/shared/gemini-queue.ts",
    "chars": 8188,
    "preview": "/**\n * Gemini Request Queue\n *\n * Singleton request queue for serializing Gemini API requests to prevent rate limit exha"
  },
  {
    "path": "packages/cli/src/handlers/shared/gemini-schema.ts",
    "chars": 5619,
    "preview": "/**\n * Gemini Schema Utilities\n *\n * Shared utilities for converting JSON Schema to Gemini's API format.\n * Used by both"
  },
  {
    "path": "packages/cli/src/handlers/shared/local-queue.ts",
    "chars": 10942,
    "preview": "/**\n * Local Model Request Queue\n *\n * Singleton queue for controlling concurrency to local models (Ollama, LM Studio, v"
  },
  {
    "path": "packages/cli/src/handlers/shared/openai-compat.ts",
    "chars": 717,
    "preview": "/**\n * Re-export shim for backwards compatibility.\n * All implementations have moved to focused modules:\n * - format/ope"
  },
  {
    "path": "packages/cli/src/handlers/shared/openrouter-queue.ts",
    "chars": 15197,
    "preview": "/**\n * OpenRouter Request Queue\n *\n * Singleton request queue for serializing OpenRouter API requests to prevent rate li"
  },
  {
    "path": "packages/cli/src/handlers/shared/remote-provider-types.ts",
    "chars": 5215,
    "preview": "/**\n * Types for remote API providers (OpenRouter, Gemini, OpenAI)\n *\n * These types define the common interface for clo"
  },
  {
    "path": "packages/cli/src/handlers/shared/stream-parsers/anthropic-sse.ts",
    "chars": 12501,
    "preview": "/**\n * Anthropic SSE passthrough stream parser.\n *\n * For providers that speak native Anthropic format (MiniMax, Kimi, Z"
  },
  {
    "path": "packages/cli/src/handlers/shared/stream-parsers/gemini-sse.ts",
    "chars": 10868,
    "preview": "/**\n * Gemini SSE → Claude SSE stream parser.\n *\n * Gemini streams SSE with `data: {\"candidates\": [{\"content\": {\"parts\":"
  },
  {
    "path": "packages/cli/src/handlers/shared/stream-parsers/index.ts",
    "chars": 576,
    "preview": "/**\n * Stream parsers — convert provider-specific streaming formats to Claude SSE.\n *\n * Each parser takes a Response fr"
  },
  {
    "path": "packages/cli/src/handlers/shared/stream-parsers/ollama-jsonl.ts",
    "chars": 5044,
    "preview": "/**\n * Ollama JSONL → Claude SSE stream parser.\n *\n * Ollama sends line-by-line JSON (NOT SSE):\n *   {\"message\": {\"conte"
  },
  {
    "path": "packages/cli/src/handlers/shared/stream-parsers/openai-responses-sse.ts",
    "chars": 12583,
    "preview": "/**\n * OpenAI Responses API SSE → Claude SSE stream parser.\n *\n * Handles Codex models that use /v1/responses instead of"
  },
  {
    "path": "packages/cli/src/handlers/shared/stream-parsers/openai-sse.ts",
    "chars": 29917,
    "preview": "/**\n * OpenAI SSE → Claude SSE stream parser.\n *\n * Converts OpenAI-compatible Server-Sent Events to Claude SSE format.\n"
  },
  {
    "path": "packages/cli/src/handlers/shared/token-tracker.ts",
    "chars": 9061,
    "preview": "/**\n * TokenTracker — unified token tracking and cost accounting.\n *\n * Replaces the 8 independent writeTokenFile implem"
  }
]

// ... and 152 more files (download for full content)

About this extraction

This page contains the full source code of the MadAppGang/claudish GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 352 files (4.4 MB), approximately 1.2M tokens, and a symbol index with 1864 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!