docs: add skill and agent operating guides for repository conventions
This commit is contained in:
132
.github/agents/agent.agent.md
vendored
Normal file
132
.github/agents/agent.agent.md
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
description: "Use when: developing, debugging, refactoring, reviewing, exploring, or explaining code in the Claude Code CLI codebase. Covers all engineering tasks including feature implementation, bug fixes, code review, architecture analysis, and codebase navigation."
|
||||
name: "Claude Code Engineer"
|
||||
tools: [read, edit, search, execute, agent, web, todo]
|
||||
---
|
||||
|
||||
You are a senior software engineer specializing in this codebase — the TypeScript source of Anthropic's Claude Code CLI. You have deep knowledge of its architecture, conventions, and patterns.
|
||||
|
||||
## Codebase Overview
|
||||
|
||||
- **Language**: TypeScript (strict mode)
|
||||
- **Runtime**: Bun
|
||||
- **Terminal UI**: React + Ink (React for CLI)
|
||||
- **CLI Framework**: Commander.js
|
||||
- **Validation**: Zod (`zod/v4`)
|
||||
- **Module Format**: ESM with `.js` extensions on all import paths
|
||||
- **Scale**: ~1,900 files, 512K+ lines
|
||||
|
||||
## Architecture
|
||||
|
||||
| Layer | Location | Purpose |
|
||||
|-------|----------|---------|
|
||||
| Entrypoint | `src/main.tsx` | CLI parser and command dispatch |
|
||||
| Commands | `src/commands/` | ~50 slash commands, each in its own directory |
|
||||
| Tools | `src/tools/` | ~40 agent tools (`buildTool()` pattern) |
|
||||
| Components | `src/components/` | ~140 Ink React components |
|
||||
| Hooks | `src/hooks/` | React hooks for state and behavior |
|
||||
| Services | `src/services/` | External integrations |
|
||||
| Bridge | `src/bridge/` | IDE integration (VS Code, JetBrains) |
|
||||
| Coordinator | `src/coordinator/` | Multi-agent orchestration |
|
||||
| Plugins | `src/plugins/` | Plugin system |
|
||||
| Skills | `src/skills/` | Skill system |
|
||||
| Types | `src/types/` | Shared type definitions |
|
||||
| Utils | `src/utils/` | Utility functions |
|
||||
| Schemas | `src/schemas/` | Zod schemas for config validation |
|
||||
| State | `src/state/` | State management |
|
||||
| Query | `src/query/`, `src/QueryEngine.ts` | LLM query pipeline and API caller |
|
||||
| Context | `src/context/`, `src/context.ts` | System/user context collection |
|
||||
|
||||
## Coding Conventions
|
||||
|
||||
### Naming
|
||||
|
||||
- **Tool files**: `PascalCase` directories and files — `BashTool/BashTool.ts`
|
||||
- **Components**: `PascalCase.tsx` — `Spinner.tsx`, `MessageResponse.tsx`
|
||||
- **Utilities**: `camelCase.ts` — `claudemd.ts`, `gitSettings.ts`
|
||||
- **Commands**: `kebab-case` directories — `commit-push-pr/`, `security-review/`
|
||||
|
||||
### Imports
|
||||
|
||||
```typescript
|
||||
// ESM — always use .js extension, even for .ts/.tsx source files
|
||||
import { Item } from './file.js'
|
||||
import type { TypeName } from './types.js'
|
||||
|
||||
// Lodash-es (individual modules, not barrel import)
|
||||
import memoize from 'lodash-es/memoize.js'
|
||||
|
||||
// Zod v4
|
||||
import { z } from 'zod/v4'
|
||||
|
||||
// Bun feature flags for conditional compilation
|
||||
import { feature } from 'bun:bundle'
|
||||
```
|
||||
|
||||
### Tool Pattern
|
||||
|
||||
Every tool follows the `buildTool()` factory:
|
||||
|
||||
```typescript
|
||||
const MyTool = buildTool({
|
||||
name: 'ToolName',
|
||||
description,
|
||||
inputSchema: lazySchema(() => z.object({ /* ... */ })),
|
||||
outputSchema: lazySchema(() => z.object({ /* ... */ })),
|
||||
async execute(input, context) { /* ... */ },
|
||||
async checkPermissions(input, context) { /* ... */ },
|
||||
getPath?(input) { /* ... */ },
|
||||
isReadOnly() { /* ... */ },
|
||||
isConcurrencySafe() { /* ... */ },
|
||||
})
|
||||
```
|
||||
|
||||
### Schemas
|
||||
|
||||
Use `lazySchema()` wrappers for deferred evaluation to avoid circular dependency issues:
|
||||
|
||||
```typescript
|
||||
const inputSchema = lazySchema(() => z.strictObject({
|
||||
path: z.string(),
|
||||
content: z.string(),
|
||||
}))
|
||||
```
|
||||
|
||||
### Patterns to Follow
|
||||
|
||||
- **Named exports** over default exports (except command/tool definitions)
|
||||
- **Memoize** expensive or repeated computations with `lodash-es/memoize.js`
|
||||
- **Functional style** — use hooks and functions, not classes
|
||||
- **Context + Provider** pattern for shared state (`useMailbox()`, `useAppState()`)
|
||||
- **Feature flags** via `feature('FLAG')` from `bun:bundle` for conditional compilation
|
||||
- **Minimal defensive coding** — validate at system boundaries, trust internal code
|
||||
- **No emojis** in output unless the user explicitly requests them
|
||||
|
||||
### Linting
|
||||
|
||||
- **ESLint** with custom rules: `no-process-exit`, `no-top-level-side-effects`
|
||||
- **Biome** for import organization
|
||||
- Respect existing `eslint-disable` and `biome-ignore` comments
|
||||
|
||||
## Constraints
|
||||
|
||||
- DO NOT add unnecessary dependencies or abstractions
|
||||
- DO NOT use `require()` — this is an ESM codebase (except inside `feature()` guards)
|
||||
- DO NOT forget `.js` extensions on relative imports
|
||||
- DO NOT use default exports unless the existing pattern in that module already does
|
||||
- DO NOT use classes for new code — prefer functional patterns with hooks
|
||||
- DO NOT add unnecessary comments, docstrings, or type annotations to unchanged code
|
||||
- DO NOT use barrel imports from lodash — import individual modules
|
||||
|
||||
## Approach
|
||||
|
||||
1. **Understand first**: Read relevant source files and trace code paths before proposing changes
|
||||
2. **Follow existing patterns**: Match the style and structure of neighboring code exactly
|
||||
3. **Minimal changes**: Only modify what is necessary — no drive-by refactors
|
||||
4. **Validate schemas**: When adding tool inputs/outputs, use `lazySchema()` + Zod strict objects
|
||||
5. **Check permissions**: New tools must implement `checkPermissions()` appropriately
|
||||
6. **Test impact**: Consider what existing code paths a change might affect
|
||||
|
||||
## Output Format
|
||||
|
||||
When explaining code, be direct and concise. Reference specific files and line numbers. When implementing changes, provide complete, working code that follows all conventions above.
|
||||
65
.github/copilot-instructions.md
vendored
Normal file
65
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
# Claude Code CLI — Agent Instructions
|
||||
|
||||
## What This Is
|
||||
|
||||
This is the **leaked source code** of Anthropic's Claude Code CLI (leaked 2026-03-31). It is a read-only reference codebase — there is no build system, no tests, and no package.json included. Treat it as a study/exploration resource.
|
||||
|
||||
- **Language**: TypeScript / TSX
|
||||
- **Runtime**: Bun (not Node.js)
|
||||
- **Terminal UI**: React + Ink (React for CLI)
|
||||
- **Scale**: ~1,900 files, 512,000+ lines across `src/`
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Pipeline
|
||||
|
||||
1. **Entrypoint** — `src/main.tsx`: Commander.js CLI parser, service initialization, lazy feature loading via Bun feature flags
|
||||
2. **Query Engine** — `src/QueryEngine.ts` (~46K lines): LLM API call handler — streaming, tool loops, thinking mode, retries, token counting
|
||||
3. **Tool System** — `src/Tool.ts` (~29K lines) + `src/tools/`: ~50 tools (BashTool, FileEditTool, AgentTool, etc.), each self-contained with input schema, permissions, and execution logic
|
||||
4. **Command System** — `src/commands.ts` (~25K lines) + `src/commands/`: ~50 slash commands (`/commit`, `/review`, `/config`, etc.)
|
||||
5. **Context** — `src/context.ts`: Collects OS, shell, git, and user context for prompt construction
|
||||
|
||||
### Key Subsystems
|
||||
|
||||
| Directory | Purpose |
|
||||
|-----------|---------|
|
||||
| `src/bridge/` | IDE integration layer (VS Code, JetBrains) — bidirectional messaging, JWT auth, session management |
|
||||
| `src/coordinator/` | Multi-agent orchestration |
|
||||
| `src/services/` | External integrations — Anthropic API, MCP, OAuth, LSP, analytics, plugins |
|
||||
| `src/hooks/` | React hooks including `toolPermission/` for per-tool permission checks |
|
||||
| `src/components/` | ~140 Ink UI components |
|
||||
| `src/plugins/` | Plugin system |
|
||||
| `src/skills/` | Skill system |
|
||||
| `src/tasks/` | Task management |
|
||||
| `src/types/` | Centralized TypeScript type definitions |
|
||||
| `src/utils/` | Shared utilities |
|
||||
| `src/schemas/` | Zod-based config schemas |
|
||||
| `src/memdir/` | Persistent memory directory |
|
||||
| `src/voice/` | Voice input |
|
||||
| `src/vim/` | Vim mode |
|
||||
| `src/buddy/` | Companion sprite (Easter egg) |
|
||||
|
||||
### Feature Flags (Dead Code Elimination)
|
||||
|
||||
Bun strips inactive code at build time via `import { feature } from 'bun:bundle'`. Notable flags: `PROACTIVE`, `KAIROS`, `BRIDGE_MODE`, `DAEMON`, `VOICE_MODE`, `AGENT_TRIGGERS`, `MONITOR_TOOL`, `COORDINATOR_MODE`.
|
||||
|
||||
## Code Style & Conventions
|
||||
|
||||
- **Imports**: ESM with explicit `.js` extensions (e.g., `from './commands.js'`)
|
||||
- **Files/Dirs**: kebab-case (`commit-push-pr.ts`, `add-dir/`)
|
||||
- **Classes/Components**: PascalCase (`BashTool`, `QueryEngine`)
|
||||
- **Functions**: camelCase (`getCommands()`, `getAllTools()`)
|
||||
- **Constants**: UPPER_SNAKE_CASE (`ALLOWED_TOOLS`, `FRAME_INTERVAL_MS`)
|
||||
- **Linter**: Biome (not ESLint). Look for `biome-ignore` directives
|
||||
- **Ant-only code**: Some imports/features are gated behind `process.env.USER_TYPE === 'ant'` (Anthropic internal)
|
||||
- **Index pattern**: Features export via `index.ts` files
|
||||
|
||||
## Navigation Tips
|
||||
|
||||
- **Find a tool**: Look in `src/tools/<ToolName>/` — each tool is a directory with its implementation, UI, and permissions co-located
|
||||
- **Find a command**: Look in `src/commands/<command-name>/` or `src/commands/<command-name>.ts`
|
||||
- **Understand permissions**: Start at `src/hooks/toolPermission/`
|
||||
- **Trace an API call**: Start at `src/QueryEngine.ts` → `src/services/api/`
|
||||
- **Understand types**: Centralized in `src/types/`, tool-specific types in `src/Tool.ts`
|
||||
- **Follow the bridge**: IDE integration starts at `src/bridge/bridgeMain.ts`
|
||||
- **MCP integration**: `src/services/mcp/`
|
||||
12
.vscode/mcp.json
vendored
Normal file
12
.vscode/mcp.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"servers": {
|
||||
"claude-code-explorer": {
|
||||
"type": "stdio",
|
||||
"command": "node",
|
||||
"args": ["${workspaceFolder}/mcp-server/dist/index.js"],
|
||||
"env": {
|
||||
"CLAUDE_CODE_SRC_ROOT": "${workspaceFolder}/src"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
README.md
28
README.md
@@ -252,6 +252,34 @@ Built-in and third-party plugins are loaded through the `plugins/` subsystem.
|
||||
|
||||
---
|
||||
|
||||
## GitPretty Setup (Per-File Pretty Commits)
|
||||
|
||||
If you want GitHub's file UI to show visually distinct commit messages per file, use the helper script in this repo:
|
||||
|
||||
```bash
|
||||
bash ./gitpretty-apply.sh .
|
||||
```
|
||||
|
||||
This will:
|
||||
|
||||
1. Clone `gitpretty` into `~/.gitpretty` (first run only)
|
||||
2. Make scripts executable
|
||||
3. Run `emoji-file-commits.sh` against this repo
|
||||
|
||||
Optional: install auto-emoji hooks for future commits:
|
||||
|
||||
```bash
|
||||
bash ./gitpretty-apply.sh . --hooks
|
||||
```
|
||||
|
||||
After running, push as usual:
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This repository archives source code that was leaked from Anthropic's npm registry on **2026-03-31**. All original source code is the property of [Anthropic](https://www.anthropic.com). Contact [nichxbt](https://www.x.com/nichxbt) for any comments.
|
||||
|
||||
218
Skill.md
Normal file
218
Skill.md
Normal file
@@ -0,0 +1,218 @@
|
||||
---
|
||||
name: claude-code-skill
|
||||
description: Development conventions and architecture guide for the Claude Code CLI repository.
|
||||
---
|
||||
|
||||
# Claude Code — Repository Skill
|
||||
|
||||
## Project Overview
|
||||
|
||||
Claude Code is Anthropic's CLI tool for interacting with Claude from the terminal. It supports file editing, shell commands, git workflows, code review, multi-agent coordination, IDE integration (VS Code, JetBrains), and Model Context Protocol (MCP).
|
||||
|
||||
**Codebase:** ~1,900 files, 512,000+ lines of TypeScript under `src/`.
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Component | Technology |
|
||||
|------------------|------------------------------------------------|
|
||||
| Language | TypeScript (strict mode, ES modules) |
|
||||
| Runtime | Bun (JSX support, `bun:bundle` feature flags) |
|
||||
| Terminal UI | React + Ink (React for CLI) |
|
||||
| CLI Parser | Commander.js (`@commander-js/extra-typings`) |
|
||||
| API Client | `@anthropic-ai/sdk` |
|
||||
| Validation | Zod v4 |
|
||||
| Linter/Formatter | Biome |
|
||||
| Analytics | GrowthBook (feature flags & A/B testing) |
|
||||
| Protocol | Model Context Protocol (MCP) |
|
||||
|
||||
## Architecture
|
||||
|
||||
### Directory Map (`src/`)
|
||||
|
||||
| Directory | Purpose |
|
||||
|------------------|-----------------------------------------------------------------|
|
||||
| `commands/` | ~50 slash commands (`/commit`, `/review`, `/config`, etc.) |
|
||||
| `tools/` | ~40 agent tools (Bash, FileRead, FileWrite, Glob, Grep, etc.) |
|
||||
| `components/` | ~140 Ink/React UI components for terminal rendering |
|
||||
| `services/` | External integrations (API, OAuth, MCP, LSP, analytics, plugins)|
|
||||
| `bridge/` | Bidirectional IDE communication layer |
|
||||
| `state/` | React context + custom store (AppState) |
|
||||
| `hooks/` | React hooks (permissions, keybindings, commands, settings) |
|
||||
| `types/` | TypeScript type definitions |
|
||||
| `utils/` | Utilities (shell, file ops, permissions, config, git) |
|
||||
| `screens/` | Full-screen UIs (Doctor, REPL, Resume, Compact) |
|
||||
| `skills/` | Bundled skills + skill loader system |
|
||||
| `plugins/` | Plugin system (marketplace + bundled plugins) |
|
||||
| `coordinator/` | Multi-agent coordination & supervisor logic |
|
||||
| `tasks/` | Task management (shell tasks, agent tasks, teammates) |
|
||||
| `context/` | React context providers (notifications, stats, FPS) |
|
||||
| `memdir/` | Persistent memory system (CLAUDE.md, user/project memory) |
|
||||
| `entrypoints/` | Initialization logic, Agent SDK, MCP entry |
|
||||
| `voice/` | Voice input/output (STT, keyterms) |
|
||||
| `vim/` | Vim mode keybinding support |
|
||||
| `schemas/` | Zod configuration schemas |
|
||||
| `keybindings/` | Keybinding configuration & resolver |
|
||||
| `migrations/` | Config migrations between versions |
|
||||
| `outputStyles/` | Output formatting & theming |
|
||||
| `query/` | Query pipeline & processing |
|
||||
| `server/` | Server/daemon mode |
|
||||
| `remote/` | Remote session handling |
|
||||
|
||||
### Key Files
|
||||
|
||||
| File | Role |
|
||||
|---------------------|-----------------------------------------------------|
|
||||
| `src/main.tsx` | CLI entry point (Commander parser, startup profiling)|
|
||||
| `src/QueryEngine.ts`| Core LLM API caller (streaming, tool-call loops) |
|
||||
| `src/Tool.ts` | Tool type definitions & `buildTool` factory |
|
||||
| `src/tools.ts` | Tool registry & presets |
|
||||
| `src/commands.ts` | Command registry |
|
||||
| `src/context.ts` | System/user context collection (git status, memory) |
|
||||
| `src/cost-tracker.ts`| Token cost tracking |
|
||||
|
||||
### Entry Points & Initialization Sequence
|
||||
|
||||
1. `src/main.tsx` — Commander CLI parser, startup profiling
|
||||
2. `src/entrypoints/init.ts` — Config, telemetry, OAuth, MDM
|
||||
3. `src/entrypoints/cli.tsx` — CLI session orchestration
|
||||
4. `src/entrypoints/mcp.ts` — MCP server mode
|
||||
5. `src/entrypoints/sdk/` — Agent SDK (programmatic API)
|
||||
6. `src/replLauncher.tsx` — REPL session launcher
|
||||
|
||||
Startup performs parallel initialization: MDM policy reads, Keychain prefetch, feature flag checks, then core init.
|
||||
|
||||
## Patterns & Conventions
|
||||
|
||||
### Tool Definition
|
||||
|
||||
Each tool lives in `src/tools/{ToolName}/` and uses `buildTool`:
|
||||
|
||||
```typescript
|
||||
export const MyTool = buildTool({
|
||||
name: 'MyTool',
|
||||
aliases: ['my_tool'],
|
||||
description: 'What this tool does',
|
||||
inputSchema: z.object({
|
||||
param: z.string(),
|
||||
}),
|
||||
async call(args, context, canUseTool, parentMessage, onProgress) {
|
||||
// Execute and return { data: result, newMessages?: [...] }
|
||||
},
|
||||
async checkPermissions(input, context) { /* Permission checks */ },
|
||||
isConcurrencySafe(input) { /* Can run in parallel? */ },
|
||||
isReadOnly(input) { /* Non-destructive? */ },
|
||||
prompt(options) { /* System prompt injection */ },
|
||||
renderToolUseMessage(input, options) { /* UI for invocation */ },
|
||||
renderToolResultMessage(content, progressMessages, options) { /* UI for result */ },
|
||||
})
|
||||
```
|
||||
|
||||
**Directory structure per tool:** `{ToolName}.ts` or `.tsx` (main), `UI.tsx` (rendering), `prompt.ts` (system prompt), plus utility files.
|
||||
|
||||
### Command Definition
|
||||
|
||||
Commands live in `src/commands/` and follow three types:
|
||||
|
||||
- **PromptCommand** — Sends a formatted prompt with injected tools (most commands)
|
||||
- **LocalCommand** — Runs in-process, returns text
|
||||
- **LocalJSXCommand** — Runs in-process, returns React JSX
|
||||
|
||||
```typescript
|
||||
const command = {
|
||||
type: 'prompt',
|
||||
name: 'my-command',
|
||||
description: 'What this command does',
|
||||
progressMessage: 'working...',
|
||||
allowedTools: ['Bash(git *)', 'FileRead(*)'],
|
||||
source: 'builtin',
|
||||
async getPromptForCommand(args, context) {
|
||||
return [{ type: 'text', text: '...' }]
|
||||
},
|
||||
} satisfies Command
|
||||
```
|
||||
|
||||
Commands are registered in `src/commands.ts` and invoked via `/command-name` in the REPL.
|
||||
|
||||
### Component Structure
|
||||
|
||||
- Functional React components with Ink primitives (`Box`, `Text`, `useInput()`)
|
||||
- Styled with Chalk for terminal colors
|
||||
- React Compiler for optimized re-renders
|
||||
- Design system primitives in `src/components/design-system/`
|
||||
|
||||
### State Management
|
||||
|
||||
- `AppState` via React context + custom store (`src/state/AppStateStore.ts`)
|
||||
- Mutable state object passed to tool contexts
|
||||
- Selector functions for derived state
|
||||
- Change observers in `src/state/onChangeAppState.ts`
|
||||
|
||||
### Permission System
|
||||
|
||||
- **Modes:** `default` (prompt per operation), `plan` (show plan, ask once), `bypassPermissions` (auto-approve), `auto` (ML classifier)
|
||||
- **Rules:** Wildcard patterns — `Bash(git *)`, `FileEdit(/src/*)`
|
||||
- Tools implement `checkPermissions()` returning `{ granted: boolean, reason?, prompt? }`
|
||||
|
||||
### Feature Flags & Build
|
||||
|
||||
Bun's `bun:bundle` feature flags enable dead-code elimination at build time:
|
||||
|
||||
```typescript
|
||||
import { feature } from 'bun:bundle'
|
||||
if (feature('PROACTIVE')) { /* proactive agent tools */ }
|
||||
```
|
||||
|
||||
Notable flags: `PROACTIVE`, `KAIROS`, `BRIDGE_MODE`, `VOICE_MODE`, `COORDINATOR_MODE`, `DAEMON`, `WORKFLOW_SCRIPTS`.
|
||||
|
||||
Some features are also gated via `process.env.USER_TYPE === 'ant'`.
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
| Element | Convention | Example |
|
||||
|-------------|---------------------|----------------------------------|
|
||||
| Files | PascalCase (exports) or kebab-case (commands) | `BashTool.tsx`, `commit-push-pr.ts` |
|
||||
| Components | PascalCase | `App.tsx`, `PromptInput.tsx` |
|
||||
| Types | PascalCase, suffix with Props/State/Context | `ToolUseContext` |
|
||||
| Hooks | `use` prefix | `useCanUseTool`, `useSettings` |
|
||||
| Constants | SCREAMING_SNAKE_CASE | `MAX_TOKENS`, `DEFAULT_TIMEOUT_MS`|
|
||||
|
||||
## Import Practices
|
||||
|
||||
- ES modules with `.js` extensions (Bun convention)
|
||||
- Lazy imports for circular dependency breaking: `const getModule = () => require('./heavy.js')`
|
||||
- Conditional imports via feature flags or `process.env`
|
||||
- `biome-ignore` markers for manual import ordering where needed
|
||||
|
||||
## Services
|
||||
|
||||
| Service | Path | Purpose |
|
||||
|--------------------|-------------------------------|-----------------------------------|
|
||||
| API | `services/api/` | Anthropic SDK client, file uploads|
|
||||
| MCP | `services/mcp/` | MCP client, tool/resource discovery|
|
||||
| OAuth | `services/oauth/` | OAuth 2.0 auth flow |
|
||||
| LSP | `services/lsp/` | Language Server Protocol manager |
|
||||
| Analytics | `services/analytics/` | GrowthBook, telemetry, events |
|
||||
| Plugins | `services/plugins/` | Plugin loader, marketplace |
|
||||
| Compact | `services/compact/` | Context compression |
|
||||
| Policy Limits | `services/policyLimits/` | Org rate limits, quota checking |
|
||||
| Remote Settings | `services/remoteManagedSettings/` | Managed settings sync (Enterprise) |
|
||||
| Token Estimation | `services/tokenEstimation.ts` | Token count estimation |
|
||||
|
||||
## Configuration
|
||||
|
||||
**Settings locations:**
|
||||
- **Global:** `~/.claude/config.json`, `~/.claude/settings.json`
|
||||
- **Project:** `.claude/config.json`, `.claude/settings.json`
|
||||
- **System:** macOS Keychain + MDM, Windows Registry + MDM
|
||||
- **Managed:** Remote sync for Enterprise users
|
||||
|
||||
## Guidelines
|
||||
|
||||
1. Read relevant source files before making changes — understand existing patterns first.
|
||||
2. Follow the tool/command/component patterns above when adding new ones.
|
||||
3. Keep edits minimal and focused — avoid unnecessary refactoring.
|
||||
4. Use Zod for all input validation at system boundaries.
|
||||
5. Gate experimental features behind `bun:bundle` feature flags or env checks.
|
||||
6. Respect the permission system — tools that modify state must implement `checkPermissions()`.
|
||||
7. Use lazy imports when adding dependencies that could create circular references.
|
||||
8. Update this file as project conventions evolve.
|
||||
33
agent.md
Normal file
33
agent.md
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: repository-agent
|
||||
description: Agent operating guide for claude-code.
|
||||
---
|
||||
|
||||
# Agent
|
||||
|
||||
## Purpose
|
||||
Define how an automated coding agent should operate in this repository.
|
||||
|
||||
## Core Rules
|
||||
- Keep changes small, targeted, and easy to review.
|
||||
- Preserve existing command behavior unless a task explicitly asks for a behavior change.
|
||||
- Favor existing patterns in `src/commands/`, `src/tools/`, and shared utility modules.
|
||||
- Avoid broad refactors while fixing localized issues.
|
||||
|
||||
## Workflow
|
||||
1. Gather context from relevant files before editing.
|
||||
2. Implement the smallest viable change.
|
||||
3. Run focused validation (type checks/tests for changed areas).
|
||||
4. Summarize what changed and any remaining risks.
|
||||
|
||||
## Code Style
|
||||
- Match existing TypeScript style and naming in nearby files.
|
||||
- Prefer explicit, readable logic over compact clever code.
|
||||
- Add brief comments only when logic is not obvious.
|
||||
|
||||
## Validation
|
||||
- Prefer targeted checks first, then broader checks if needed.
|
||||
- If validation cannot run, clearly state what was skipped and why.
|
||||
|
||||
## Notes
|
||||
- Repository conventions may evolve; update this file when team norms change.
|
||||
41
gitpretty-apply.sh
Normal file
41
gitpretty-apply.sh
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Apply gitpretty's per-file beautification so GitHub file history shows
|
||||
# readable, themed commit messages for each file.
|
||||
|
||||
REPO_PATH="${1:-.}"
|
||||
INSTALL_HOOKS="${2:-}"
|
||||
GITPRETTY_HOME="${HOME}/.gitpretty"
|
||||
|
||||
if ! command -v git >/dev/null 2>&1; then
|
||||
echo "git is required but was not found on PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "${REPO_PATH}/.git" ]; then
|
||||
echo "Target is not a git repository: ${REPO_PATH}"
|
||||
echo "Usage: $0 [repo-path] [--hooks]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "${GITPRETTY_HOME}" ]; then
|
||||
echo "Installing gitpretty into ${GITPRETTY_HOME} ..."
|
||||
git clone https://github.com/nirholas/gitpretty.git "${GITPRETTY_HOME}"
|
||||
fi
|
||||
|
||||
chmod +x "${GITPRETTY_HOME}"/*.sh "${GITPRETTY_HOME}"/scripts/*.sh
|
||||
|
||||
if [ "${INSTALL_HOOKS}" = "--hooks" ]; then
|
||||
echo "Installing gitpretty hooks in ${REPO_PATH} ..."
|
||||
(
|
||||
cd "${REPO_PATH}"
|
||||
"${GITPRETTY_HOME}"/scripts/emoji-hooks.sh install
|
||||
)
|
||||
fi
|
||||
|
||||
echo "Running per-file beautify commits in ${REPO_PATH} ..."
|
||||
"${GITPRETTY_HOME}"/emoji-file-commits.sh "${REPO_PATH}"
|
||||
|
||||
echo "Done. Review with: git -C ${REPO_PATH} log --oneline -n 20"
|
||||
119
mcp-server/README.md
Normal file
119
mcp-server/README.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Claude Code Explorer — MCP Server
|
||||
|
||||
A standalone [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server that lets any MCP-compatible client explore the Claude Code source code.
|
||||
|
||||
## What It Does
|
||||
|
||||
Exposes 7 tools and 3 resources for navigating the ~1,900-file, 512K+ line Claude Code codebase:
|
||||
|
||||
### Tools
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `list_tools` | List all 40+ agent tools (BashTool, FileEditTool, etc.) |
|
||||
| `list_commands` | List all 50+ slash commands (/commit, /review, etc.) |
|
||||
| `get_tool_source` | Read a specific tool's implementation |
|
||||
| `get_command_source` | Read a specific command's implementation |
|
||||
| `read_source_file` | Read any file from `src/` by relative path |
|
||||
| `search_source` | Regex search across the entire source tree |
|
||||
| `list_directory` | List contents of any directory under `src/` |
|
||||
| `get_architecture` | Get a full architecture overview |
|
||||
|
||||
### Resources
|
||||
|
||||
| URI | Description |
|
||||
|-----|-------------|
|
||||
| `claude-code://architecture` | README / architecture overview |
|
||||
| `claude-code://tools` | Tool registry (JSON) |
|
||||
| `claude-code://commands` | Command registry (JSON) |
|
||||
| `claude-code://source/{path}` | Any source file (template) |
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
cd mcp-server
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Claude Desktop
|
||||
|
||||
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"claude-code-explorer": {
|
||||
"command": "node",
|
||||
"args": ["/absolute/path/to/claude-code/mcp-server/dist/index.js"],
|
||||
"env": {
|
||||
"CLAUDE_CODE_SRC_ROOT": "/absolute/path/to/claude-code/src"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### VS Code (GitHub Copilot)
|
||||
|
||||
Add to `.vscode/mcp.json` in your workspace:
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"claude-code-explorer": {
|
||||
"type": "stdio",
|
||||
"command": "node",
|
||||
"args": ["${workspaceFolder}/mcp-server/dist/index.js"],
|
||||
"env": {
|
||||
"CLAUDE_CODE_SRC_ROOT": "${workspaceFolder}/src"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Cursor
|
||||
|
||||
Add to `~/.cursor/mcp.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"claude-code-explorer": {
|
||||
"command": "node",
|
||||
"args": ["/absolute/path/to/claude-code/mcp-server/dist/index.js"],
|
||||
"env": {
|
||||
"CLAUDE_CODE_SRC_ROOT": "/absolute/path/to/claude-code/src"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `CLAUDE_CODE_SRC_ROOT` | `../src` (relative to dist/) | Path to the Claude Code `src/` directory |
|
||||
|
||||
## Example Usage
|
||||
|
||||
Once connected, you can ask your AI assistant things like:
|
||||
|
||||
- "List all Claude Code tools"
|
||||
- "Show me the BashTool implementation"
|
||||
- "Search for how permissions are checked"
|
||||
- "What files are in the bridge directory?"
|
||||
- "Read the QueryEngine.ts file, lines 1-100"
|
||||
- "How does the MCP client connection work?"
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
npm run dev # Watch mode — recompile on changes
|
||||
npm run build # One-time build
|
||||
npm start # Run the server
|
||||
```
|
||||
22
mcp-server/package.json
Normal file
22
mcp-server/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "claude-code-explorer-mcp",
|
||||
"version": "1.0.0",
|
||||
"description": "MCP server for exploring the Claude Code source code",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"bin": {
|
||||
"claude-code-explorer-mcp": "dist/index.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node dist/index.js",
|
||||
"dev": "tsc --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.12.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"typescript": "^5.7.0"
|
||||
}
|
||||
}
|
||||
656
mcp-server/src/index.ts
Normal file
656
mcp-server/src/index.ts
Normal file
@@ -0,0 +1,656 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/** List immediate children of a directory (files & dirs). */
|
||||
async function listDir(dir: string): Promise<string[]> {
|
||||
try {
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
return entries.map((e: { isDirectory(): boolean; name: string }) => (e.isDirectory() ? e.name + "/" : e.name)).sort();
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/** Recursively collect all file paths under `root` (relative to root). */
|
||||
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. */
|
||||
function safePath(relPath: string): string | null {
|
||||
const resolved = path.resolve(SRC_ROOT, relPath);
|
||||
if (!resolved.startsWith(SRC_ROOT)) return null; // path traversal blocked
|
||||
return resolved;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tool & Command Metadata
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
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
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const server = new Server(
|
||||
{ name: "claude-code-explorer", version: "1.0.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),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// Source file template
|
||||
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. Provide the tool directory name (e.g. 'BashTool', 'FileEditTool').",
|
||||
inputSchema: {
|
||||
type: "object" as const,
|
||||
properties: {
|
||||
toolName: {
|
||||
type: "string",
|
||||
description:
|
||||
"Tool directory name, e.g. 'BashTool', 'FileReadTool'",
|
||||
},
|
||||
fileName: {
|
||||
type: "string",
|
||||
description:
|
||||
"Optional: specific file within the tool directory. If omitted, returns the main implementation file.",
|
||||
},
|
||||
},
|
||||
required: ["toolName"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "get_command_source",
|
||||
description:
|
||||
"Read the source code of a specific Claude Code slash command. Provide the command name (e.g. 'commit', 'review', 'mcp').",
|
||||
inputSchema: {
|
||||
type: "object" as const,
|
||||
properties: {
|
||||
commandName: {
|
||||
type: "string",
|
||||
description: "Command name, e.g. 'commit', 'review', 'mcp'",
|
||||
},
|
||||
fileName: {
|
||||
type: "string",
|
||||
description:
|
||||
"Optional: 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', 'services/mcp/types.ts'",
|
||||
},
|
||||
startLine: {
|
||||
type: "number",
|
||||
description: "Optional 1-based start line to read from.",
|
||||
},
|
||||
endLine: {
|
||||
type: "number",
|
||||
description: "Optional 1-based end line to read to.",
|
||||
},
|
||||
},
|
||||
required: ["path"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "search_source",
|
||||
description:
|
||||
"Search for a pattern (regex or plain text) across the Claude Code source code. Returns matching lines with file paths and line numbers.",
|
||||
inputSchema: {
|
||||
type: "object" as const,
|
||||
properties: {
|
||||
pattern: {
|
||||
type: "string",
|
||||
description: "Search pattern (regex supported).",
|
||||
},
|
||||
filePattern: {
|
||||
type: "string",
|
||||
description:
|
||||
"Optional glob-like filter for file extensions, e.g. '.ts', '.tsx'.",
|
||||
},
|
||||
maxResults: {
|
||||
type: "number",
|
||||
description: "Maximum number of matches to return (default: 50).",
|
||||
},
|
||||
},
|
||||
required: ["pattern"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list_directory",
|
||||
description:
|
||||
"List files and subdirectories within a directory under src/.",
|
||||
inputSchema: {
|
||||
type: "object" as const,
|
||||
properties: {
|
||||
path: {
|
||||
type: "string",
|
||||
description:
|
||||
"Relative path from src/, e.g. 'services', 'tools/BashTool'. Use '' for the root.",
|
||||
},
|
||||
},
|
||||
required: ["path"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "get_architecture",
|
||||
description:
|
||||
"Get a high-level architecture overview of Claude Code including directory structure, core systems, and key files.",
|
||||
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) {
|
||||
// ---- list_tools ----
|
||||
case "list_tools": {
|
||||
const tools = await getToolList();
|
||||
return {
|
||||
content: [{ type: "text" as const, text: JSON.stringify(tools, null, 2) }],
|
||||
};
|
||||
}
|
||||
|
||||
// ---- list_commands ----
|
||||
case "list_commands": {
|
||||
const commands = await getCommandList();
|
||||
return {
|
||||
content: [
|
||||
{ type: "text" as const, text: JSON.stringify(commands, null, 2) },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// ---- get_tool_source ----
|
||||
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) {
|
||||
// Find the main implementation file
|
||||
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}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// ---- get_command_source ----
|
||||
case "get_command_source": {
|
||||
const commandName = (args as Record<string, unknown>)
|
||||
?.commandName as string;
|
||||
if (!commandName) throw new Error("commandName is required");
|
||||
|
||||
// Try directory first, then .ts / .tsx
|
||||
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 }] };
|
||||
}
|
||||
|
||||
// Return directory listing when no specific file requested
|
||||
const files = await listDir(found);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text" as const,
|
||||
text: `Command: ${commandName}\nFiles:\n${files.map((f) => ` ${f}`).join("\n")}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// ---- read_source_file ----
|
||||
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"),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// ---- search_source ----
|
||||
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.",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// ---- list_directory ----
|
||||
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)",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// ---- get_architecture ----
|
||||
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
|
||||
- **entrypoints/mcp.ts** — Built-in MCP server entrypoint
|
||||
`;
|
||||
return { content: [{ type: "text" as const, text: overview }] };
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown tool: ${name}`);
|
||||
}
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function main() {
|
||||
// Validate source root exists
|
||||
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);
|
||||
}
|
||||
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error(`Claude Code Explorer MCP server started (src: ${SRC_ROOT})`);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error("Fatal error:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
19
mcp-server/tsconfig.json
Normal file
19
mcp-server/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "Node16",
|
||||
"moduleResolution": "Node16",
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user