feat: fix leaked source to be locally runnable

- Restore full Ink TUI startup chain (cli.tsx entry point)
- Create stub .md files for verify skill (Bun text loader hang fix)
- Create stub types for filePersistence and SDK modules
- Fix Enter key not working (modifiers-napi missing, added try-catch)
- Remove overly conservative LOCAL_RECOVERY early return
- Add README with setup instructions
- Add .env.example template
- Add bin/claude-haha entry script and preload.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
程序员阿江(Relakkes)
2026-04-01 01:30:48 +08:00
parent 5a774a2b62
commit 124912c71d
33 changed files with 5572 additions and 280 deletions
+9
View File
@@ -0,0 +1,9 @@
ANTHROPIC_AUTH_TOKEN=your_token_here
ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic
ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed
ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M2.7-highspeed
ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M2.7-highspeed
ANTHROPIC_MODEL=MiniMax-M2.7-highspeed
API_TIMEOUT_MS=3000000
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
DISABLE_TELEMETRY=1
+4
View File
@@ -0,0 +1,4 @@
.env
.env.*
!.env.example
node_modules
+131 -230
View File
@@ -1,257 +1,158 @@
# Claude Code — Leaked Source (2026-03-31) # Claude Code Haha
> **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. 基于 Claude Code 泄露源码修复的**本地可运行版本**,支持接入任意 Anthropic 兼容 API(如 MiniMax、OpenRouter 等)。
> 原始泄露源码无法直接运行。本仓库修复了启动链路中的多个阻塞问题,使完整的 Ink TUI 交互界面可以在本地工作。
## 功能
- 完整的 Ink TUI 交互界面(与官方 Claude Code 一致)
- `--print` 无头模式(脚本/CI 场景)
- 支持 MCP 服务器、插件、Skills
- 支持自定义 API 端点和模型
- 降级 Recovery CLI 模式
--- ---
## How It Leaked ## 快速开始
[Chaofan Shou (@Fried_rice)](https://x.com/Fried_rice) discovered the leak and posted it publicly: ### 1. 安装依赖
> **"Claude code source code has been leaked via a map file in their npm registry!"** 需要 [Bun](https://bun.sh) >= 1.1 和 Node.js >= 18。
>
> — [@Fried_rice, March 31, 2026](https://x.com/Fried_rice/status/2038894956459290963)
The source map file in the published npm package contained a reference to the full, unobfuscated TypeScript source, which was downloadable as a zip archive from Anthropic's R2 storage bucket. ```bash
npm install
```
### 2. 配置环境变量
复制示例文件并填入你的 API Key:
```bash
cp .env.example .env
```
编辑 `.env`
```env
# API 认证(二选一)
ANTHROPIC_API_KEY=sk-xxx # 标准 API Keyx-api-key 头)
ANTHROPIC_AUTH_TOKEN=sk-xxx # Bearer TokenAuthorization 头)
# API 端点(可选,默认 Anthropic 官方)
ANTHROPIC_BASE_URL=https://api.minimaxi.com/anthropic
# 模型配置
ANTHROPIC_MODEL=MiniMax-M2.7-highspeed
ANTHROPIC_DEFAULT_SONNET_MODEL=MiniMax-M2.7-highspeed
ANTHROPIC_DEFAULT_HAIKU_MODEL=MiniMax-M2.7-highspeed
ANTHROPIC_DEFAULT_OPUS_MODEL=MiniMax-M2.7-highspeed
# 超时(毫秒)
API_TIMEOUT_MS=3000000
# 禁用遥测和非必要网络请求
DISABLE_TELEMETRY=1
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
```
### 3. 启动
```bash
# 交互 TUI 模式(完整界面)
./bin/claude-haha
# 无头模式(单次问答)
./bin/claude-haha -p "your prompt here"
# 管道输入
echo "explain this code" | ./bin/claude-haha -p
# 查看所有选项
./bin/claude-haha --help
```
--- ---
## Overview ## 环境变量说明
Claude Code is Anthropic's official CLI tool that lets you interact with Claude directly from the terminal to perform software engineering tasks — editing files, running commands, searching codebases, managing git workflows, and more. | 变量 | 必填 | 说明 |
|------|------|------|
This repository contains the leaked `src/` directory. | `ANTHROPIC_API_KEY` | 二选一 | API Key,通过 `x-api-key` 头发送 |
| `ANTHROPIC_AUTH_TOKEN` | 二选一 | Auth Token,通过 `Authorization: Bearer` 头发送 |
- **Leaked on**: 2026-03-31 | `ANTHROPIC_BASE_URL` | 否 | 自定义 API 端点,默认 Anthropic 官方 |
- **Language**: TypeScript | `ANTHROPIC_MODEL` | 否 | 默认模型 |
- **Runtime**: Bun | `ANTHROPIC_DEFAULT_SONNET_MODEL` | 否 | Sonnet 级别模型映射 |
- **Terminal UI**: React + [Ink](https://github.com/vadimdemedes/ink) (React for CLI) | `ANTHROPIC_DEFAULT_HAIKU_MODEL` | 否 | Haiku 级别模型映射 |
- **Scale**: ~1,900 files, 512,000+ lines of code | `ANTHROPIC_DEFAULT_OPUS_MODEL` | 否 | Opus 级别模型映射 |
| `API_TIMEOUT_MS` | 否 | API 请求超时,默认 600000 (10min) |
| `DISABLE_TELEMETRY` | 否 | 设为 `1` 禁用遥测 |
| `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | 否 | 设为 `1` 禁用非必要网络请求 |
--- ---
## Directory Structure ## 降级模式
如果完整 TUI 出现问题,可以使用简化版 readline 交互模式:
```bash
CLAUDE_CODE_FORCE_RECOVERY_CLI=1 ./bin/claude-haha
```
---
## 相对于原始泄露源码的修复
泄露的源码无法直接运行,主要修复了以下问题:
| 问题 | 根因 | 修复 |
|------|------|------|
| TUI 不启动 | 入口脚本把无参数启动路由到了 recovery CLI | 恢复走 `cli.tsx` 完整入口 |
| 启动卡死 | `verify` skill 导入缺失的 `.md` 文件,Bun text loader 无限挂起 | 创建 stub `.md` 文件 |
| `--print` 卡死 | `filePersistence/types.ts` 缺失 | 创建类型桩文件 |
| `--print` 卡死 | `ultraplan/prompt.txt` 缺失 | 创建资源桩文件 |
| **Enter 键无响应** | `modifiers-napi` native 包缺失,`isModifierPressed()` 抛异常导致 `handleEnter` 中断,`onSubmit` 永远不执行 | 加 try-catch 容错 |
| setup 被跳过 | `preload.ts` 自动设置 `LOCAL_RECOVERY=1` 跳过全部初始化 | 移除默认设置 |
---
## 项目结构
``` ```
bin/claude-haha # 入口脚本
preload.ts # Bun preload(设置 MACRO 全局变量)
.env.example # 环境变量模板
src/ src/
├── main.tsx # Entrypoint (Commander.js-based CLI parser) ├── entrypoints/cli.tsx # CLI 主入口
├── commands.ts # Command registry ├── main.tsx # TUI 主逻辑(Commander.js + React/Ink
├── tools.ts # Tool registry ├── localRecoveryCli.ts # 降级 Recovery CLI
├── Tool.ts # Tool type definitions ├── setup.ts # 启动初始化
├── QueryEngine.ts # LLM query engine (core Anthropic API caller) ├── screens/REPL.tsx # 交互 REPL 界面
├── context.ts # System/user context collection ├── ink/ # Ink 终端渲染引擎
├── cost-tracker.ts # Token cost tracking ├── components/ # UI 组件
├── tools/ # Agent 工具(Bash, Edit, Grep 等)
├── commands/ # Slash command implementations (~50) ├── commands/ # 斜杠命令(/commit, /review 等)
├── tools/ # Agent tool implementations (~40) ├── skills/ # Skill 系统
├── components/ # Ink UI components (~140) ├── services/ # 服务层(API, MCP, OAuth 等)
├── hooks/ # React hooks ├── hooks/ # React hooks
── services/ # External service integrations ── utils/ # 工具函数
├── screens/ # Full-screen UIs (Doctor, REPL, Resume)
├── types/ # TypeScript type definitions
├── utils/ # Utility functions
├── bridge/ # IDE integration bridge (VS Code, JetBrains)
├── coordinator/ # Multi-agent coordinator
├── plugins/ # Plugin system
├── skills/ # Skill system
├── keybindings/ # Keybinding configuration
├── vim/ # Vim mode
├── voice/ # Voice input
├── remote/ # Remote sessions
├── server/ # Server mode
├── memdir/ # Memory directory (persistent memory)
├── tasks/ # Task management
├── state/ # State management
├── migrations/ # Config migrations
├── schemas/ # Config schemas (Zod)
├── entrypoints/ # Initialization logic
├── ink/ # Ink renderer wrapper
├── buddy/ # Companion sprite (Easter egg)
├── native-ts/ # Native TypeScript utils
├── outputStyles/ # Output styling
├── query/ # Query pipeline
└── upstreamproxy/ # Proxy configuration
``` ```
--- ---
## Core Architecture ## 技术栈
### 1. Tool System (`src/tools/`) | 类别 | 技术 |
|------|------|
Every tool Claude Code can invoke is implemented as a self-contained module. Each tool defines its input schema, permission model, and execution logic. | 运行时 | [Bun](https://bun.sh) |
| 语言 | TypeScript |
| Tool | Description | | 终端 UI | React + [Ink](https://github.com/vadimdemedes/ink) |
|---|---| | CLI 解析 | Commander.js |
| `BashTool` | Shell command execution | | API | Anthropic SDK |
| `FileReadTool` | File reading (images, PDFs, notebooks) | | 协议 | MCP, LSP |
| `FileWriteTool` | File creation / overwrite |
| `FileEditTool` | Partial file modification (string replacement) |
| `GlobTool` | File pattern matching search |
| `GrepTool` | ripgrep-based content search |
| `WebFetchTool` | Fetch URL content |
| `WebSearchTool` | Web search |
| `AgentTool` | Sub-agent spawning |
| `SkillTool` | Skill execution |
| `MCPTool` | MCP server tool invocation |
| `LSPTool` | Language Server Protocol integration |
| `NotebookEditTool` | Jupyter notebook editing |
| `TaskCreateTool` / `TaskUpdateTool` | Task creation and management |
| `SendMessageTool` | Inter-agent messaging |
| `TeamCreateTool` / `TeamDeleteTool` | Team agent management |
| `EnterPlanModeTool` / `ExitPlanModeTool` | Plan mode toggle |
| `EnterWorktreeTool` / `ExitWorktreeTool` | Git worktree isolation |
| `ToolSearchTool` | Deferred tool discovery |
| `CronCreateTool` | Scheduled trigger creation |
| `RemoteTriggerTool` | Remote trigger |
| `SleepTool` | Proactive mode wait |
| `SyntheticOutputTool` | Structured output generation |
### 2. Command System (`src/commands/`)
User-facing slash commands invoked with `/` prefix.
| Command | Description |
|---|---|
| `/commit` | Create a git commit |
| `/review` | Code review |
| `/compact` | Context compression |
| `/mcp` | MCP server management |
| `/config` | Settings management |
| `/doctor` | Environment diagnostics |
| `/login` / `/logout` | Authentication |
| `/memory` | Persistent memory management |
| `/skills` | Skill management |
| `/tasks` | Task management |
| `/vim` | Vim mode toggle |
| `/diff` | View changes |
| `/cost` | Check usage cost |
| `/theme` | Change theme |
| `/context` | Context visualization |
| `/pr_comments` | View PR comments |
| `/resume` | Restore previous session |
| `/share` | Share session |
| `/desktop` | Desktop app handoff |
| `/mobile` | Mobile app handoff |
### 3. Service Layer (`src/services/`)
| Service | Description |
|---|---|
| `api/` | Anthropic API client, file API, bootstrap |
| `mcp/` | Model Context Protocol server connection and management |
| `oauth/` | OAuth 2.0 authentication flow |
| `lsp/` | Language Server Protocol manager |
| `analytics/` | GrowthBook-based feature flags and analytics |
| `plugins/` | Plugin loader |
| `compact/` | Conversation context compression |
| `policyLimits/` | Organization policy limits |
| `remoteManagedSettings/` | Remote managed settings |
| `extractMemories/` | Automatic memory extraction |
| `tokenEstimation.ts` | Token count estimation |
| `teamMemorySync/` | Team memory synchronization |
### 4. Bridge System (`src/bridge/`)
A bidirectional communication layer connecting IDE extensions (VS Code, JetBrains) with the Claude Code CLI.
- `bridgeMain.ts` — Bridge main loop
- `bridgeMessaging.ts` — Message protocol
- `bridgePermissionCallbacks.ts` — Permission callbacks
- `replBridge.ts` — REPL session bridge
- `jwtUtils.ts` — JWT-based authentication
- `sessionRunner.ts` — Session execution management
### 5. Permission System (`src/hooks/toolPermission/`)
Checks permissions on every tool invocation. Either prompts the user for approval/denial or automatically resolves based on the configured permission mode (`default`, `plan`, `bypassPermissions`, `auto`, etc.).
### 6. Feature Flags
Dead code elimination via Bun's `bun:bundle` feature flags:
```typescript
import { feature } from 'bun:bundle'
// Inactive code is completely stripped at build time
const voiceCommand = feature('VOICE_MODE')
? require('./commands/voice/index.js').default
: null
```
Notable flags: `PROACTIVE`, `KAIROS`, `BRIDGE_MODE`, `DAEMON`, `VOICE_MODE`, `AGENT_TRIGGERS`, `MONITOR_TOOL`
---
## Key Files in Detail
### `QueryEngine.ts` (~46K lines)
The core engine for LLM API calls. Handles streaming responses, tool-call loops, thinking mode, retry logic, and token counting.
### `Tool.ts` (~29K lines)
Defines base types and interfaces for all tools — input schemas, permission models, and progress state types.
### `commands.ts` (~25K lines)
Manages registration and execution of all slash commands. Uses conditional imports to load different command sets per environment.
### `main.tsx`
Commander.js-based CLI parser + React/Ink renderer initialization. At startup, parallelizes MDM settings, keychain prefetch, and GrowthBook initialization for faster boot.
---
## Tech Stack
| Category | Technology |
|---|---|
| Runtime | [Bun](https://bun.sh) |
| Language | TypeScript (strict) |
| Terminal UI | [React](https://react.dev) + [Ink](https://github.com/vadimdemedes/ink) |
| CLI Parsing | [Commander.js](https://github.com/tj/commander.js) (extra-typings) |
| Schema Validation | [Zod v4](https://zod.dev) |
| Code Search | [ripgrep](https://github.com/BurntSushi/ripgrep) (via GrepTool) |
| Protocols | [MCP SDK](https://modelcontextprotocol.io), LSP |
| API | [Anthropic SDK](https://docs.anthropic.com) |
| Telemetry | OpenTelemetry + gRPC |
| Feature Flags | GrowthBook |
| Auth | OAuth 2.0, JWT, macOS Keychain |
---
## Notable Design Patterns
### Parallel Prefetch
Startup time is optimized by prefetching MDM settings, keychain reads, and API preconnect in parallel — before heavy module evaluation begins.
```typescript
// main.tsx — fired as side-effects before other imports
startMdmRawRead()
startKeychainPrefetch()
```
### Lazy Loading
Heavy modules (OpenTelemetry ~400KB, gRPC ~700KB) are deferred via dynamic `import()` until actually needed.
### Agent Swarms
Sub-agents are spawned via `AgentTool`, with `coordinator/` handling multi-agent orchestration. `TeamCreateTool` enables team-level parallel work.
### Skill System
Reusable workflows defined in `skills/` and executed through `SkillTool`. Users can add custom skills.
### Plugin Architecture
Built-in and third-party plugins are loaded through the `plugins/` subsystem.
--- ---
## Disclaimer ## 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). 本仓库基于 2026-03-31 从 Anthropic npm registry 泄露的 Claude Code 源码。所有原始源码版权归 [Anthropic](https://www.anthropic.com) 所有。仅供学习和研究用途。
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"
# Force recovery CLI (simple readline REPL, no Ink TUI)
if [[ "${CLAUDE_CODE_FORCE_RECOVERY_CLI:-0}" == "1" ]]; then
exec bun --env-file=.env ./src/localRecoveryCli.ts "$@"
fi
# Default: full CLI with Ink TUI
exec bun --env-file=.env ./src/entrypoints/cli.tsx "$@"
+1
View File
@@ -0,0 +1 @@
preload = ["./preload.ts"]
+4798
View File
File diff suppressed because it is too large Load Diff
+77
View File
@@ -0,0 +1,77 @@
{
"name": "claude-code-local",
"version": "999.0.0-local",
"private": true,
"type": "module",
"bin": {
"claude-haha": "./bin/claude-haha"
},
"scripts": {
"claude-haha": "bun run ./bin/claude-haha",
"start": "bun run ./bin/claude-haha"
},
"dependencies": {
"@anthropic-ai/sandbox-runtime": "^0.0.44",
"@anthropic-ai/sdk": "^0.80.0",
"@aws-sdk/client-bedrock-runtime": "^3.1020.0",
"@commander-js/extra-typings": "^14.0.0",
"@growthbook/growthbook": "^1.6.5",
"@modelcontextprotocol/sdk": "^1.29.0",
"@opentelemetry/api-logs": "^0.214.0",
"@opentelemetry/core": "^2.6.1",
"@opentelemetry/resources": "^2.6.1",
"@opentelemetry/sdk-logs": "^0.214.0",
"@opentelemetry/sdk-metrics": "^2.6.1",
"@opentelemetry/sdk-trace-base": "^2.6.1",
"@opentelemetry/semantic-conventions": "^1.40.0",
"ajv": "^8.18.0",
"asciichart": "^1.5.25",
"auto-bind": "^5.0.1",
"axios": "^1.14.0",
"bidi-js": "^1.0.3",
"chalk": "^5.6.2",
"chokidar": "^5.0.0",
"cli-boxes": "^4.0.1",
"code-excerpt": "^4.0.0",
"diff": "^8.0.4",
"emoji-regex": "^10.6.0",
"env-paths": "^4.0.0",
"execa": "^9.6.1",
"figures": "^6.1.0",
"fuse.js": "^7.1.0",
"get-east-asian-width": "^1.5.0",
"google-auth-library": "^10.6.2",
"highlight.js": "^11.11.1",
"https-proxy-agent": "^8.0.0",
"ignore": "^7.0.5",
"indent-string": "^5.0.0",
"ink": "^6.8.0",
"jsonc-parser": "^3.3.1",
"lodash-es": "^4.17.23",
"lru-cache": "^11.2.7",
"marked": "^17.0.5",
"p-map": "^7.0.4",
"picomatch": "^4.0.4",
"proper-lockfile": "^4.1.2",
"qrcode": "^1.5.4",
"react": "^19.2.4",
"react-reconciler": "^0.33.0",
"semver": "^7.7.4",
"shell-quote": "^1.8.3",
"signal-exit": "^4.1.0",
"stack-utils": "^2.0.6",
"strip-ansi": "^7.2.0",
"supports-hyperlinks": "^4.4.0",
"tree-kill": "^1.2.2",
"type-fest": "^5.5.0",
"undici": "^7.24.6",
"usehooks-ts": "^3.1.1",
"vscode-jsonrpc": "^8.2.1",
"vscode-languageserver-types": "^3.17.5",
"wrap-ansi": "^10.0.0",
"ws": "^8.20.0",
"xss": "^1.0.15",
"yaml": "^2.8.3",
"zod": "^4.3.6"
}
}
+17
View File
@@ -0,0 +1,17 @@
const version = process.env.CLAUDE_CODE_LOCAL_VERSION ?? '999.0.0-local';
const packageUrl = process.env.CLAUDE_CODE_LOCAL_PACKAGE_URL ?? 'claude-code-local';
const buildTime = process.env.CLAUDE_CODE_LOCAL_BUILD_TIME ?? new Date().toISOString();
process.env.CLAUDE_CODE_LOCAL_SKIP_REMOTE_PREFETCH ??= '1';
Object.assign(globalThis, {
MACRO: {
VERSION: version,
PACKAGE_URL: packageUrl,
NATIVE_PACKAGE_URL: packageUrl,
BUILD_TIME: buildTime,
FEEDBACK_CHANNEL: 'local',
VERSION_CHANGELOG: '',
ISSUES_EXPLAINER: '',
},
});
@@ -0,0 +1,3 @@
// Local recovery stub for missing generated SDK types.
// The leaked source tree does not include this codegen artifact.
export {}
+2
View File
@@ -0,0 +1,2 @@
// Local recovery stub for missing SDK runtime type exports.
export {}
@@ -0,0 +1,2 @@
// Local recovery stub for missing generated SDK settings types.
export {}
+2
View File
@@ -0,0 +1,2 @@
// Local recovery stub for missing SDK tool type exports.
export {}
-1
View File
@@ -1,5 +1,4 @@
import { c as _c } from "react/compiler-runtime"; import { c as _c } from "react/compiler-runtime";
import '../global.d.ts';
import React, { type PropsWithChildren, type Ref } from 'react'; import React, { type PropsWithChildren, type Ref } from 'react';
import type { Except } from 'type-fest'; import type { Except } from 'type-fest';
import type { DOMElement } from '../dom.js'; import type { DOMElement } from '../dom.js';
-1
View File
@@ -5,7 +5,6 @@ import type { DOMElement } from '../dom.js';
import { markDirty, scheduleRenderFrom } from '../dom.js'; import { markDirty, scheduleRenderFrom } from '../dom.js';
import { markCommitStart } from '../reconciler.js'; import { markCommitStart } from '../reconciler.js';
import type { Styles } from '../styles.js'; import type { Styles } from '../styles.js';
import '../global.d.ts';
import Box from './Box.js'; import Box from './Box.js';
export type ScrollBoxHandle = { export type ScrollBoxHandle = {
scrollTo: (y: number) => void; scrollTo: (y: number) => void;
+291
View File
@@ -0,0 +1,291 @@
import Anthropic from '@anthropic-ai/sdk'
import { readFileSync } from 'fs'
import { createInterface } from 'readline'
type OutputFormat = 'text' | 'json'
function printHelp(): void {
process.stdout.write(
[
'Usage: claude-haha [options] [prompt]',
'',
'Local recovery mode for this leaked source tree.',
'',
'Options:',
' -h, --help Show help',
' -v, --version Show version',
' (no args) Start local interactive mode',
' -p, --print Send a single prompt and print the result',
' --model <model> Override model',
' --system-prompt <text> Override system prompt',
' --system-prompt-file <file> Read system prompt from file',
' --append-system-prompt <text> Append to the system prompt',
' --output-format <format> text (default) or json',
'',
'Environment:',
' ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN',
' ANTHROPIC_BASE_URL',
' ANTHROPIC_MODEL',
' API_TIMEOUT_MS',
'',
].join('\n'),
)
}
function printVersion(): void {
process.stdout.write('999.0.0-local (Claude Code local recovery)\n')
}
function parseArgs(argv: string[]) {
let print = false
let model = process.env.ANTHROPIC_MODEL
let systemPrompt: string | undefined
let appendSystemPrompt: string | undefined
let outputFormat: OutputFormat = 'text'
const positional: string[] = []
for (let i = 0; i < argv.length; i++) {
const arg = argv[i]
if (!arg) continue
if (arg === '-h' || arg === '--help') {
return { command: 'help' as const }
}
if (arg === '-v' || arg === '--version' || arg === '-V') {
return { command: 'version' as const }
}
if (arg === '-p' || arg === '--print') {
print = true
continue
}
if (arg === '--bare') {
continue
}
if (arg === '--dangerously-skip-permissions') {
continue
}
if (arg === '--model') {
model = argv[++i]
continue
}
if (arg === '--system-prompt') {
systemPrompt = argv[++i]
continue
}
if (arg === '--system-prompt-file') {
const file = argv[++i]
systemPrompt = readFileSync(file!, 'utf8')
continue
}
if (arg === '--append-system-prompt') {
appendSystemPrompt = argv[++i]
continue
}
if (arg === '--output-format') {
const value = argv[++i]
if (value === 'json' || value === 'text') {
outputFormat = value
}
continue
}
if (arg.startsWith('-')) {
continue
}
positional.push(arg)
}
return {
command: 'run' as const,
print,
model,
systemPrompt,
appendSystemPrompt,
outputFormat,
prompt: positional.join(' ').trim(),
}
}
async function readPromptFromStdin(): Promise<string> {
if (process.stdin.isTTY) return ''
const chunks: Buffer[] = []
for await (const chunk of process.stdin) {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)))
}
return Buffer.concat(chunks).toString('utf8').trim()
}
function getSystemPrompt(
systemPrompt: string | undefined,
appendSystemPrompt: string | undefined,
): string | undefined {
if (systemPrompt && appendSystemPrompt) {
return `${systemPrompt}\n\n${appendSystemPrompt}`
}
return systemPrompt ?? appendSystemPrompt
}
async function run(): Promise<void> {
const parsed = parseArgs(process.argv.slice(2))
if (parsed.command === 'help') {
printHelp()
return
}
if (parsed.command === 'version') {
printVersion()
return
}
if (!parsed.print) {
await runInteractive(parsed)
return
}
const prompt = parsed.prompt || (await readPromptFromStdin())
if (!prompt) {
process.stderr.write('Error: prompt is required\n')
process.exitCode = 1
return
}
const apiKey = process.env.ANTHROPIC_API_KEY
const authToken = process.env.ANTHROPIC_AUTH_TOKEN
if (!apiKey && !authToken) {
process.stderr.write(
'Error: set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN\n',
)
process.exitCode = 1
return
}
const model =
parsed.model ||
process.env.ANTHROPIC_DEFAULT_SONNET_MODEL ||
process.env.ANTHROPIC_MODEL
if (!model) {
process.stderr.write('Error: model is required\n')
process.exitCode = 1
return
}
const client = new Anthropic({
apiKey: apiKey ?? undefined,
authToken: authToken ?? undefined,
baseURL: process.env.ANTHROPIC_BASE_URL || undefined,
timeout: parseInt(process.env.API_TIMEOUT_MS || String(600_000), 10),
maxRetries: 0,
})
const response = await client.messages.create({
model,
max_tokens: 4096,
system: getSystemPrompt(parsed.systemPrompt, parsed.appendSystemPrompt),
messages: [{ role: 'user', content: prompt }],
})
if (parsed.outputFormat === 'json') {
process.stdout.write(`${JSON.stringify(response, null, 2)}\n`)
return
}
const text = response.content
.filter(block => block.type === 'text')
.map(block => block.text)
.join('\n')
process.stdout.write(`${text}\n`)
}
async function runInteractive(parsed: {
model?: string
systemPrompt?: string
appendSystemPrompt?: string
}): Promise<void> {
const apiKey = process.env.ANTHROPIC_API_KEY
const authToken = process.env.ANTHROPIC_AUTH_TOKEN
if (!apiKey && !authToken) {
process.stderr.write(
'Error: set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN\n',
)
process.exitCode = 1
return
}
const model =
parsed.model ||
process.env.ANTHROPIC_DEFAULT_SONNET_MODEL ||
process.env.ANTHROPIC_MODEL
if (!model) {
process.stderr.write('Error: model is required\n')
process.exitCode = 1
return
}
const client = new Anthropic({
apiKey: apiKey ?? undefined,
authToken: authToken ?? undefined,
baseURL: process.env.ANTHROPIC_BASE_URL || undefined,
timeout: parseInt(process.env.API_TIMEOUT_MS || String(600_000), 10),
maxRetries: 0,
})
const system = getSystemPrompt(parsed.systemPrompt, parsed.appendSystemPrompt)
const messages: Array<{ role: 'user' | 'assistant'; content: string }> = []
const rl = createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'you> ',
})
process.stdout.write(
`Claude Haha local interactive mode\nmodel: ${model}\ncommands: /exit, /clear\n\n`,
)
rl.prompt()
for await (const line of rl) {
const input = line.trim()
if (!input) {
rl.prompt()
continue
}
if (input === '/exit' || input === '/quit') {
rl.close()
break
}
if (input === '/clear') {
messages.length = 0
process.stdout.write('history cleared\n')
rl.prompt()
continue
}
messages.push({ role: 'user', content: input })
try {
const response = await client.messages.create({
model,
max_tokens: 4096,
system,
messages,
})
const text = response.content
.filter(block => block.type === 'text')
.map(block => block.text)
.join('\n')
process.stdout.write(`claude> ${text}\n\n`)
messages.push({ role: 'assistant', content: text })
} catch (error) {
const message =
error instanceof Error ? error.message : String(error)
process.stderr.write(`error: ${message}\n`)
}
rl.prompt()
}
}
void run().catch(error => {
const message = error instanceof Error ? error.stack || error.message : String(error)
process.stderr.write(`${message}\n`)
process.exitCode = 1
})
+12 -6
View File
@@ -165,7 +165,7 @@ import { setCwd } from 'src/utils/Shell.js';
import { type ProcessedResume, processResumedConversation } from 'src/utils/sessionRestore.js'; import { type ProcessedResume, processResumedConversation } from 'src/utils/sessionRestore.js';
import { parseSettingSourcesFlag } from 'src/utils/settings/constants.js'; import { parseSettingSourcesFlag } from 'src/utils/settings/constants.js';
import { plural } from 'src/utils/stringUtils.js'; import { plural } from 'src/utils/stringUtils.js';
import { type ChannelEntry, getInitialMainLoopModel, getIsNonInteractiveSession, getSdkBetas, getSessionId, getUserMsgOptIn, setAllowedChannels, setAllowedSettingSources, setChromeFlagOverride, setClientType, setCwdState, setDirectConnectServerUrl, setFlagSettingsPath, setInitialMainLoopModel, setInlinePlugins, setIsInteractive, setKairosActive, setOriginalCwd, setQuestionPreviewFormat, setSdkBetas, setSessionBypassPermissionsMode, setSessionPersistenceDisabled, setSessionSource, setUserMsgOptIn, switchSession } from './bootstrap/state.js'; import { type ChannelEntry, getInitialMainLoopModel, getIsNonInteractiveSession, getSdkBetas, getSessionId, getUserMsgOptIn, setAllowedChannels, setAllowedSettingSources, setChromeFlagOverride, setClientType, setCwdState, setDirectConnectServerUrl, setFlagSettingsPath, setInitialMainLoopModel, setInlinePlugins, setIsInteractive, setKairosActive, setOriginalCwd, setProjectRoot, setQuestionPreviewFormat, setSdkBetas, setSessionBypassPermissionsMode, setSessionPersistenceDisabled, setSessionSource, setUserMsgOptIn, switchSession } from './bootstrap/state.js';
/* eslint-disable @typescript-eslint/no-require-imports */ /* eslint-disable @typescript-eslint/no-require-imports */
const autoModeStateModule = feature('TRANSCRIPT_CLASSIFIER') ? require('./utils/permissions/autoModeState.js') as typeof import('./utils/permissions/autoModeState.js') : null; const autoModeStateModule = feature('TRANSCRIPT_CLASSIFIER') ? require('./utils/permissions/autoModeState.js') as typeof import('./utils/permissions/autoModeState.js') : null;
@@ -973,7 +973,7 @@ async function run(): Promise<CommanderCommand> {
// If not provided but flag is present, value will be true // If not provided but flag is present, value will be true
// The actual filtering is handled in debug.ts by parsing process.argv // The actual filtering is handled in debug.ts by parsing process.argv
return true; return true;
}).addOption(new Option('-d2e, --debug-to-stderr', 'Enable debug mode (to stderr)').argParser(Boolean).hideHelp()).option('--debug-file <path>', 'Write debug logs to a specific file path (implicitly enables debug mode)', () => true).option('--verbose', 'Override verbose mode setting from config', () => true).option('-p, --print', 'Print response and exit (useful for pipes). Note: The workspace trust dialog is skipped when Claude is run with the -p mode. Only use this flag in directories you trust.', () => true).option('--bare', 'Minimal mode: skip hooks, LSP, plugin sync, attribution, auto-memory, background prefetches, keychain reads, and CLAUDE.md auto-discovery. Sets CLAUDE_CODE_SIMPLE=1. Anthropic auth is strictly ANTHROPIC_API_KEY or apiKeyHelper via --settings (OAuth and keychain are never read). 3P providers (Bedrock/Vertex/Foundry) use their own credentials. Skills still resolve via /skill-name. Explicitly provide context via: --system-prompt[-file], --append-system-prompt[-file], --add-dir (CLAUDE.md dirs), --mcp-config, --settings, --agents, --plugin-dir.', () => true).addOption(new Option('--init', 'Run Setup hooks with init trigger, then continue').hideHelp()).addOption(new Option('--init-only', 'Run Setup and SessionStart:startup hooks, then exit').hideHelp()).addOption(new Option('--maintenance', 'Run Setup hooks with maintenance trigger, then continue').hideHelp()).addOption(new Option('--output-format <format>', 'Output format (only works with --print): "text" (default), "json" (single result), or "stream-json" (realtime streaming)').choices(['text', 'json', 'stream-json'])).addOption(new Option('--json-schema <schema>', 'JSON Schema for structured output validation. ' + 'Example: {"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}').argParser(String)).option('--include-hook-events', 'Include all hook lifecycle events in the output stream (only works with --output-format=stream-json)', () => true).option('--include-partial-messages', 'Include partial message chunks as they arrive (only works with --print and --output-format=stream-json)', () => true).addOption(new Option('--input-format <format>', 'Input format (only works with --print): "text" (default), or "stream-json" (realtime streaming input)').choices(['text', 'stream-json'])).option('--mcp-debug', '[DEPRECATED. Use --debug instead] Enable MCP debug mode (shows MCP server errors)', () => true).option('--dangerously-skip-permissions', 'Bypass all permission checks. Recommended only for sandboxes with no internet access.', () => true).option('--allow-dangerously-skip-permissions', 'Enable bypassing all permission checks as an option, without it being enabled by default. Recommended only for sandboxes with no internet access.', () => true).addOption(new Option('--thinking <mode>', 'Thinking mode: enabled (equivalent to adaptive), disabled').choices(['enabled', 'adaptive', 'disabled']).hideHelp()).addOption(new Option('--max-thinking-tokens <tokens>', '[DEPRECATED. Use --thinking instead for newer models] Maximum number of thinking tokens (only works with --print)').argParser(Number).hideHelp()).addOption(new Option('--max-turns <turns>', 'Maximum number of agentic turns in non-interactive mode. This will early exit the conversation after the specified number of turns. (only works with --print)').argParser(Number).hideHelp()).addOption(new Option('--max-budget-usd <amount>', 'Maximum dollar amount to spend on API calls (only works with --print)').argParser(value => { }).addOption(new Option('--debug-to-stderr', 'Enable debug mode (to stderr)').argParser(Boolean).hideHelp()).option('--debug-file <path>', 'Write debug logs to a specific file path (implicitly enables debug mode)', () => true).option('--verbose', 'Override verbose mode setting from config', () => true).option('-p, --print', 'Print response and exit (useful for pipes). Note: The workspace trust dialog is skipped when Claude is run with the -p mode. Only use this flag in directories you trust.', () => true).option('--bare', 'Minimal mode: skip hooks, LSP, plugin sync, attribution, auto-memory, background prefetches, keychain reads, and CLAUDE.md auto-discovery. Sets CLAUDE_CODE_SIMPLE=1. Anthropic auth is strictly ANTHROPIC_API_KEY or apiKeyHelper via --settings (OAuth and keychain are never read). 3P providers (Bedrock/Vertex/Foundry) use their own credentials. Skills still resolve via /skill-name. Explicitly provide context via: --system-prompt[-file], --append-system-prompt[-file], --add-dir (CLAUDE.md dirs), --mcp-config, --settings, --agents, --plugin-dir.', () => true).addOption(new Option('--init', 'Run Setup hooks with init trigger, then continue').hideHelp()).addOption(new Option('--init-only', 'Run Setup and SessionStart:startup hooks, then exit').hideHelp()).addOption(new Option('--maintenance', 'Run Setup hooks with maintenance trigger, then continue').hideHelp()).addOption(new Option('--output-format <format>', 'Output format (only works with --print): "text" (default), "json" (single result), or "stream-json" (realtime streaming)').choices(['text', 'json', 'stream-json'])).addOption(new Option('--json-schema <schema>', 'JSON Schema for structured output validation. ' + 'Example: {"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}').argParser(String)).option('--include-hook-events', 'Include all hook lifecycle events in the output stream (only works with --output-format=stream-json)', () => true).option('--include-partial-messages', 'Include partial message chunks as they arrive (only works with --print and --output-format=stream-json)', () => true).addOption(new Option('--input-format <format>', 'Input format (only works with --print): "text" (default), or "stream-json" (realtime streaming input)').choices(['text', 'stream-json'])).option('--mcp-debug', '[DEPRECATED. Use --debug instead] Enable MCP debug mode (shows MCP server errors)', () => true).option('--dangerously-skip-permissions', 'Bypass all permission checks. Recommended only for sandboxes with no internet access.', () => true).option('--allow-dangerously-skip-permissions', 'Enable bypassing all permission checks as an option, without it being enabled by default. Recommended only for sandboxes with no internet access.', () => true).addOption(new Option('--thinking <mode>', 'Thinking mode: enabled (equivalent to adaptive), disabled').choices(['enabled', 'adaptive', 'disabled']).hideHelp()).addOption(new Option('--max-thinking-tokens <tokens>', '[DEPRECATED. Use --thinking instead for newer models] Maximum number of thinking tokens (only works with --print)').argParser(Number).hideHelp()).addOption(new Option('--max-turns <turns>', 'Maximum number of agentic turns in non-interactive mode. This will early exit the conversation after the specified number of turns. (only works with --print)').argParser(Number).hideHelp()).addOption(new Option('--max-budget-usd <amount>', 'Maximum dollar amount to spend on API calls (only works with --print)').argParser(value => {
const amount = Number(value); const amount = Number(value);
if (isNaN(amount) || amount <= 0) { if (isNaN(amount) || amount <= 0) {
throw new Error('--max-budget-usd must be a positive number greater than 0'); throw new Error('--max-budget-usd must be a positive number greater than 0');
@@ -1904,9 +1904,6 @@ async function run(): Promise<CommanderCommand> {
profileCheckpoint('action_before_setup'); profileCheckpoint('action_before_setup');
logForDebugging('[STARTUP] Running setup()...'); logForDebugging('[STARTUP] Running setup()...');
const setupStart = Date.now(); const setupStart = Date.now();
const {
setup
} = await import('./setup.js');
const messagingSocketPath = feature('UDS_INBOX') ? (options as { const messagingSocketPath = feature('UDS_INBOX') ? (options as {
messagingSocketPath?: string; messagingSocketPath?: string;
}).messagingSocketPath : undefined; }).messagingSocketPath : undefined;
@@ -1924,7 +1921,16 @@ async function run(): Promise<CommanderCommand> {
initBuiltinPlugins(); initBuiltinPlugins();
initBundledSkills(); initBundledSkills();
} }
const setupPromise = setup(preSetupCwd, permissionMode, allowDangerouslySkipPermissions, worktreeEnabled, worktreeName, tmuxEnabled, sessionId ? validateUuid(sessionId) : undefined, worktreePRNumber, messagingSocketPath); const setupPromise = process.env.CLAUDE_CODE_LOCAL_RECOVERY === '1' ? (async () => {
setOriginalCwd(preSetupCwd);
setProjectRoot(preSetupCwd);
logForDebugging('[STARTUP] setup() skipped in local recovery mode');
})() : (async () => {
const {
setup
} = await import('./setup.js');
await setup(preSetupCwd, permissionMode, allowDangerouslySkipPermissions, worktreeEnabled, worktreeName, tmuxEnabled, sessionId ? validateUuid(sessionId) : undefined, worktreePRNumber, messagingSocketPath);
})();
const commandsPromise = worktreeEnabled ? null : getCommands(preSetupCwd); const commandsPromise = worktreeEnabled ? null : getCommands(preSetupCwd);
const agentDefsPromise = worktreeEnabled ? null : getAgentDefinitionsWithOverrides(preSetupCwd); const agentDefsPromise = worktreeEnabled ? null : getAgentDefinitionsWithOverrides(preSetupCwd);
// Suppress transient unhandledRejection if these reject during the // Suppress transient unhandledRejection if these reject during the
+4
View File
@@ -165,6 +165,10 @@ function computeChecksum(
* getSettings() to avoid circular dependencies during settings loading. * getSettings() to avoid circular dependencies during settings loading.
*/ */
export function isPolicyLimitsEligible(): boolean { export function isPolicyLimitsEligible(): boolean {
if (process.env.CLAUDE_CODE_LOCAL_SKIP_REMOTE_PREFETCH === '1') {
return false
}
// 3p provider users should not hit the policy limits endpoint // 3p provider users should not hit the policy limits endpoint
if (getAPIProvider() !== 'firstParty') { if (getAPIProvider() !== 'firstParty') {
return false return false
@@ -49,6 +49,10 @@ export function resetSyncCache(): void {
export function isRemoteManagedSettingsEligible(): boolean { export function isRemoteManagedSettingsEligible(): boolean {
if (cached !== undefined) return cached if (cached !== undefined) return cached
if (process.env.CLAUDE_CODE_LOCAL_SKIP_REMOTE_PREFETCH === '1') {
return (cached = setEligibility(false))
}
// 3p provider users should not hit the settings endpoint // 3p provider users should not hit the settings endpoint
if (getAPIProvider() !== 'firstParty') { if (getAPIProvider() !== 'firstParty') {
return (cached = setEligibility(false)) return (cached = setEligibility(false))
+10
View File
@@ -159,6 +159,16 @@ export async function setup(
// IMPORTANT: setCwd() must be called before any other code that depends on the cwd // IMPORTANT: setCwd() must be called before any other code that depends on the cwd
setCwd(cwd) setCwd(cwd)
setOriginalCwd(cwd)
setProjectRoot(cwd)
// Local recovery mode: when CLAUDE_CODE_LOCAL_RECOVERY=1 is explicitly set,
// trim startup to minimum. Otherwise run full setup for the Ink TUI.
if (process.env.CLAUDE_CODE_LOCAL_RECOVERY === '1') {
process.stderr.write('[local-recovery] setup early return\n')
profileCheckpoint('setup_local_recovery_early_return')
return
}
// Capture hooks configuration snapshot to avoid hidden hook modifications. // Capture hooks configuration snapshot to avoid hidden hook modifications.
// IMPORTANT: Must be called AFTER setCwd() so hooks are loaded from the correct directory // IMPORTANT: Must be called AFTER setCwd() so hooks are loaded from the correct directory
+13 -34
View File
@@ -1,16 +1,5 @@
import { feature } from 'bun:bundle' import { feature } from 'bun:bundle'
import { shouldAutoEnableClaudeInChrome } from 'src/utils/claudeInChrome/setup.js' import { shouldAutoEnableClaudeInChrome } from 'src/utils/claudeInChrome/setup.js'
import { registerBatchSkill } from './batch.js'
import { registerClaudeInChromeSkill } from './claudeInChrome.js'
import { registerDebugSkill } from './debug.js'
import { registerKeybindingsSkill } from './keybindings.js'
import { registerLoremIpsumSkill } from './loremIpsum.js'
import { registerRememberSkill } from './remember.js'
import { registerSimplifySkill } from './simplify.js'
import { registerSkillifySkill } from './skillify.js'
import { registerStuckSkill } from './stuck.js'
import { registerUpdateConfigSkill } from './updateConfig.js'
import { registerVerifySkill } from './verify.js'
/** /**
* Initialize all bundled skills. * Initialize all bundled skills.
@@ -22,58 +11,48 @@ import { registerVerifySkill } from './verify.js'
* 3. Import and call that function here * 3. Import and call that function here
*/ */
export function initBundledSkills(): void { export function initBundledSkills(): void {
registerUpdateConfigSkill() /* eslint-disable @typescript-eslint/no-require-imports */
registerKeybindingsSkill() require('./updateConfig.js').registerUpdateConfigSkill()
registerVerifySkill() require('./keybindings.js').registerKeybindingsSkill()
registerDebugSkill() require('./verify.js').registerVerifySkill()
registerLoremIpsumSkill() require('./debug.js').registerDebugSkill()
registerSkillifySkill() require('./loremIpsum.js').registerLoremIpsumSkill()
registerRememberSkill() require('./skillify.js').registerSkillifySkill()
registerSimplifySkill() require('./remember.js').registerRememberSkill()
registerBatchSkill() require('./simplify.js').registerSimplifySkill()
registerStuckSkill() require('./batch.js').registerBatchSkill()
require('./stuck.js').registerStuckSkill()
if (feature('KAIROS') || feature('KAIROS_DREAM')) { if (feature('KAIROS') || feature('KAIROS_DREAM')) {
/* eslint-disable @typescript-eslint/no-require-imports */
const { registerDreamSkill } = require('./dream.js') const { registerDreamSkill } = require('./dream.js')
/* eslint-enable @typescript-eslint/no-require-imports */
registerDreamSkill() registerDreamSkill()
} }
if (feature('REVIEW_ARTIFACT')) { if (feature('REVIEW_ARTIFACT')) {
/* eslint-disable @typescript-eslint/no-require-imports */
const { registerHunterSkill } = require('./hunter.js') const { registerHunterSkill } = require('./hunter.js')
/* eslint-enable @typescript-eslint/no-require-imports */
registerHunterSkill() registerHunterSkill()
} }
if (feature('AGENT_TRIGGERS')) { if (feature('AGENT_TRIGGERS')) {
/* eslint-disable @typescript-eslint/no-require-imports */
const { registerLoopSkill } = require('./loop.js') const { registerLoopSkill } = require('./loop.js')
/* eslint-enable @typescript-eslint/no-require-imports */
// /loop's isEnabled delegates to isKairosCronEnabled() — same lazy // /loop's isEnabled delegates to isKairosCronEnabled() — same lazy
// per-invocation pattern as the cron tools. Registered unconditionally; // per-invocation pattern as the cron tools. Registered unconditionally;
// the skill's own isEnabled callback decides visibility. // the skill's own isEnabled callback decides visibility.
registerLoopSkill() registerLoopSkill()
} }
if (feature('AGENT_TRIGGERS_REMOTE')) { if (feature('AGENT_TRIGGERS_REMOTE')) {
/* eslint-disable @typescript-eslint/no-require-imports */
const { const {
registerScheduleRemoteAgentsSkill, registerScheduleRemoteAgentsSkill,
} = require('./scheduleRemoteAgents.js') } = require('./scheduleRemoteAgents.js')
/* eslint-enable @typescript-eslint/no-require-imports */
registerScheduleRemoteAgentsSkill() registerScheduleRemoteAgentsSkill()
} }
if (feature('BUILDING_CLAUDE_APPS')) { if (feature('BUILDING_CLAUDE_APPS')) {
/* eslint-disable @typescript-eslint/no-require-imports */
const { registerClaudeApiSkill } = require('./claudeApi.js') const { registerClaudeApiSkill } = require('./claudeApi.js')
/* eslint-enable @typescript-eslint/no-require-imports */
registerClaudeApiSkill() registerClaudeApiSkill()
} }
if (shouldAutoEnableClaudeInChrome()) { if (shouldAutoEnableClaudeInChrome()) {
registerClaudeInChromeSkill() require('./claudeInChrome.js').registerClaudeInChromeSkill()
} }
if (feature('RUN_SKILL_GENERATOR')) { if (feature('RUN_SKILL_GENERATOR')) {
/* eslint-disable @typescript-eslint/no-require-imports */
const { registerRunSkillGeneratorSkill } = require('./runSkillGenerator.js') const { registerRunSkillGeneratorSkill } = require('./runSkillGenerator.js')
/* eslint-enable @typescript-eslint/no-require-imports */
registerRunSkillGeneratorSkill() registerRunSkillGeneratorSkill()
} }
/* eslint-enable @typescript-eslint/no-require-imports */
} }
+6
View File
@@ -0,0 +1,6 @@
---
name: verify
description: Verify a code change does what it should by running the app.
---
Verify the code change works as expected.
@@ -0,0 +1 @@
# CLI verification example
@@ -0,0 +1 @@
# Server verification example
@@ -0,0 +1,5 @@
import React from 'react'
export function TungstenLiveMonitor(): React.JSX.Element | null {
return null
}
+43
View File
@@ -0,0 +1,43 @@
import { z } from 'zod/v4'
const inputSchema = z.object({}).passthrough()
export const TungstenTool = {
name: 'tungsten',
aliases: [],
maxResultSizeChars: 0,
inputSchema,
async description() {
return 'Unavailable in this local recovery build.'
},
async prompt() {
return 'TungstenTool is unavailable in this local recovery build.'
},
async call() {
return {
data: {
success: false,
error: 'TungstenTool is unavailable in this local recovery build.',
},
}
},
isConcurrencySafe() {
return true
},
isEnabled() {
return false
},
isReadOnly() {
return true
},
async checkPermissions() {
return {
behavior: 'deny' as const,
message: 'TungstenTool is unavailable in this local recovery build.',
}
},
}
export function clearSessionsWithTungstenUsage(): void {}
export function resetInitializationState(): void {}
+1
View File
@@ -0,0 +1 @@
export const WORKFLOW_TOOL_NAME = 'workflow'
+20
View File
@@ -0,0 +1,20 @@
export type ConnectorTextBlock = {
type: 'connector_text';
text?: string;
};
export type ConnectorTextDelta = {
type: 'connector_text_delta';
text?: string;
};
export function isConnectorTextBlock(
value: unknown,
): value is ConnectorTextBlock {
return (
typeof value === 'object' &&
value !== null &&
'type' in value &&
(value as { type?: unknown }).type === 'connector_text'
);
}
+22
View File
@@ -0,0 +1,22 @@
// Local recovery stub for missing filePersistence types
export const DEFAULT_UPLOAD_CONCURRENCY = 5
export const FILE_COUNT_LIMIT = 100
export const OUTPUTS_SUBDIR = 'outputs'
export interface FailedPersistence {
filePath: string
error: string
}
export interface PersistedFile {
filePath: string
fileId?: string
}
export interface FilesPersistedEventData {
persisted: PersistedFile[]
failed: FailedPersistence[]
}
export type TurnStartTime = number
+9 -5
View File
@@ -28,9 +28,13 @@ export function isModifierPressed(modifier: ModifierKey): boolean {
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
return false return false
} }
// Dynamic import to avoid loading native module at top level try {
const { isModifierPressed: nativeIsModifierPressed } = // Dynamic import to avoid loading native module at top level
// eslint-disable-next-line @typescript-eslint/no-require-imports const { isModifierPressed: nativeIsModifierPressed } =
require('modifiers-napi') as { isModifierPressed: (m: string) => boolean } // eslint-disable-next-line @typescript-eslint/no-require-imports
return nativeIsModifierPressed(modifier) require('modifiers-napi') as { isModifierPressed: (m: string) => boolean }
return nativeIsModifierPressed(modifier)
} catch {
return false
}
} }
+1
View File
@@ -0,0 +1 @@
You are a planning assistant.
+24
View File
@@ -0,0 +1,24 @@
export type PermissionMode =
| 'ask'
| 'skip_all_permission_checks'
| 'follow_a_plan';
export type Logger = {
silly?: (...args: unknown[]) => void;
debug?: (...args: unknown[]) => void;
info?: (...args: unknown[]) => void;
warn?: (...args: unknown[]) => void;
error?: (...args: unknown[]) => void;
};
export type ClaudeForChromeContext = Record<string, unknown>;
export const BROWSER_TOOLS: Array<{ name: string }> = [];
export function createClaudeForChromeMcpServer(
_context: ClaudeForChromeContext,
) {
return {
async connect(_transport: unknown): Promise<void> {},
};
}
+19
View File
@@ -0,0 +1,19 @@
export type SyntaxTheme = {
name: string;
};
export class ColorDiff {
format(input: string): string {
return input;
}
}
export class ColorFile {
format(input: string): string {
return input;
}
}
export function getSyntaxTheme(themeName: string): SyntaxTheme {
return { name: themeName };
}
+24
View File
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"allowJs": true,
"jsx": "react-jsx",
"baseUrl": ".",
"paths": {
"@ant/claude-for-chrome-mcp": [
"./stubs/ant-claude-for-chrome-mcp.ts"
],
"color-diff-napi": [
"./stubs/color-diff-napi.ts"
],
"src/*": [
"./src/*"
]
},
"types": [
"bun-types"
]
}
}