milady
This commit is contained in:
154
.env.example
Normal file
154
.env.example
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# Claude Code CLI — Environment Variables
|
||||||
|
# =============================================================================
|
||||||
|
# Copy this file to .env and fill in the values you need.
|
||||||
|
# All variables are optional unless noted otherwise.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# ── Authentication ───────────────────────────────────────────────────────────
|
||||||
|
# Your Anthropic API key (required for direct API access)
|
||||||
|
ANTHROPIC_API_KEY=
|
||||||
|
|
||||||
|
# Custom API base URL (default: https://api.anthropic.com)
|
||||||
|
# ANTHROPIC_BASE_URL=
|
||||||
|
|
||||||
|
# ── Model Selection ──────────────────────────────────────────────────────────
|
||||||
|
# Override the default model
|
||||||
|
# ANTHROPIC_MODEL=
|
||||||
|
|
||||||
|
# Override the small/fast model used internally
|
||||||
|
# ANTHROPIC_SMALL_FAST_MODEL=
|
||||||
|
|
||||||
|
# Custom model overrides (name + description are optional display labels)
|
||||||
|
# ANTHROPIC_DEFAULT_OPUS_MODEL=
|
||||||
|
# ANTHROPIC_DEFAULT_OPUS_MODEL_NAME=
|
||||||
|
# ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION=
|
||||||
|
# ANTHROPIC_DEFAULT_SONNET_MODEL=
|
||||||
|
# ANTHROPIC_DEFAULT_SONNET_MODEL_NAME=
|
||||||
|
# ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION=
|
||||||
|
# ANTHROPIC_DEFAULT_HAIKU_MODEL=
|
||||||
|
# ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME=
|
||||||
|
# ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION=
|
||||||
|
|
||||||
|
# Custom model option (shown alongside built-in models)
|
||||||
|
# ANTHROPIC_CUSTOM_MODEL_OPTION=
|
||||||
|
# ANTHROPIC_CUSTOM_MODEL_OPTION_NAME=
|
||||||
|
# ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION=
|
||||||
|
|
||||||
|
# Model for sub-agents / teammates
|
||||||
|
# CLAUDE_CODE_SUBAGENT_MODEL=
|
||||||
|
|
||||||
|
# ── Alternative Providers ────────────────────────────────────────────────────
|
||||||
|
# Use AWS Bedrock instead of direct Anthropic API
|
||||||
|
# CLAUDE_CODE_USE_BEDROCK=true
|
||||||
|
# ANTHROPIC_BEDROCK_BASE_URL=
|
||||||
|
# AWS_BEARER_TOKEN_BEDROCK=
|
||||||
|
# CLAUDE_CODE_SKIP_BEDROCK_AUTH=false
|
||||||
|
|
||||||
|
# Use Google Vertex AI
|
||||||
|
# CLAUDE_CODE_USE_VERTEX=true
|
||||||
|
|
||||||
|
# ── Shell & Environment ─────────────────────────────────────────────────────
|
||||||
|
# Override shell used for BashTool (default: auto-detected)
|
||||||
|
# CLAUDE_CODE_SHELL=/bin/bash
|
||||||
|
|
||||||
|
# Prefix prepended to every shell command
|
||||||
|
# CLAUDE_CODE_SHELL_PREFIX=
|
||||||
|
|
||||||
|
# Override temp directory
|
||||||
|
# CLAUDE_CODE_TMPDIR=
|
||||||
|
|
||||||
|
# ── Performance & Limits ─────────────────────────────────────────────────────
|
||||||
|
# Maximum output tokens per response
|
||||||
|
# CLAUDE_CODE_MAX_OUTPUT_TOKENS=
|
||||||
|
|
||||||
|
# Maximum tokens when reading files
|
||||||
|
# CLAUDE_CODE_FILE_READ_MAX_OUTPUT_TOKENS=
|
||||||
|
|
||||||
|
# Idle timeout (minutes) before session ends (default: 75)
|
||||||
|
# CLAUDE_CODE_IDLE_THRESHOLD_MINUTES=75
|
||||||
|
|
||||||
|
# Idle token threshold (default: 100000)
|
||||||
|
# CLAUDE_CODE_IDLE_TOKEN_THRESHOLD=100000
|
||||||
|
|
||||||
|
# Node.js options (e.g. heap size)
|
||||||
|
# NODE_OPTIONS=--max-old-space-size=8192
|
||||||
|
|
||||||
|
# ── Features & Modes ────────────────────────────────────────────────────────
|
||||||
|
# Enable simplified/worker mode
|
||||||
|
# CLAUDE_CODE_SIMPLE=true
|
||||||
|
|
||||||
|
# Enable coordinator (multi-agent) mode
|
||||||
|
# CLAUDE_CODE_COORDINATOR_MODE=true
|
||||||
|
|
||||||
|
# Enable proactive mode
|
||||||
|
# CLAUDE_CODE_PROACTIVE=true
|
||||||
|
|
||||||
|
# Enable experimental agent teams
|
||||||
|
# CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=true
|
||||||
|
|
||||||
|
# Enable task management
|
||||||
|
# CLAUDE_CODE_ENABLE_TASKS=true
|
||||||
|
|
||||||
|
# Enable prompt suggestions
|
||||||
|
# CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION=true
|
||||||
|
|
||||||
|
# ── Accessibility & UX ──────────────────────────────────────────────────────
|
||||||
|
# Enable accessibility mode
|
||||||
|
# CLAUDE_CODE_ACCESSIBILITY=true
|
||||||
|
|
||||||
|
# Disable terminal title updates
|
||||||
|
# CLAUDE_CODE_DISABLE_TERMINAL_TITLE=true
|
||||||
|
|
||||||
|
# Disable virtual scrolling
|
||||||
|
# CLAUDE_CODE_DISABLE_VIRTUAL_SCROLL=true
|
||||||
|
|
||||||
|
# Streamlined output (less verbose)
|
||||||
|
# CLAUDE_CODE_STREAMLINED_OUTPUT=true
|
||||||
|
|
||||||
|
# Disable syntax highlighting
|
||||||
|
# CLAUDE_CODE_SYNTAX_HIGHLIGHT=false
|
||||||
|
|
||||||
|
# ── Remote / Bridge ─────────────────────────────────────────────────────────
|
||||||
|
# Enable remote execution mode
|
||||||
|
# CLAUDE_CODE_REMOTE=true
|
||||||
|
|
||||||
|
# Remote session ID
|
||||||
|
# CLAUDE_CODE_REMOTE_SESSION_ID=
|
||||||
|
|
||||||
|
# Environment kind (e.g. bridge)
|
||||||
|
# CLAUDE_CODE_ENVIRONMENT_KIND=
|
||||||
|
|
||||||
|
# OAuth tokens for bridge
|
||||||
|
# CLAUDE_CODE_OAUTH_REFRESH_TOKEN=
|
||||||
|
# CLAUDE_CODE_OAUTH_SCOPES=
|
||||||
|
|
||||||
|
# ── Debugging ────────────────────────────────────────────────────────────────
|
||||||
|
# Debug log level (error, warn, info, debug, trace)
|
||||||
|
# CLAUDE_CODE_DEBUG_LOG_LEVEL=info
|
||||||
|
|
||||||
|
# Debug logs directory
|
||||||
|
# CLAUDE_CODE_DEBUG_LOGS_DIR=
|
||||||
|
|
||||||
|
# Enable startup profiling
|
||||||
|
# CLAUDE_CODE_PROFILE_STARTUP=true
|
||||||
|
|
||||||
|
# General debug flag
|
||||||
|
# DEBUG=true
|
||||||
|
|
||||||
|
# ── Privacy & Telemetry ─────────────────────────────────────────────────────
|
||||||
|
# Disable telemetry
|
||||||
|
# DISABLE_TELEMETRY=true
|
||||||
|
|
||||||
|
# Disable non-essential network traffic
|
||||||
|
# CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=true
|
||||||
|
|
||||||
|
# ── Proxy & Network ─────────────────────────────────────────────────────────
|
||||||
|
# HTTPS proxy
|
||||||
|
# HTTPS_PROXY=
|
||||||
|
|
||||||
|
# Custom SSL certificate
|
||||||
|
# SSL_CERT_FILE=
|
||||||
|
|
||||||
|
# Unix socket for Anthropic API
|
||||||
|
# ANTHROPIC_UNIX_SOCKET=
|
||||||
27
.github/workflows/ci.yml
vendored
Normal file
27
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Typecheck & Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Typecheck
|
||||||
|
run: bun run typecheck
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: bun run lint
|
||||||
37
.gitignore
vendored
Normal file
37
.gitignore
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
dist/
|
||||||
|
out/
|
||||||
|
build/
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Editor / IDE
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.vscode/settings.json
|
||||||
|
.vscode/launch.json
|
||||||
|
|
||||||
|
# Debug & logs
|
||||||
|
*.log
|
||||||
|
debug-logs/
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# Bun
|
||||||
|
bun.lockb
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
4
.vscode/mcp.json
vendored
4
.vscode/mcp.json
vendored
@@ -2,8 +2,8 @@
|
|||||||
"servers": {
|
"servers": {
|
||||||
"claude-code-explorer": {
|
"claude-code-explorer": {
|
||||||
"type": "stdio",
|
"type": "stdio",
|
||||||
"command": "node",
|
"command": "npx",
|
||||||
"args": ["${workspaceFolder}/mcp-server/dist/index.js"],
|
"args": ["tsx", "${workspaceFolder}/mcp-server/src/index.ts"],
|
||||||
"env": {
|
"env": {
|
||||||
"CLAUDE_CODE_SRC_ROOT": "${workspaceFolder}/src"
|
"CLAUDE_CODE_SRC_ROOT": "${workspaceFolder}/src"
|
||||||
}
|
}
|
||||||
|
|||||||
29
Dockerfile
Normal file
29
Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# Claude Code CLI — Development Container
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# This image provides a ready-to-explore environment for the
|
||||||
|
# leaked source. It does NOT produce a runnable build (the
|
||||||
|
# original build tooling was not included in the leak).
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
FROM oven/bun:1-alpine AS base
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install OS-level dependencies used at runtime
|
||||||
|
RUN apk add --no-cache git ripgrep
|
||||||
|
|
||||||
|
# Copy manifests first for layer caching
|
||||||
|
COPY package.json bun.lockb* ./
|
||||||
|
|
||||||
|
# Install npm packages
|
||||||
|
RUN bun install --frozen-lockfile || bun install
|
||||||
|
|
||||||
|
# Copy source
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Typecheck (optional — fails loudly if deps are wrong)
|
||||||
|
# RUN bun run typecheck
|
||||||
|
|
||||||
|
# Default: drop into a shell for exploration
|
||||||
|
CMD ["sh"]
|
||||||
9
LICENSE
Normal file
9
LICENSE
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
UNLICENSED — NOT FOR REDISTRIBUTION
|
||||||
|
|
||||||
|
This repository contains leaked proprietary source code belonging to Anthropic, PBC.
|
||||||
|
It is published here strictly for educational and research purposes.
|
||||||
|
|
||||||
|
The original software is NOT open-source. Anthropic has not released this code
|
||||||
|
under any permissive or copyleft license. Use at your own legal risk.
|
||||||
|
|
||||||
|
For the official Claude Code CLI, see: https://docs.anthropic.com/en/docs/claude-code
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
> **Note:** The original unmodified leaked source is preserved in the [`backup` branch](https://github.com/nirholas/claude-code/tree/backup).
|
||||||
|
|
||||||
# Claude Code — Leaked Source (2026-03-31)
|
# Claude Code — Leaked Source (2026-03-31)
|
||||||
|
|
||||||
> **On March 31, 2026, the full source code of Anthropic's Claude Code CLI was leaked** via a `.map` file exposed in their npm registry.
|
> **On March 31, 2026, the full source code of Anthropic's Claude Code CLI was leaked** via a `.map` file exposed in their npm registry.
|
||||||
|
|||||||
47
biome.json
Normal file
47
biome.json
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||||
|
"organizeImports": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true,
|
||||||
|
"complexity": {
|
||||||
|
"noExcessiveCognitiveComplexity": "warn"
|
||||||
|
},
|
||||||
|
"correctness": {
|
||||||
|
"noUnusedImports": "warn",
|
||||||
|
"noUnusedVariables": "warn"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"noNonNullAssertion": "off",
|
||||||
|
"useImportType": "warn"
|
||||||
|
},
|
||||||
|
"suspicious": {
|
||||||
|
"noExplicitAny": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"indentStyle": "tab",
|
||||||
|
"indentWidth": 2,
|
||||||
|
"lineWidth": 100
|
||||||
|
},
|
||||||
|
"javascript": {
|
||||||
|
"formatter": {
|
||||||
|
"quoteStyle": "single",
|
||||||
|
"semicolons": "asNeeded"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
"formatter": {
|
||||||
|
"indentStyle": "space",
|
||||||
|
"indentWidth": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"ignore": ["node_modules", "dist", "*.d.ts"]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ A standalone [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) se
|
|||||||
|
|
||||||
## What It Does
|
## What It Does
|
||||||
|
|
||||||
Exposes 7 tools and 3 resources for navigating the ~1,900-file, 512K+ line Claude Code codebase:
|
Exposes 7 tools, 3 resources, and 5 prompts for navigating the ~1,900-file, 512K+ line Claude Code codebase:
|
||||||
|
|
||||||
### Tools
|
### Tools
|
||||||
|
|
||||||
@@ -28,6 +28,16 @@ Exposes 7 tools and 3 resources for navigating the ~1,900-file, 512K+ line Claud
|
|||||||
| `claude-code://commands` | Command registry (JSON) |
|
| `claude-code://commands` | Command registry (JSON) |
|
||||||
| `claude-code://source/{path}` | Any source file (template) |
|
| `claude-code://source/{path}` | Any source file (template) |
|
||||||
|
|
||||||
|
### Prompts
|
||||||
|
|
||||||
|
| Prompt | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `explain_tool` | Deep-dive explanation of a specific tool's purpose, schema, permissions, and flow |
|
||||||
|
| `explain_command` | Explanation of a specific slash command's behavior and implementation |
|
||||||
|
| `architecture_overview` | Guided tour of the full Claude Code architecture |
|
||||||
|
| `how_does_it_work` | Explain a feature/subsystem (permissions, MCP, bridge, etc.) |
|
||||||
|
| `compare_tools` | Side-by-side comparison of two tools |
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -99,6 +109,18 @@ Add to `~/.cursor/mcp.json`:
|
|||||||
|----------|---------|-------------|
|
|----------|---------|-------------|
|
||||||
| `CLAUDE_CODE_SRC_ROOT` | `../src` (relative to dist/) | Path to the Claude Code `src/` directory |
|
| `CLAUDE_CODE_SRC_ROOT` | `../src` (relative to dist/) | Path to the Claude Code `src/` directory |
|
||||||
|
|
||||||
|
## Prompts
|
||||||
|
|
||||||
|
The server also exposes prompt templates for guided exploration:
|
||||||
|
|
||||||
|
| Prompt | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `explain_tool` | Deep-dive explanation of a specific tool (input schema, permissions, execution flow) |
|
||||||
|
| `explain_command` | Explain how a slash command works |
|
||||||
|
| `architecture_overview` | Guided tour of the entire Claude Code architecture |
|
||||||
|
| `how_does_it_work` | Explain a feature or subsystem (e.g. "permission system", "MCP client", "query engine") |
|
||||||
|
| `compare_tools` | Side-by-side comparison of two tools |
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
Once connected, you can ask your AI assistant things like:
|
Once connected, you can ask your AI assistant things like:
|
||||||
@@ -109,11 +131,14 @@ Once connected, you can ask your AI assistant things like:
|
|||||||
- "What files are in the bridge directory?"
|
- "What files are in the bridge directory?"
|
||||||
- "Read the QueryEngine.ts file, lines 1-100"
|
- "Read the QueryEngine.ts file, lines 1-100"
|
||||||
- "How does the MCP client connection work?"
|
- "How does the MCP client connection work?"
|
||||||
|
- Use the `explain_tool` prompt with "FileEditTool" to get a full breakdown
|
||||||
|
- Use `how_does_it_work` with "bridge" to understand IDE integration
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run dev # Watch mode — recompile on changes
|
npm install
|
||||||
npm run build # One-time build
|
npm run dev # Run directly with tsx (no build needed)
|
||||||
npm start # Run the server
|
npm run build # Compile TypeScript to dist/
|
||||||
|
npm start # Run compiled server
|
||||||
```
|
```
|
||||||
|
|||||||
1179
mcp-server/package-lock.json
generated
Normal file
1179
mcp-server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "claude-code-explorer-mcp",
|
"name": "claude-code-explorer-mcp",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"description": "MCP server for exploring the Claude Code source code",
|
"description": "MCP server for exploring the Claude Code source code — STDIO, HTTP, and SSE transports",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -10,13 +10,17 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "node dist/index.js",
|
"start": "node dist/index.js",
|
||||||
"dev": "tsc --watch"
|
"start:http": "node dist/http.js",
|
||||||
|
"dev": "npx tsx src/index.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.12.1"
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
||||||
|
"express": "^4.21.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/express": "^5.0.0",
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
|
"tsx": "^4.19.0",
|
||||||
"typescript": "^5.7.0"
|
"typescript": "^5.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import {
|
|||||||
ListResourcesRequestSchema,
|
ListResourcesRequestSchema,
|
||||||
ReadResourceRequestSchema,
|
ReadResourceRequestSchema,
|
||||||
ListResourceTemplatesRequestSchema,
|
ListResourceTemplatesRequestSchema,
|
||||||
|
ListPromptsRequestSchema,
|
||||||
|
GetPromptRequestSchema,
|
||||||
} from "@modelcontextprotocol/sdk/types.js";
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
import * as fs from "node:fs/promises";
|
import * as fs from "node:fs/promises";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
@@ -147,6 +149,7 @@ const server = new Server(
|
|||||||
capabilities: {
|
capabilities: {
|
||||||
tools: {},
|
tools: {},
|
||||||
resources: {},
|
resources: {},
|
||||||
|
prompts: {},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -631,6 +634,335 @@ ${commands.map((c) => `- **${c.name}** ${c.isDirectory ? "(directory)" : "(file)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ---- Prompts -------------------------------------------------------------
|
||||||
|
|
||||||
|
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
||||||
|
prompts: [
|
||||||
|
{
|
||||||
|
name: "explain_tool",
|
||||||
|
description:
|
||||||
|
"Explain how a specific Claude Code tool works, including its input schema, permissions, and execution flow.",
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: "toolName",
|
||||||
|
description:
|
||||||
|
"Tool directory name, e.g. 'BashTool', 'FileEditTool', 'AgentTool'",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "explain_command",
|
||||||
|
description:
|
||||||
|
"Explain how a specific Claude Code slash command works.",
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: "commandName",
|
||||||
|
description:
|
||||||
|
"Command name, e.g. 'commit', 'review', 'mcp', 'config'",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "architecture_overview",
|
||||||
|
description:
|
||||||
|
"Get a guided tour of the Claude Code architecture with explanations of each subsystem.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "how_does_it_work",
|
||||||
|
description:
|
||||||
|
"Explain how a specific feature or subsystem of Claude Code works. Good for understanding MCP integration, permission model, tool system, etc.",
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: "feature",
|
||||||
|
description:
|
||||||
|
"Feature or subsystem to explain, e.g. 'permission system', 'MCP client', 'tool deferred loading', 'query engine', 'bridge/IDE integration'",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "compare_tools",
|
||||||
|
description:
|
||||||
|
"Compare two Claude Code tools side by side — their purpose, inputs, permissions, and implementation patterns.",
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
name: "tool1",
|
||||||
|
description: "First tool name, e.g. 'FileReadTool'",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tool2",
|
||||||
|
description: "Second tool name, e.g. 'FileWriteTool'",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
|
||||||
|
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
||||||
|
const { name, arguments: args } = request.params;
|
||||||
|
|
||||||
|
switch (name) {
|
||||||
|
case "explain_tool": {
|
||||||
|
const toolName = args?.toolName;
|
||||||
|
if (!toolName) throw new Error("toolName argument is required");
|
||||||
|
const toolDir = safePath(`tools/${toolName}`);
|
||||||
|
if (!toolDir || !(await dirExists(toolDir)))
|
||||||
|
throw new Error(`Tool not found: ${toolName}`);
|
||||||
|
|
||||||
|
const files = await listDir(toolDir);
|
||||||
|
const mainFile =
|
||||||
|
files.find((f) => f === `${toolName}.ts` || f === `${toolName}.tsx`) ??
|
||||||
|
files.find((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
||||||
|
|
||||||
|
let source = "";
|
||||||
|
if (mainFile) {
|
||||||
|
const abs = path.join(toolDir, mainFile);
|
||||||
|
source = await fs.readFile(abs, "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
description: `Explanation of the ${toolName} tool`,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "user" as const,
|
||||||
|
content: {
|
||||||
|
type: "text" as const,
|
||||||
|
text: `Analyze and explain this Claude Code tool implementation. Cover:
|
||||||
|
1. **Purpose** — What does this tool do?
|
||||||
|
2. **Input Schema** — What parameters does it accept?
|
||||||
|
3. **Permissions** — What permission checks are performed?
|
||||||
|
4. **Execution Flow** — How does it process a request?
|
||||||
|
5. **Output** — What does it return?
|
||||||
|
6. **Concurrency/Safety** — Is it read-only? Concurrency-safe? Destructive?
|
||||||
|
|
||||||
|
Files in tools/${toolName}/: ${files.join(", ")}
|
||||||
|
|
||||||
|
Main source (${mainFile ?? "not found"}):\n\`\`\`typescript\n${source}\n\`\`\``,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "explain_command": {
|
||||||
|
const commandName = args?.commandName;
|
||||||
|
if (!commandName) throw new Error("commandName argument is required");
|
||||||
|
|
||||||
|
const candidates = [
|
||||||
|
`commands/${commandName}`,
|
||||||
|
`commands/${commandName}.ts`,
|
||||||
|
`commands/${commandName}.tsx`,
|
||||||
|
];
|
||||||
|
let found: string | null = null;
|
||||||
|
let isDir = false;
|
||||||
|
for (const c of candidates) {
|
||||||
|
const abs = safePath(c);
|
||||||
|
if (abs && (await dirExists(abs))) { found = abs; isDir = true; break; }
|
||||||
|
if (abs && (await fileExists(abs))) { found = abs; break; }
|
||||||
|
}
|
||||||
|
if (!found) throw new Error(`Command not found: ${commandName}`);
|
||||||
|
|
||||||
|
let source = "";
|
||||||
|
let fileList = "";
|
||||||
|
if (isDir) {
|
||||||
|
const files = await listDir(found);
|
||||||
|
fileList = files.join(", ");
|
||||||
|
const indexFile = files.find((f) => f === "index.ts" || f === "index.tsx");
|
||||||
|
if (indexFile) {
|
||||||
|
source = await fs.readFile(path.join(found, indexFile), "utf-8");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
source = await fs.readFile(found, "utf-8");
|
||||||
|
fileList = path.basename(found);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
description: `Explanation of the /${commandName} command`,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "user" as const,
|
||||||
|
content: {
|
||||||
|
type: "text" as const,
|
||||||
|
text: `Analyze and explain this Claude Code slash command. Cover:
|
||||||
|
1. **Purpose** — What does /${commandName} do?
|
||||||
|
2. **Type** — Is it a 'prompt' command (sends to LLM) or 'action' command (executes directly)?
|
||||||
|
3. **Allowed Tools** — What tools can the LLM use when processing this command?
|
||||||
|
4. **Arguments** — What arguments does it accept?
|
||||||
|
5. **Implementation** — How does it work?
|
||||||
|
|
||||||
|
Files: ${fileList}
|
||||||
|
|
||||||
|
Source:\n\`\`\`typescript\n${source}\n\`\`\``,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "architecture_overview": {
|
||||||
|
const readmePath = path.resolve(SRC_ROOT, "..", "README.md");
|
||||||
|
let readme = "";
|
||||||
|
try { readme = await fs.readFile(readmePath, "utf-8"); } catch { /* */ }
|
||||||
|
|
||||||
|
const topLevel = await listDir(SRC_ROOT);
|
||||||
|
const tools = await getToolList();
|
||||||
|
const commands = await getCommandList();
|
||||||
|
|
||||||
|
return {
|
||||||
|
description: "Architecture overview of Claude Code",
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "user" as const,
|
||||||
|
content: {
|
||||||
|
type: "text" as const,
|
||||||
|
text: `Give a comprehensive guided tour of the Claude Code architecture. Use the following context:
|
||||||
|
|
||||||
|
## README
|
||||||
|
${readme}
|
||||||
|
|
||||||
|
## src/ top-level files and directories
|
||||||
|
${topLevel.join("\n")}
|
||||||
|
|
||||||
|
## Tools (${tools.length})
|
||||||
|
${tools.map((t) => `- ${t.name}: ${t.files.join(", ")}`).join("\n")}
|
||||||
|
|
||||||
|
## Commands (${commands.length})
|
||||||
|
${commands.map((c) => `- ${c.name} ${c.isDirectory ? "(dir)" : "(file)"}`).join("\n")}
|
||||||
|
|
||||||
|
Explain the overall architecture, how the main subsystems connect, and the request lifecycle from CLI input to tool execution.`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "how_does_it_work": {
|
||||||
|
const feature = args?.feature;
|
||||||
|
if (!feature) throw new Error("feature argument is required");
|
||||||
|
|
||||||
|
// Map well-known features to relevant source paths
|
||||||
|
const featureMap: Record<string, string[]> = {
|
||||||
|
"permission system": ["utils/permissions/", "hooks/toolPermission/", "Tool.ts"],
|
||||||
|
"permissions": ["utils/permissions/", "hooks/toolPermission/", "Tool.ts"],
|
||||||
|
"mcp client": ["services/mcp/", "tools/MCPTool/", "tools/ListMcpResourcesTool/", "tools/ReadMcpResourceTool/"],
|
||||||
|
"mcp": ["services/mcp/", "entrypoints/mcp.ts", "tools/MCPTool/"],
|
||||||
|
"tool system": ["Tool.ts", "tools.ts", "tools/"],
|
||||||
|
"tools": ["Tool.ts", "tools.ts"],
|
||||||
|
"query engine": ["QueryEngine.ts", "query/"],
|
||||||
|
"bridge": ["bridge/"],
|
||||||
|
"ide integration": ["bridge/"],
|
||||||
|
"context": ["context.ts", "context/"],
|
||||||
|
"commands": ["commands.ts", "types/command.ts"],
|
||||||
|
"command system": ["commands.ts", "types/command.ts", "commands/"],
|
||||||
|
"cost tracking": ["cost-tracker.ts", "costHook.ts"],
|
||||||
|
"plugins": ["plugins/"],
|
||||||
|
"skills": ["skills/"],
|
||||||
|
"tasks": ["tasks.ts", "tasks/", "tools/TaskCreateTool/"],
|
||||||
|
"coordinator": ["coordinator/"],
|
||||||
|
"multi-agent": ["coordinator/", "tools/AgentTool/"],
|
||||||
|
"memory": ["memdir/", "commands/memory/"],
|
||||||
|
"voice": ["voice/"],
|
||||||
|
"server": ["server/"],
|
||||||
|
};
|
||||||
|
|
||||||
|
const paths = featureMap[feature.toLowerCase()] ?? [];
|
||||||
|
let contextFiles = "";
|
||||||
|
for (const p of paths) {
|
||||||
|
const abs = safePath(p);
|
||||||
|
if (!abs) continue;
|
||||||
|
try {
|
||||||
|
const stat = await fs.stat(abs);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
const files = await listDir(abs);
|
||||||
|
contextFiles += `\n### ${p}\nFiles: ${files.join(", ")}\n`;
|
||||||
|
} else {
|
||||||
|
const content = await fs.readFile(abs, "utf-8");
|
||||||
|
// Only include first 200 lines to keep prompt manageable
|
||||||
|
const preview = content.split("\n").slice(0, 200).join("\n");
|
||||||
|
contextFiles += `\n### ${p} (first 200 lines)\n\`\`\`typescript\n${preview}\n\`\`\`\n`;
|
||||||
|
}
|
||||||
|
} catch { /* skip */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
description: `How ${feature} works in Claude Code`,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "user" as const,
|
||||||
|
content: {
|
||||||
|
type: "text" as const,
|
||||||
|
text: `Explain how "${feature}" works in the Claude Code CLI. Use these relevant source files as context:
|
||||||
|
${contextFiles || "(No specific source files mapped for this feature. Use the search_source and read_source_file tools to find relevant code.)"}
|
||||||
|
|
||||||
|
Explain the design, key types/interfaces, data flow, and how it integrates with the rest of the system.`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "compare_tools": {
|
||||||
|
const tool1 = args?.tool1;
|
||||||
|
const tool2 = args?.tool2;
|
||||||
|
if (!tool1 || !tool2) throw new Error("Both tool1 and tool2 arguments are required");
|
||||||
|
|
||||||
|
const sources: string[] = [];
|
||||||
|
for (const toolName of [tool1, tool2]) {
|
||||||
|
const toolDir = safePath(`tools/${toolName}`);
|
||||||
|
if (!toolDir || !(await dirExists(toolDir))) {
|
||||||
|
sources.push(`// Tool not found: ${toolName}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const files = await listDir(toolDir);
|
||||||
|
const mainFile =
|
||||||
|
files.find((f) => f === `${toolName}.ts` || f === `${toolName}.tsx`) ??
|
||||||
|
files.find((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
||||||
|
if (mainFile) {
|
||||||
|
const content = await fs.readFile(path.join(toolDir, mainFile), "utf-8");
|
||||||
|
sources.push(`// tools/${toolName}/${mainFile}\n${content}`);
|
||||||
|
} else {
|
||||||
|
sources.push(`// No main source found for ${toolName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
description: `Comparison of ${tool1} vs ${tool2}`,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "user" as const,
|
||||||
|
content: {
|
||||||
|
type: "text" as const,
|
||||||
|
text: `Compare these two Claude Code tools side by side. Analyze:
|
||||||
|
1. **Purpose** — What each tool does
|
||||||
|
2. **Input Schemas** — How their parameters differ
|
||||||
|
3. **Permissions** — Different permission models
|
||||||
|
4. **Read-only / Destructive** — Safety characteristics
|
||||||
|
5. **Implementation Patterns** — Similarities and differences in how they're built
|
||||||
|
|
||||||
|
## ${tool1}
|
||||||
|
\`\`\`typescript
|
||||||
|
${sources[0]}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## ${tool2}
|
||||||
|
\`\`\`typescript
|
||||||
|
${sources[1]}
|
||||||
|
\`\`\``,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown prompt: ${name}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Main
|
// Main
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
661
mcp-server/src/server.ts
Normal file
661
mcp-server/src/server.ts
Normal file
@@ -0,0 +1,661 @@
|
|||||||
|
/**
|
||||||
|
* Shared MCP server definition — transport-agnostic.
|
||||||
|
*
|
||||||
|
* Exposes tools and resources for exploring the Claude Code source code.
|
||||||
|
* This module is imported by both the STDIO and HTTP entrypoints.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||||
|
import {
|
||||||
|
CallToolRequestSchema,
|
||||||
|
ListToolsRequestSchema,
|
||||||
|
ListResourcesRequestSchema,
|
||||||
|
ReadResourceRequestSchema,
|
||||||
|
ListResourceTemplatesRequestSchema,
|
||||||
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
import * as fs from "node:fs/promises";
|
||||||
|
import * as path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Configuration
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
export const SRC_ROOT = path.resolve(
|
||||||
|
process.env.CLAUDE_CODE_SRC_ROOT ?? path.join(__dirname, "..", "..", "src")
|
||||||
|
);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Helpers
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function dirExists(p: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
return (await fs.stat(p)).isDirectory();
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fileExists(p: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
return (await fs.stat(p)).isFile();
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listDir(dir: string): Promise<string[]> {
|
||||||
|
try {
|
||||||
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||||
|
return entries
|
||||||
|
.map((e) => (e.isDirectory() ? e.name + "/" : e.name))
|
||||||
|
.sort();
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function walkFiles(root: string, rel = ""): Promise<string[]> {
|
||||||
|
const results: string[] = [];
|
||||||
|
let entries;
|
||||||
|
try {
|
||||||
|
entries = await fs.readdir(path.join(root, rel), { withFileTypes: true });
|
||||||
|
} catch {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
for (const e of entries) {
|
||||||
|
const child = rel ? `${rel}/${e.name}` : e.name;
|
||||||
|
if (e.isDirectory()) {
|
||||||
|
results.push(...(await walkFiles(root, child)));
|
||||||
|
} else {
|
||||||
|
results.push(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Safely resolve a user-supplied relative path under SRC_ROOT (blocks path traversal). */
|
||||||
|
function safePath(relPath: string): string | null {
|
||||||
|
const resolved = path.resolve(SRC_ROOT, relPath);
|
||||||
|
if (!resolved.startsWith(SRC_ROOT)) return null;
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Metadata Types
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
interface ToolInfo {
|
||||||
|
name: string;
|
||||||
|
directory: string;
|
||||||
|
files: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CommandInfo {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
isDirectory: boolean;
|
||||||
|
files?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getToolList(): Promise<ToolInfo[]> {
|
||||||
|
const toolsDir = path.join(SRC_ROOT, "tools");
|
||||||
|
const entries = await fs.readdir(toolsDir, { withFileTypes: true });
|
||||||
|
const tools: ToolInfo[] = [];
|
||||||
|
for (const e of entries) {
|
||||||
|
if (!e.isDirectory() || e.name === "shared" || e.name === "testing")
|
||||||
|
continue;
|
||||||
|
const files = await listDir(path.join(toolsDir, e.name));
|
||||||
|
tools.push({ name: e.name, directory: `tools/${e.name}`, files });
|
||||||
|
}
|
||||||
|
return tools.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCommandList(): Promise<CommandInfo[]> {
|
||||||
|
const cmdsDir = path.join(SRC_ROOT, "commands");
|
||||||
|
const entries = await fs.readdir(cmdsDir, { withFileTypes: true });
|
||||||
|
const commands: CommandInfo[] = [];
|
||||||
|
for (const e of entries) {
|
||||||
|
if (e.isDirectory()) {
|
||||||
|
const files = await listDir(path.join(cmdsDir, e.name));
|
||||||
|
commands.push({
|
||||||
|
name: e.name,
|
||||||
|
path: `commands/${e.name}`,
|
||||||
|
isDirectory: true,
|
||||||
|
files,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
commands.push({
|
||||||
|
name: e.name.replace(/\.(ts|tsx)$/, ""),
|
||||||
|
path: `commands/${e.name}`,
|
||||||
|
isDirectory: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return commands.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Server Factory
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export function createServer(): Server {
|
||||||
|
const server = new Server(
|
||||||
|
{ name: "claude-code-explorer", version: "1.1.0" },
|
||||||
|
{
|
||||||
|
capabilities: {
|
||||||
|
tools: {},
|
||||||
|
resources: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// ---- Resources ---------------------------------------------------------
|
||||||
|
|
||||||
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
||||||
|
resources: [
|
||||||
|
{
|
||||||
|
uri: "claude-code://architecture",
|
||||||
|
name: "Architecture Overview",
|
||||||
|
description:
|
||||||
|
"High-level overview of the Claude Code source architecture",
|
||||||
|
mimeType: "text/markdown",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: "claude-code://tools",
|
||||||
|
name: "Tool Registry",
|
||||||
|
description: "List of all agent tools with their files",
|
||||||
|
mimeType: "application/json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: "claude-code://commands",
|
||||||
|
name: "Command Registry",
|
||||||
|
description: "List of all slash commands",
|
||||||
|
mimeType: "application/json",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
|
||||||
|
server.setRequestHandler(
|
||||||
|
ListResourceTemplatesRequestSchema,
|
||||||
|
async () => ({
|
||||||
|
resourceTemplates: [
|
||||||
|
{
|
||||||
|
uriTemplate: "claude-code://source/{path}",
|
||||||
|
name: "Source file",
|
||||||
|
description:
|
||||||
|
"Read a source file from the Claude Code src/ directory",
|
||||||
|
mimeType: "text/plain",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
server.setRequestHandler(
|
||||||
|
ReadResourceRequestSchema,
|
||||||
|
async (request: { params: { uri: string } }) => {
|
||||||
|
const { uri } = request.params;
|
||||||
|
|
||||||
|
if (uri === "claude-code://architecture") {
|
||||||
|
const readmePath = path.resolve(SRC_ROOT, "..", "README.md");
|
||||||
|
let text: string;
|
||||||
|
try {
|
||||||
|
text = await fs.readFile(readmePath, "utf-8");
|
||||||
|
} catch {
|
||||||
|
text = "README.md not found.";
|
||||||
|
}
|
||||||
|
return { contents: [{ uri, mimeType: "text/markdown", text }] };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri === "claude-code://tools") {
|
||||||
|
const tools = await getToolList();
|
||||||
|
return {
|
||||||
|
contents: [
|
||||||
|
{
|
||||||
|
uri,
|
||||||
|
mimeType: "application/json",
|
||||||
|
text: JSON.stringify(tools, null, 2),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri === "claude-code://commands") {
|
||||||
|
const commands = await getCommandList();
|
||||||
|
return {
|
||||||
|
contents: [
|
||||||
|
{
|
||||||
|
uri,
|
||||||
|
mimeType: "application/json",
|
||||||
|
text: JSON.stringify(commands, null, 2),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri.startsWith("claude-code://source/")) {
|
||||||
|
const relPath = uri.slice("claude-code://source/".length);
|
||||||
|
const abs = safePath(relPath);
|
||||||
|
if (!abs) throw new Error("Invalid path");
|
||||||
|
const text = await fs.readFile(abs, "utf-8");
|
||||||
|
return { contents: [{ uri, mimeType: "text/plain", text }] };
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unknown resource: ${uri}`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// ---- Tools -------------------------------------------------------------
|
||||||
|
|
||||||
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||||
|
tools: [
|
||||||
|
{
|
||||||
|
name: "list_tools",
|
||||||
|
description:
|
||||||
|
"List all Claude Code agent tools (BashTool, FileReadTool, etc.) with their source files.",
|
||||||
|
inputSchema: { type: "object" as const, properties: {} },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "list_commands",
|
||||||
|
description:
|
||||||
|
"List all Claude Code slash commands (/commit, /review, /mcp, etc.) with their source files.",
|
||||||
|
inputSchema: { type: "object" as const, properties: {} },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "get_tool_source",
|
||||||
|
description:
|
||||||
|
"Read the full source code of a specific Claude Code tool implementation.",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object" as const,
|
||||||
|
properties: {
|
||||||
|
toolName: {
|
||||||
|
type: "string",
|
||||||
|
description: "Tool directory name, e.g. 'BashTool'",
|
||||||
|
},
|
||||||
|
fileName: {
|
||||||
|
type: "string",
|
||||||
|
description:
|
||||||
|
"Specific file within the tool directory. Omit for the main file.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["toolName"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "get_command_source",
|
||||||
|
description:
|
||||||
|
"Read the source code of a specific Claude Code slash command.",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object" as const,
|
||||||
|
properties: {
|
||||||
|
commandName: {
|
||||||
|
type: "string",
|
||||||
|
description: "Command name, e.g. 'commit', 'review'",
|
||||||
|
},
|
||||||
|
fileName: {
|
||||||
|
type: "string",
|
||||||
|
description: "Specific file within the command directory.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["commandName"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "read_source_file",
|
||||||
|
description:
|
||||||
|
"Read any source file from the Claude Code src/ directory by relative path.",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object" as const,
|
||||||
|
properties: {
|
||||||
|
path: {
|
||||||
|
type: "string",
|
||||||
|
description: "Relative path from src/, e.g. 'QueryEngine.ts'",
|
||||||
|
},
|
||||||
|
startLine: {
|
||||||
|
type: "number",
|
||||||
|
description: "1-based start line.",
|
||||||
|
},
|
||||||
|
endLine: {
|
||||||
|
type: "number",
|
||||||
|
description: "1-based end line.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["path"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "search_source",
|
||||||
|
description:
|
||||||
|
"Search for a regex pattern across the Claude Code source. Returns matching lines with paths and line numbers.",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object" as const,
|
||||||
|
properties: {
|
||||||
|
pattern: {
|
||||||
|
type: "string",
|
||||||
|
description: "Search pattern (regex).",
|
||||||
|
},
|
||||||
|
filePattern: {
|
||||||
|
type: "string",
|
||||||
|
description: "File extension filter, e.g. '.ts'",
|
||||||
|
},
|
||||||
|
maxResults: {
|
||||||
|
type: "number",
|
||||||
|
description: "Max matches (default: 50).",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["pattern"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "list_directory",
|
||||||
|
description: "List files and subdirectories under src/.",
|
||||||
|
inputSchema: {
|
||||||
|
type: "object" as const,
|
||||||
|
properties: {
|
||||||
|
path: {
|
||||||
|
type: "string",
|
||||||
|
description: "Relative path from src/, e.g. 'services'. '' for root.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["path"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "get_architecture",
|
||||||
|
description:
|
||||||
|
"Get a high-level architecture overview of Claude Code.",
|
||||||
|
inputSchema: { type: "object" as const, properties: {} },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
|
||||||
|
server.setRequestHandler(
|
||||||
|
CallToolRequestSchema,
|
||||||
|
async (request: {
|
||||||
|
params: { name: string; arguments?: Record<string, unknown> };
|
||||||
|
}) => {
|
||||||
|
const { name, arguments: args } = request.params;
|
||||||
|
|
||||||
|
switch (name) {
|
||||||
|
case "list_tools": {
|
||||||
|
const tools = await getToolList();
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{ type: "text" as const, text: JSON.stringify(tools, null, 2) },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "list_commands": {
|
||||||
|
const commands = await getCommandList();
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text" as const,
|
||||||
|
text: JSON.stringify(commands, null, 2),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "get_tool_source": {
|
||||||
|
const toolName = (args as Record<string, unknown>)
|
||||||
|
?.toolName as string;
|
||||||
|
if (!toolName) throw new Error("toolName is required");
|
||||||
|
const toolDir = safePath(`tools/${toolName}`);
|
||||||
|
if (!toolDir || !(await dirExists(toolDir)))
|
||||||
|
throw new Error(`Tool not found: ${toolName}`);
|
||||||
|
|
||||||
|
let fileName = (args as Record<string, unknown>)?.fileName as
|
||||||
|
| string
|
||||||
|
| undefined;
|
||||||
|
if (!fileName) {
|
||||||
|
const files = await listDir(toolDir);
|
||||||
|
const main =
|
||||||
|
files.find(
|
||||||
|
(f) => f === `${toolName}.ts` || f === `${toolName}.tsx`
|
||||||
|
) ??
|
||||||
|
files.find((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
|
||||||
|
if (!main) throw new Error(`No source files in ${toolName}`);
|
||||||
|
fileName = main;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = safePath(`tools/${toolName}/${fileName}`);
|
||||||
|
if (!filePath || !(await fileExists(filePath)))
|
||||||
|
throw new Error(`File not found: tools/${toolName}/${fileName}`);
|
||||||
|
const content = await fs.readFile(filePath, "utf-8");
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text" as const,
|
||||||
|
text: `// tools/${toolName}/${fileName}\n// ${content.split("\n").length} lines\n\n${content}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "get_command_source": {
|
||||||
|
const commandName = (args as Record<string, unknown>)
|
||||||
|
?.commandName as string;
|
||||||
|
if (!commandName) throw new Error("commandName is required");
|
||||||
|
|
||||||
|
const candidates = [
|
||||||
|
`commands/${commandName}`,
|
||||||
|
`commands/${commandName}.ts`,
|
||||||
|
`commands/${commandName}.tsx`,
|
||||||
|
];
|
||||||
|
let found: string | null = null;
|
||||||
|
let isDir = false;
|
||||||
|
for (const c of candidates) {
|
||||||
|
const abs = safePath(c);
|
||||||
|
if (abs && (await dirExists(abs))) {
|
||||||
|
found = abs;
|
||||||
|
isDir = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (abs && (await fileExists(abs))) {
|
||||||
|
found = abs;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) throw new Error(`Command not found: ${commandName}`);
|
||||||
|
|
||||||
|
if (!isDir) {
|
||||||
|
const content = await fs.readFile(found, "utf-8");
|
||||||
|
return { content: [{ type: "text" as const, text: content }] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const reqFile = (args as Record<string, unknown>)?.fileName as
|
||||||
|
| string
|
||||||
|
| undefined;
|
||||||
|
if (reqFile) {
|
||||||
|
const filePath = safePath(`commands/${commandName}/${reqFile}`);
|
||||||
|
if (!filePath || !(await fileExists(filePath)))
|
||||||
|
throw new Error(
|
||||||
|
`File not found: commands/${commandName}/${reqFile}`
|
||||||
|
);
|
||||||
|
const content = await fs.readFile(filePath, "utf-8");
|
||||||
|
return { content: [{ type: "text" as const, text: content }] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = await listDir(found);
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text" as const,
|
||||||
|
text: `Command: ${commandName}\nFiles:\n${files.map((f) => ` ${f}`).join("\n")}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "read_source_file": {
|
||||||
|
const relPath = (args as Record<string, unknown>)?.path as string;
|
||||||
|
if (!relPath) throw new Error("path is required");
|
||||||
|
const abs = safePath(relPath);
|
||||||
|
if (!abs || !(await fileExists(abs)))
|
||||||
|
throw new Error(`File not found: ${relPath}`);
|
||||||
|
const content = await fs.readFile(abs, "utf-8");
|
||||||
|
const lines = content.split("\n");
|
||||||
|
const start =
|
||||||
|
((args as Record<string, unknown>)?.startLine as number) ?? 1;
|
||||||
|
const end =
|
||||||
|
((args as Record<string, unknown>)?.endLine as number) ??
|
||||||
|
lines.length;
|
||||||
|
const slice = lines.slice(
|
||||||
|
Math.max(0, start - 1),
|
||||||
|
Math.min(lines.length, end)
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text" as const,
|
||||||
|
text: slice
|
||||||
|
.map(
|
||||||
|
(l: string, i: number) =>
|
||||||
|
`${(start + i).toString().padStart(5)} | ${l}`
|
||||||
|
)
|
||||||
|
.join("\n"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "search_source": {
|
||||||
|
const pattern = (args as Record<string, unknown>)
|
||||||
|
?.pattern as string;
|
||||||
|
if (!pattern) throw new Error("pattern is required");
|
||||||
|
const filePattern = (args as Record<string, unknown>)
|
||||||
|
?.filePattern as string | undefined;
|
||||||
|
const maxResults =
|
||||||
|
((args as Record<string, unknown>)?.maxResults as number) ?? 50;
|
||||||
|
|
||||||
|
let regex: RegExp;
|
||||||
|
try {
|
||||||
|
regex = new RegExp(pattern, "i");
|
||||||
|
} catch {
|
||||||
|
throw new Error(`Invalid regex pattern: ${pattern}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const allFiles = await walkFiles(SRC_ROOT);
|
||||||
|
const filtered = filePattern
|
||||||
|
? allFiles.filter((f) => f.endsWith(filePattern))
|
||||||
|
: allFiles;
|
||||||
|
|
||||||
|
const matches: string[] = [];
|
||||||
|
for (const file of filtered) {
|
||||||
|
if (matches.length >= maxResults) break;
|
||||||
|
const abs = path.join(SRC_ROOT, file);
|
||||||
|
let content: string;
|
||||||
|
try {
|
||||||
|
content = await fs.readFile(abs, "utf-8");
|
||||||
|
} catch {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const lines = content.split("\n");
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (matches.length >= maxResults) break;
|
||||||
|
if (regex.test(lines[i]!)) {
|
||||||
|
matches.push(`${file}:${i + 1}: ${lines[i]!.trim()}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text" as const,
|
||||||
|
text:
|
||||||
|
matches.length > 0
|
||||||
|
? `Found ${matches.length} match(es):\n\n${matches.join("\n")}`
|
||||||
|
: "No matches found.",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "list_directory": {
|
||||||
|
const relPath =
|
||||||
|
((args as Record<string, unknown>)?.path as string) ?? "";
|
||||||
|
const abs = safePath(relPath);
|
||||||
|
if (!abs || !(await dirExists(abs)))
|
||||||
|
throw new Error(`Directory not found: ${relPath}`);
|
||||||
|
const entries = await listDir(abs);
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text" as const,
|
||||||
|
text:
|
||||||
|
entries.length > 0
|
||||||
|
? entries.join("\n")
|
||||||
|
: "(empty directory)",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "get_architecture": {
|
||||||
|
const topLevel = await listDir(SRC_ROOT);
|
||||||
|
const tools = await getToolList();
|
||||||
|
const commands = await getCommandList();
|
||||||
|
|
||||||
|
const overview = `# Claude Code Architecture Overview
|
||||||
|
|
||||||
|
## Source Root
|
||||||
|
${SRC_ROOT}
|
||||||
|
|
||||||
|
## Top-Level Entries
|
||||||
|
${topLevel.map((e) => `- ${e}`).join("\n")}
|
||||||
|
|
||||||
|
## Agent Tools (${tools.length})
|
||||||
|
${tools.map((t) => `- **${t.name}** — ${t.files.length} files: ${t.files.join(", ")}`).join("\n")}
|
||||||
|
|
||||||
|
## Slash Commands (${commands.length})
|
||||||
|
${commands.map((c) => `- **${c.name}** ${c.isDirectory ? "(directory)" : "(file)"}${c.files ? ": " + c.files.join(", ") : ""}`).join("\n")}
|
||||||
|
|
||||||
|
## Key Files
|
||||||
|
- **main.tsx** — CLI entrypoint (Commander.js)
|
||||||
|
- **QueryEngine.ts** — Core LLM API caller, streaming, tool loops
|
||||||
|
- **Tool.ts** — Base tool types, schemas, permission model
|
||||||
|
- **commands.ts** — Command registry and loader
|
||||||
|
- **tools.ts** — Tool registry and loader
|
||||||
|
- **context.ts** — System/user context collection
|
||||||
|
|
||||||
|
## Core Subsystems
|
||||||
|
- **bridge/** — IDE integration (VS Code, JetBrains)
|
||||||
|
- **coordinator/** — Multi-agent orchestration
|
||||||
|
- **services/mcp/** — MCP client connections
|
||||||
|
- **services/api/** — Anthropic API client
|
||||||
|
- **plugins/** — Plugin system
|
||||||
|
- **skills/** — Skill system
|
||||||
|
- **tasks/** — Background task management
|
||||||
|
- **server/** — Server/remote mode
|
||||||
|
`;
|
||||||
|
return { content: [{ type: "text" as const, text: overview }] };
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unknown tool: ${name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Validate that SRC_ROOT exists. Exits with error if not. */
|
||||||
|
export async function validateSrcRoot(): Promise<void> {
|
||||||
|
if (!(await dirExists(SRC_ROOT))) {
|
||||||
|
console.error(
|
||||||
|
`Error: Claude Code src/ directory not found at ${SRC_ROOT}`
|
||||||
|
);
|
||||||
|
console.error(
|
||||||
|
"Set CLAUDE_CODE_SRC_ROOT environment variable to the src/ directory path."
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
74
package.json
Normal file
74
package.json
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"name": "@anthropic-ai/claude-code",
|
||||||
|
"version": "0.0.0-leaked",
|
||||||
|
"description": "Anthropic Claude Code CLI — leaked source (2026-03-31). Not an official release.",
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"main": "src/entrypoints/cli.tsx",
|
||||||
|
"bin": {
|
||||||
|
"claude": "src/entrypoints/cli.tsx"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"lint": "biome check src/",
|
||||||
|
"lint:fix": "biome check --write src/",
|
||||||
|
"format": "biome format --write src/",
|
||||||
|
"check": "biome check src/ && tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@anthropic-ai/sdk": "^0.39.0",
|
||||||
|
"@commander-js/extra-typings": "^13.1.0",
|
||||||
|
"@growthbook/growthbook": "^1.4.0",
|
||||||
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
||||||
|
"@opentelemetry/api": "^1.9.0",
|
||||||
|
"@opentelemetry/api-logs": "^0.57.0",
|
||||||
|
"@opentelemetry/core": "^1.30.0",
|
||||||
|
"@opentelemetry/sdk-logs": "^0.57.0",
|
||||||
|
"@opentelemetry/sdk-metrics": "^1.30.0",
|
||||||
|
"@opentelemetry/sdk-trace-base": "^1.30.0",
|
||||||
|
"axios": "^1.7.0",
|
||||||
|
"chalk": "^5.4.0",
|
||||||
|
"chokidar": "^4.0.0",
|
||||||
|
"cli-boxes": "^3.0.0",
|
||||||
|
"code-excerpt": "^4.0.0",
|
||||||
|
"diff": "^7.0.0",
|
||||||
|
"execa": "^9.5.0",
|
||||||
|
"figures": "^6.1.0",
|
||||||
|
"highlight.js": "^11.11.0",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"marked": "^15.0.0",
|
||||||
|
"p-map": "^7.0.0",
|
||||||
|
"picomatch": "^4.0.0",
|
||||||
|
"proper-lockfile": "^4.1.2",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"semver": "^7.6.0",
|
||||||
|
"stack-utils": "^2.0.6",
|
||||||
|
"strip-ansi": "^7.1.0",
|
||||||
|
"supports-hyperlinks": "^3.1.0",
|
||||||
|
"tree-kill": "^1.2.2",
|
||||||
|
"type-fest": "^4.30.0",
|
||||||
|
"undici": "^7.3.0",
|
||||||
|
"wrap-ansi": "^9.0.0",
|
||||||
|
"ws": "^8.18.0",
|
||||||
|
"yaml": "^2.6.0",
|
||||||
|
"zod": "^3.24.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "^1.9.0",
|
||||||
|
"@types/diff": "^7.0.0",
|
||||||
|
"@types/lodash-es": "^4.17.12",
|
||||||
|
"@types/node": "^22.10.0",
|
||||||
|
"@types/picomatch": "^3.0.0",
|
||||||
|
"@types/proper-lockfile": "^4.1.4",
|
||||||
|
"@types/react": "^19.0.0",
|
||||||
|
"@types/semver": "^7.5.8",
|
||||||
|
"@types/stack-utils": "^2.0.3",
|
||||||
|
"@types/ws": "^8.5.0",
|
||||||
|
"typescript": "^5.7.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"bun": ">=1.1.0"
|
||||||
|
},
|
||||||
|
"packageManager": "bun@1.1.0"
|
||||||
|
}
|
||||||
56
scripts/build.sh
Normal file
56
scripts/build.sh
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# build.sh — Minimal build / check script for the leaked source
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/build.sh # install + typecheck + lint
|
||||||
|
# ./scripts/build.sh install # install deps only
|
||||||
|
# ./scripts/build.sh check # typecheck + lint only
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
STEP="${1:-all}"
|
||||||
|
|
||||||
|
install_deps() {
|
||||||
|
echo "── Installing dependencies ──"
|
||||||
|
if command -v bun &>/dev/null; then
|
||||||
|
bun install
|
||||||
|
elif command -v npm &>/dev/null; then
|
||||||
|
npm install
|
||||||
|
else
|
||||||
|
echo "Error: neither bun nor npm found on PATH" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
typecheck() {
|
||||||
|
echo "── Running TypeScript type-check ──"
|
||||||
|
npx tsc --noEmit
|
||||||
|
}
|
||||||
|
|
||||||
|
lint() {
|
||||||
|
echo "── Running Biome lint ──"
|
||||||
|
npx @biomejs/biome check src/
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$STEP" in
|
||||||
|
install)
|
||||||
|
install_deps
|
||||||
|
;;
|
||||||
|
check)
|
||||||
|
typecheck
|
||||||
|
lint
|
||||||
|
;;
|
||||||
|
all)
|
||||||
|
install_deps
|
||||||
|
typecheck
|
||||||
|
lint
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown step: $STEP"
|
||||||
|
echo "Usage: $0 [install|check|all]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "── Done ──"
|
||||||
9
src/types/bun-bundle.d.ts
vendored
Normal file
9
src/types/bun-bundle.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// Type stubs for bun:bundle feature flags used throughout the codebase.
|
||||||
|
// In a real Bun build these are provided by the bundler and eliminated at compile time.
|
||||||
|
declare module 'bun:bundle' {
|
||||||
|
/**
|
||||||
|
* Returns true if the named feature flag is enabled at build time.
|
||||||
|
* Bun uses this for dead-code elimination — disabled branches are stripped entirely.
|
||||||
|
*/
|
||||||
|
export function feature(name: string): boolean
|
||||||
|
}
|
||||||
26
tsconfig.json
Normal file
26
tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"declaration": false,
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": false,
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"types": ["bun-types", "node"],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"bun:bundle": ["./src/types/bun-bundle.d.ts"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user