mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 21:05:51 +00:00
Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ed0e63095 | ||
|
|
0201db55ac | ||
|
|
7a9f53b63f | ||
|
|
dbc8a85cd7 | ||
|
|
3b3e4fb1ea | ||
|
|
6607b13364 | ||
|
|
cc09c304ec | ||
|
|
3c2e046bf9 | ||
|
|
bd6417c715 | ||
|
|
4bf9f04a4d | ||
|
|
c0f7735110 | ||
|
|
f9d011164a | ||
|
|
481e2a58a9 | ||
|
|
c5edee431f | ||
|
|
a57ca08566 | ||
|
|
6536757428 | ||
|
|
a0dc4540ca | ||
|
|
7e4df5c3e9 | ||
|
|
4d939e5722 | ||
|
|
2e9aaf4993 | ||
|
|
34154ee3f5 | ||
|
|
29cc74a170 | ||
|
|
d2b66d9d2c | ||
|
|
d70e7f7f05 | ||
|
|
72a2093cd6 | ||
|
|
b5c299f5d2 | ||
|
|
ac42ce2d67 | ||
|
|
c659912517 | ||
|
|
a14b7f352b | ||
|
|
c5ab83a3fc | ||
|
|
03b7f9b453 | ||
|
|
bddd146f25 | ||
|
|
c8d08d235b | ||
|
|
a02dc0bded | ||
|
|
3cb1e50b25 | ||
|
|
cfab161e28 | ||
|
|
90027279e6 | ||
|
|
3470783ced | ||
|
|
8169b96250 | ||
|
|
fe08cacf8d | ||
|
|
5a4c820e1d | ||
|
|
1a4e9702c2 | ||
|
|
2273a0bcfe | ||
|
|
b80483c23e | ||
|
|
8442aaadd2 | ||
|
|
dad3ad2b8d | ||
|
|
b5b81dfe49 | ||
|
|
ecbd5a93e4 | ||
|
|
be80da4ce0 | ||
|
|
fce40fed1f | ||
|
|
a7e03a5b30 | ||
|
|
05cabbbd73 | ||
|
|
d4b30d32c3 | ||
|
|
e0484e2817 | ||
|
|
2fb1c9dcd8 | ||
|
|
bbb8b613a9 | ||
|
|
c63b875ae3 | ||
|
|
9b8503d13d | ||
|
|
3cf94fbda0 | ||
|
|
9a3081dff6 | ||
|
|
bd6448ecda | ||
|
|
1071270ce3 | ||
|
|
711440474c | ||
|
|
8399d9ed20 | ||
|
|
513ccc3003 |
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: macos-latest
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -23,8 +23,16 @@ jobs:
|
||||
- name: Type check
|
||||
run: bunx tsc --noEmit
|
||||
|
||||
- name: Test
|
||||
run: bun test
|
||||
- name: Test with Coverage
|
||||
run: |
|
||||
set -o pipefail
|
||||
bun test --coverage --coverage-reporter=lcov 2>&1 | grep -vE '^\s*(\(pass\)|\(skip\))' | sed '/^.*\/__tests__\/.*:$/d' | cat -s
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
file: ./coverage/lcov.info
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
- name: Build
|
||||
run: bun run build
|
||||
run: bun run build:vite
|
||||
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -13,9 +13,8 @@ src/utils/vendor/
|
||||
# AI tool runtime directories
|
||||
.agents/
|
||||
.claude/
|
||||
.codex/
|
||||
.omx/
|
||||
|
||||
.docs/task/
|
||||
# Binary / screenshot files (root only)
|
||||
/*.png
|
||||
*.bmp
|
||||
@@ -30,3 +29,12 @@ __pycache__/
|
||||
logs
|
||||
|
||||
data
|
||||
.omc
|
||||
.codex/*
|
||||
!.codex/agents/
|
||||
!.codex/agents/**
|
||||
!.codex/skills/
|
||||
!.codex/skills/**
|
||||
.codex/skills/.system/**
|
||||
!.codex/prompts/
|
||||
!.codex/prompts/**
|
||||
|
||||
78
.impeccable.md
Normal file
78
.impeccable.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Impeccable Design Context
|
||||
|
||||
## Users
|
||||
|
||||
**Primary**: Technical teams and enterprises using AI-assisted coding in production workflows.
|
||||
- DevOps engineers managing remote agents via RCS dashboard
|
||||
- Development teams collaborating through shared sessions
|
||||
- Individual developers using terminal CLI daily
|
||||
|
||||
**Context**: Used during focused work sessions — debugging, code review, agent orchestration. Users are in "get things done" mode, not browsing. They value efficiency but also appreciate warmth and personality.
|
||||
|
||||
**Job to be done**: Make advanced AI coding tools accessible and controllable, especially features that normally require enterprise accounts or Anthropic OAuth.
|
||||
|
||||
## Brand Personality
|
||||
|
||||
**3 words**: Warm, Considered, Human
|
||||
|
||||
**Voice**: Like a knowledgeable colleague who's genuinely enthusiastic about the craft — not a corporate product manager. Community-first, open, slightly playful. Chinese developer community culture (贴吧/discord 温暖氛围).
|
||||
|
||||
**Emotional goals**: Confidence (this tool is solid), Warmth (this community is welcoming), Delight (small moments of personality make the difference).
|
||||
|
||||
**References**:
|
||||
- **Anthropic's own design language** — their clean, considered aesthetic with warm undertones. The terra cotta/burnt orange as a human accent. Lots of breathing room. Typography-forward.
|
||||
- **NOT**: Generic AI product (no ChatGPT blue, no gradient text, no "AI slop"). NOT corporate SaaS (no Salesforce-blue dashboards, no enterprise sterility).
|
||||
|
||||
**Anti-references**: Corporate enterprise dashboards, generic AI product pages, anything that looks like it was "designed by committee."
|
||||
|
||||
## Aesthetic Direction
|
||||
|
||||
**Theme**: Light + Dark dual mode (user/system preference switch)
|
||||
|
||||
**Tone**: Anthropomorphic warmth meets terminal precision. The brand orange (Claude's terra cotta) is the thread that ties everything together — it's the human element in a technical world.
|
||||
|
||||
**Typography**: Clean, considered, with good hierarchy. Terminal-native for CLI; modern web fonts for Web UI (RCS dashboard, docs). Favor readability and personality.
|
||||
|
||||
**Color**:
|
||||
- Primary: Claude orange family (`#D77757` / terra cotta)
|
||||
- Accent: Warm neutrals tinted toward orange
|
||||
- Semantic: Success/Error/Warning following Anthropic's established palette
|
||||
- Dark mode: Warm dark surfaces (not cold blue-black)
|
||||
|
||||
**Differentiation**: The CCB brand sits at the intersection of "serious tool" and "community project." It should feel like Anthropic's design principles applied to an open-source context — less corporate polish, more human craft. The mascot "Clawd" and the playful "踩踩背" naming hint at personality that the design should honor.
|
||||
|
||||
**Scope**: All Web UI — RCS control panel, documentation site, landing pages.
|
||||
|
||||
## Design Principles
|
||||
|
||||
1. **Considered over clever** — Every design choice should feel intentional, not trendy. If it doesn't serve the user, it doesn't ship.
|
||||
2. **Warmth through subtlety** — Orange tints on neutrals, breathing room in layouts, personality in copy. Not giant emoji or aggressive color.
|
||||
3. **Density with clarity** — Technical users need information density, but not chaos. Every pixel earns its place.
|
||||
4. **Community voice** — The design should feel like it was made by people who use it, not by a distant design team. Slightly rough edges are fine if they're honest.
|
||||
5. **Anthropic's shadow** — When in doubt, follow Anthropic's design instincts — the clean layouts, the generous spacing, the warm color temperature. Then add the community touch.
|
||||
|
||||
## Existing Design Assets
|
||||
|
||||
### Brand Colors (from theme system)
|
||||
- Claude Orange: `rgb(215,119,87)` / `#D77757`
|
||||
- Claude Blue: `rgb(87,105,247)` / `#5769F7`
|
||||
- Permission Blue: `rgb(87,105,247)`
|
||||
- Auto Accept Violet: `rgb(135,0,255)`
|
||||
- Plan Mode Teal: `rgb(0,102,102)`
|
||||
- Success: `rgb(78,186,101)`
|
||||
- Error: `rgb(255,107,128)`
|
||||
- Warning: `rgb(255,193,7)`
|
||||
|
||||
### Logo
|
||||
- CCB text + orange play button icon
|
||||
- Dark/Light SVG variants in `docs/logo/`
|
||||
- Favicon: Orange circle `#D97706` with white play triangle
|
||||
|
||||
### Mascot
|
||||
- "Clawd" — terminal-art character with multiple poses
|
||||
- Theme-aware coloring
|
||||
|
||||
### Theme System
|
||||
- 7 variants: dark, light, dark-ansi, light-ansi, dark-daltonized, light-daltonized, auto
|
||||
- 89+ semantic color tokens
|
||||
- Full documentation in `packages/@ant/ink/docs/04-theme-system.md`
|
||||
115
CLAUDE.md
115
CLAUDE.md
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Project Overview
|
||||
|
||||
This is a **reverse-engineered / decompiled** version of Anthropic's official Claude Code CLI tool. The goal is to restore core functionality while trimming secondary capabilities. Many modules are stubbed or feature-flagged off. TypeScript strict mode is enforced — **`bunx tsc --noEmit` must pass with zero errors**.
|
||||
This is a **reverse-engineered / decompiled** version of Anthropic's official Claude Code CLI tool. The goal is to restore core functionality while trimming secondary capabilities. Many modules are stubbed or feature-flagged off. TypeScript strict mode is enforced(见 Working with This Codebase 段的 tsc 要求)。
|
||||
|
||||
## Git Commit Message Convention
|
||||
|
||||
@@ -39,8 +39,11 @@ echo "say hello" | bun run src/entrypoints/cli.tsx -p
|
||||
# Build (code splitting, outputs dist/cli.js + chunk files)
|
||||
bun run build
|
||||
|
||||
# Build with Vite (alternative build pipeline)
|
||||
bun run build:vite
|
||||
|
||||
# Test
|
||||
bun test # run all tests (2453 tests / 137 files / 0 fail)
|
||||
bun test # run all tests (3175 tests / 207 files / 0 fail)
|
||||
bun test src/utils/__tests__/hash.test.ts # run single file
|
||||
bun test --coverage # with coverage report
|
||||
|
||||
@@ -55,6 +58,8 @@ bun run health
|
||||
# Check unused exports
|
||||
bun run check:unused
|
||||
|
||||
bun run typecheck
|
||||
|
||||
# Remote Control Server
|
||||
bun run rcs
|
||||
|
||||
@@ -72,14 +77,14 @@ bun run docs:dev
|
||||
- **Build**: `build.ts` 执行 `Bun.build()` with `splitting: true`,入口 `src/entrypoints/cli.tsx`,输出 `dist/cli.js` + chunk files。Build 默认启用 19 个 feature(见下方 Feature Flag 段)。构建后自动替换 `import.meta.require` 为 Node.js 兼容版本(产物 bun/node 都可运行)。
|
||||
- **Dev mode**: `scripts/dev.ts` 通过 Bun `-d` flag 注入 `MACRO.*` defines,运行 `src/entrypoints/cli.tsx`。默认启用全部 feature。
|
||||
- **Module system**: ESM (`"type": "module"`), TSX with `react-jsx` transform.
|
||||
- **Monorepo**: Bun workspaces — 14 个 internal packages in `packages/` resolved via `workspace:*`。
|
||||
- **Monorepo**: Bun workspaces — 15 个 workspace packages + 若干辅助目录 in `packages/` resolved via `workspace:*`。
|
||||
- **Lint/Format**: Biome (`biome.json`)。`bun run lint` / `bun run lint:fix` / `bun run format`。
|
||||
- **Defines**: 集中管理在 `scripts/defines.ts`。当前版本 `2.1.888`。
|
||||
- **CI**: GitHub Actions — `ci.yml`(构建+测试)、`release-rcs.yml`(RCS 发布)、`update-contributors.yml`(自动更新贡献者)。
|
||||
|
||||
### Entry & Bootstrap
|
||||
|
||||
1. **`src/entrypoints/cli.tsx`** (323 行) — True entrypoint。`main()` 函数按优先级处理多条快速路径:
|
||||
1. **`src/entrypoints/cli.tsx`** (373 行) — True entrypoint。`main()` 函数按优先级处理多条快速路径:
|
||||
- `--version` / `-v` — 零模块加载
|
||||
- `--dump-system-prompt` — feature-gated (DUMP_SYSTEM_PROMPT)
|
||||
- `--claude-in-chrome-mcp` / `--chrome-native-host`
|
||||
@@ -92,7 +97,7 @@ bun run docs:dev
|
||||
- `environment-runner` / `self-hosted-runner` — BYOC runner
|
||||
- `--tmux` + `--worktree` 组合
|
||||
- 默认路径:加载 `main.tsx` 启动完整 CLI
|
||||
2. **`src/main.tsx`** (~6970 行) — Commander.js CLI definition。注册大量 subcommands:`mcp` (serve/add/remove/list...)、`server`、`ssh`、`open`、`auth`、`plugin`、`agents`、`auto-mode`、`doctor`、`update` 等。主 `.action()` 处理器负责权限、MCP、会话恢复、REPL/Headless 模式分发。
|
||||
2. **`src/main.tsx`** (~6981 行) — Commander.js CLI definition。注册大量 subcommands:`mcp` (serve/add/remove/list...)、`server`、`ssh`、`open`、`auth`、`plugin`、`agents`、`auto-mode`、`doctor`、`update` 等。主 `.action()` 处理器负责权限、MCP、会话恢复、REPL/Headless 模式分发。
|
||||
3. **`src/entrypoints/init.ts`** — One-time initialization (telemetry, config, trust dialog)。
|
||||
|
||||
### Core Loop
|
||||
@@ -110,8 +115,8 @@ bun run docs:dev
|
||||
### Tool System
|
||||
|
||||
- **`src/Tool.ts`** — Tool interface definition (`Tool` type) and utilities (`findToolByName`, `toolMatchesName`).
|
||||
- **`src/tools.ts`** (387 行) — Tool registry. Assembles the tool list; some tools are conditionally loaded via `feature()` flags or `process.env.USER_TYPE`.
|
||||
- **`src/tools/<ToolName>/`** — 55 个 tool 目录。主要分类:
|
||||
- **`src/tools.ts`** (392 行) — Tool registry. Assembles the tool list; tools are imported from `@claude-code-best/builtin-tools` package. Some tools are conditionally loaded via `feature()` flags or `process.env.USER_TYPE`.
|
||||
- **`packages/builtin-tools/src/tools/`** — 59 个子目录(含 shared/testing 等工具目录),通过 `@claude-code-best/builtin-tools` 包导出。主要分类:
|
||||
- **文件操作**: FileEditTool, FileReadTool, FileWriteTool, GlobTool, GrepTool
|
||||
- **Shell/执行**: BashTool, PowerShellTool, REPLTool
|
||||
- **Agent 系统**: AgentTool, TaskCreateTool, TaskUpdateTool, TaskListTool, TaskGetTool
|
||||
@@ -119,7 +124,6 @@ bun run docs:dev
|
||||
- **Web/MCP**: WebFetchTool, WebSearchTool, MCPTool, McpAuthTool
|
||||
- **调度**: CronCreateTool, CronDeleteTool, CronListTool
|
||||
- **其他**: LSPTool, ConfigTool, SkillTool, EnterWorktreeTool, ExitWorktreeTool 等
|
||||
- **`src/tools/shared/`** — Tool 共享工具函数。
|
||||
|
||||
### UI Layer (Ink)
|
||||
|
||||
@@ -150,9 +154,17 @@ bun run docs:dev
|
||||
| `packages/@ant/computer-use-input/` | 键鼠模拟(dispatcher + darwin/win32/linux backend) |
|
||||
| `packages/@ant/computer-use-swift/` | 截图 + 应用管理(dispatcher + per-platform backend) |
|
||||
| `packages/@ant/claude-for-chrome-mcp/` | Chrome 浏览器控制(通过 `--chrome` 启用) |
|
||||
| `packages/remote-control-server/` | 自托管 Remote Control Server(Docker 部署,含 Web UI) |
|
||||
| `packages/swarm/` | Swarm 解耦模块 |
|
||||
| `packages/shell/` | Shell 抽象 |
|
||||
| `packages/@ant/model-provider/` | Model provider 抽象层 |
|
||||
| `packages/builtin-tools/` | 内置工具集(60 个 tool 实现,通过 `@claude-code-best/builtin-tools` 导出) |
|
||||
| `packages/agent-tools/` | Agent 工具集 |
|
||||
| `packages/acp-link/` | ACP 代理服务器(WebSocket → ACP agent 桥接) |
|
||||
| `packages/cc-knowledge/` | Claude Code 知识库(非 workspace 包) |
|
||||
| `packages/langfuse-dashboard/` | Langfuse 可观测性面板(非 workspace 包) |
|
||||
| `packages/mcp-client/` | MCP 客户端库 |
|
||||
| `packages/mcp-server/` | MCP 服务端库(非 workspace 包) |
|
||||
| `packages/remote-control-server/` | 自托管 Remote Control Server(Docker 部署,含 Web UI)— Web UI 已重构为 React + Vite + Radix UI,支持 ACP agent 接入 |
|
||||
| `packages/swarm/` | Swarm 解耦模块(非 workspace 包) |
|
||||
| `packages/shell/` | Shell 抽象(非 workspace 包) |
|
||||
| `packages/audio-capture-napi/` | 原生音频捕获(已恢复) |
|
||||
| `packages/color-diff-napi/` | 颜色差异计算(完整实现,11 tests) |
|
||||
| `packages/image-processor-napi/` | 图像处理(已恢复) |
|
||||
@@ -161,11 +173,18 @@ bun run docs:dev
|
||||
|
||||
### Bridge / Remote Control
|
||||
|
||||
- **`src/bridge/`** (~37 files) — Remote Control / Bridge 模式。feature-gated by `BRIDGE_MODE`。包含 bridge API、会话管理、JWT 认证、消息传输、权限回调等。Entry: `bridgeMain.ts`。
|
||||
- **`packages/remote-control-server/`** — 自托管 RCS,支持 Docker 部署,含 Web UI 控制面板。通过 `bun run rcs` 启动。
|
||||
- **`src/bridge/`** (~38 files) — Remote Control / Bridge 模式。feature-gated by `BRIDGE_MODE`。包含 bridge API、会话管理、JWT 认证、消息传输、权限回调等。Entry: `bridgeMain.ts`。
|
||||
- **`packages/remote-control-server/`** — 自托管 RCS,支持 Docker 部署,含 Web UI 控制面板(React 19 + Vite + Radix UI)。支持 ACP agent 通过 acp-link 接入(ACP WebSocket handler、relay handler、SSE event stream)。通过 `bun run rcs` 启动。
|
||||
- CLI 快速路径: `claude remote-control` / `claude rc` / `claude bridge`。
|
||||
- 详见 `docs/features/remote-control-self-hosting.md`。
|
||||
|
||||
### ACP Protocol (Agent Client Protocol)
|
||||
|
||||
- **`src/services/acp/`** — ACP agent 实现,包含 `agent.ts`(AcpAgent 类)、`bridge.ts`(Claude Code ↔ ACP 桥接)、`permissions.ts`(权限处理)、`entry.ts`(入口)。
|
||||
- **`packages/acp-link/`** — ACP 代理服务器,将 WebSocket 客户端桥接到 ACP agent。提供 `acp-link` CLI 命令,支持自定义端口/HTTPS/认证/会话管理、RCS 集成(REST 注册 + WS identify 两步流程)、权限模式透传(fallback: 客户端传值 > config > `ACP_PERMISSION_MODE` 环境变量)。
|
||||
- ACP 权限管道改进:`createAcpCanUseTool` 统一权限流水线,`applySessionMode` 模式同步,`bypassPermissions` 可用性检测(非 root/sandbox 环境)。
|
||||
- ACP Plan 可视化已支持 `session/update plan` 类型的消息展示(PlanView 组件,含进度条/状态图标/优先级标签)。
|
||||
|
||||
### Daemon Mode
|
||||
|
||||
- **`src/daemon/`** — Daemon 模式(长驻 supervisor)。feature-gated by `DAEMON`。包含 `main.ts`(entry)和 `workerRegistry.ts`(worker 管理)。
|
||||
@@ -196,30 +215,13 @@ Feature flags control which functionality is enabled at runtime. 代码中统一
|
||||
|
||||
### Multi-API 兼容层
|
||||
|
||||
所有兼容层均采用流适配器模式:将第三方 API 格式转为 Anthropic 内部格式,下游代码完全不改。
|
||||
支持 OpenAI、Gemini、Grok 三种第三方 API,通过 `/login` 命令配置,均采用流适配器模式转为 Anthropic 内部格式。详见各兼容层的 docs 文档。
|
||||
|
||||
#### OpenAI 兼容层
|
||||
### 穷鬼模式(Budget Mode)
|
||||
|
||||
通过 `CLAUDE_CODE_USE_OPENAI=1` 启用,支持 Ollama/DeepSeek/vLLM 等任意 OpenAI Chat Completions 协议端点。含 DeepSeek thinking mode 支持。
|
||||
|
||||
- **`src/services/api/openai/`** — client、消息/工具转换、流适配、模型映射
|
||||
- 关键环境变量:`CLAUDE_CODE_USE_OPENAI`、`OPENAI_API_KEY`、`OPENAI_BASE_URL`、`OPENAI_MODEL`
|
||||
|
||||
#### Gemini 兼容层
|
||||
|
||||
通过 `CLAUDE_CODE_USE_GEMINI=1` 启用。独立环境变量体系。
|
||||
|
||||
- **`src/services/api/gemini/`** — client、模型映射、类型定义
|
||||
- 关键环境变量:`GEMINI_API_KEY`(必填)、`GEMINI_MODEL`(直接指定)、`GEMINI_DEFAULT_SONNET_MODEL`/`GEMINI_DEFAULT_OPUS_MODEL`(按能力映射)
|
||||
- 模型映射优先级:`GEMINI_MODEL` > `GEMINI_DEFAULT_*_MODEL` > `ANTHROPIC_DEFAULT_*_MODEL`(已废弃) > 原样返回
|
||||
|
||||
#### Grok 兼容层
|
||||
|
||||
通过 `CLAUDE_CODE_USE_GROK=1` 启用。自定义模型映射支持 xAI Grok API。
|
||||
|
||||
- **`src/services/api/grok/`** — client、模型映射
|
||||
|
||||
详见各兼容层的 docs 文档。
|
||||
- 通过 `/poor` 命令切换,持久化到 `settings.json`。
|
||||
- 启用后跳过 `extract_memories`、`prompt_suggestion` 和 `verification_agent`,显著减少 token 消耗。
|
||||
- 实现在 `src/commands/poor/poorMode.ts`。
|
||||
|
||||
### Stubbed/Deleted Modules
|
||||
|
||||
@@ -245,20 +247,29 @@ Feature flags control which functionality is enabled at runtime. 代码中统一
|
||||
## Testing
|
||||
|
||||
- **框架**: `bun:test`(内置断言 + mock)
|
||||
- **当前状态**: 2472 tests / 138 files / 0 fail
|
||||
- **当前状态**: 3175 tests / 207 files / 0 fail
|
||||
- **单元测试**: 就近放置于 `src/**/__tests__/`,文件名 `<module>.test.ts`
|
||||
- **集成测试**: `tests/integration/` — 4 个文件(cli-arguments, context-build, message-pipeline, tool-chain)
|
||||
- **共享 mock/fixture**: `tests/mocks/`(api-responses, file-system, fixtures/)
|
||||
- **命名**: `describe("functionName")` + `test("behavior description")`,英文
|
||||
- **Mock 模式**: 对重依赖模块使用 `mock.module()` + `await import()` 解锁(必须内联在测试文件中,不能从共享 helper 导入)
|
||||
- **包测试**: `packages/` 下各包也有独立测试(如 `color-diff-napi` 11 tests)
|
||||
|
||||
### Mock 使用规范
|
||||
|
||||
**只 mock 有副作用的依赖链,不 mock 纯函数/纯数据模块。**
|
||||
|
||||
被迫 mock 的根源:`log.ts` / `debug.ts` → `bootstrap/state.ts`(模块级 `realpathSync` / `randomUUID` 副作用)。必须 mock 的模块:`log.ts`、`debug.ts`、`bun:bundle`、`settings/settings.js`、`config.ts`、`auth.ts`、第三方网络库。
|
||||
|
||||
不要 mock:纯函数模块(`errors.ts`、`stringUtils.js`)、mock 值与真实实现相同的模块、mock 路径与实际 import 不匹配的模块。
|
||||
|
||||
路径规则:统一用 `.ts` 扩展名 + `src/*` 别名路径,禁止双重 mock 同一模块。
|
||||
|
||||
### 类型检查
|
||||
|
||||
项目使用 TypeScript strict 模式,**tsc 必须零错误**。每次修改后运行:
|
||||
|
||||
```bash
|
||||
bunx tsc --noEmit
|
||||
bun run typecheck # equivalent to bun run typecheck
|
||||
```
|
||||
|
||||
**类型规范**:
|
||||
@@ -271,7 +282,7 @@ bunx tsc --noEmit
|
||||
|
||||
## Working with This Codebase
|
||||
|
||||
- **tsc must pass** — `bunx tsc --noEmit` 必须零错误,任何修改都不能引入新的类型错误。
|
||||
- **tsc must pass** — `bun run typecheck` 必须零错误,任何修改都不能引入新的类型错误。
|
||||
- **Feature flags** — 默认全部关闭(`feature()` 返回 `false`)。Dev/build 各有自己的默认启用列表。不要在 `cli.tsx` 中重定义 `feature` 函数。
|
||||
- **React Compiler output** — Components have decompiled memoization boilerplate (`const $ = _c(N)`). This is normal.
|
||||
- **`bun:bundle` import** — `import { feature } from 'bun:bundle'` 是 Bun 内置模块,由运行时/构建器解析。不要用自定义函数替代它。**`feature()` 只能直接用在 `if` 语句或三元表达式的条件位置**(Bun 编译器限制),不能赋值给变量、不能放在箭头函数体里、不能作为 `&&` 链的一部分。正确:`if (feature('X')) {}` 或 `feature('X') ? a : b`。
|
||||
@@ -281,3 +292,29 @@ bunx tsc --noEmit
|
||||
- **Biome 配置** — 大量 lint 规则被关闭(decompiled 代码不适合严格 lint)。`.tsx` 文件用 120 行宽 + 强制分号;其他文件 80 行宽 + 按需分号。
|
||||
- **Ink 框架在 `packages/@ant/ink/`** — 不是 `src/ink/`(该目录不存在)。Ink 相关的组件、hooks、keybindings 都在 packages 中。
|
||||
- **Provider 优先级** — `modelType` 参数 > 环境变量 > 默认 `firstParty`。新增 provider 需在 `src/utils/model/providers.ts` 注册。
|
||||
|
||||
## Design Context
|
||||
|
||||
Impeccable 设计上下文保存在 `.impeccable.md` 中。设计 Web UI(RCS 控制面板、文档站、着陆页)时必须参考该文件。
|
||||
|
||||
### 核心设计原则
|
||||
|
||||
1. **Considered over clever** — 每个设计选择都应感觉有意为之,而非追逐潮流
|
||||
2. **Warmth through subtlety** — 通过橙色色调的中性色、留白布局、有温度的文案来传达温暖
|
||||
3. **Density with clarity** — 技术用户需要信息密度,但不能混乱
|
||||
4. **Community voice** — 设计应感觉是由使用者创造的,而非遥远的设计团队
|
||||
5. **Anthropic's shadow** — 遵循 Anthropic 的设计直觉:干净的布局、充足的间距、温暖的色温
|
||||
|
||||
### 品牌色
|
||||
|
||||
- 主色:Claude Orange `#D77757`(terra cotta)
|
||||
- 辅色:Claude Blue `#5769F7`
|
||||
- 暗色模式使用温暖的深色表面(非冷蓝黑色)
|
||||
|
||||
### 目标用户
|
||||
|
||||
技术团队/企业,在专业工作流中使用 AI 辅助编程。友好的开源社区氛围,非企业 SaaS 风格。
|
||||
|
||||
### 视觉参考
|
||||
|
||||
Anthropic 公司的设计风格 — 干净、考究、温暖的底色。大量留白,以排版为核心。避免 AI 产品常见的设计套路(渐变文字、玻璃态、霓虹色)。
|
||||
|
||||
30
README.md
30
README.md
@@ -10,25 +10,26 @@
|
||||
|
||||
> Which Claude do you like? The open source one is the best.
|
||||
|
||||
牢 A (Anthropic) 官方 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI 工具的源码反编译/逆向还原项目。目标是将 Claude Code 大部分功能及工程化能力复现 (问就是老佛爷已经付过钱了)。虽然很难绷, 但是它叫做 CCB(踩踩背)...
|
||||
牢 A (Anthropic) 官方 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI 工具的源码反编译/逆向还原项目。目标是将 Claude Code 大部分功能及工程化能力复现 (问就是老佛爷已经付过钱了)。虽然很难绷, 但是它叫做 CCB(踩踩背)... 而且, 我们实现了企业版或者需要登陆 Claude 账号才能使用的特性, 实现技术普惠
|
||||
|
||||
[文档在这里, 支持投稿 PR](https://ccb.agent-aura.top/) | [留影文档在这里](./Friends.md) | [Discord 群组](https://discord.gg/qZU6zS7Q)
|
||||
|
||||
| 特性 | 说明 | 文档 |
|
||||
|------|------|------|
|
||||
| **Claude 群控技术** | Pipe IPC 多实例协作:同机 main/sub 自动编排 + LAN 跨机器零配置发现与通讯,`/pipes` 选择面板 + `Shift+↓` 交互 + 消息广播路由 | [Pipe IPC](https://ccb.agent-aura.top/docs/features/pipes-and-lan) / [LAN](https://ccb.agent-aura.top/docs/features/lan-pipes) |
|
||||
| Remote Control 私有部署 | Docker 自托管 RCS + Web UI | [文档](https://ccb.agent-aura.top/docs/features/remote-control-self-hosting) |
|
||||
| /dream 记忆整理 | 自动整理和优化记忆文件 | [文档](https://ccb.agent-aura.top/docs/features/auto-dream) |
|
||||
| Web Search | 内置网页搜索工具 | [文档](https://ccb.agent-aura.top/docs/features/web-browser-tool) |
|
||||
| 自定义模型供应商 | OpenAI/Anthropic/Gemini/Grok 兼容 | [文档](https://ccb.agent-aura.top/docs/features/custom-platform-login) |
|
||||
| **ACP 协议一等一支持** | 支持接入 Zed、Cursor 等 IDE,支持会话恢复、Skills、权限桥接 | [文档](https://ccb.agent-aura.top/docs/features/acp-zed) |
|
||||
| **Remote Control 私有部署** | Docker 自托管远程界面, 可以手机上看 CC | [文档](https://ccb.agent-aura.top/docs/features/remote-control-self-hosting) |
|
||||
| **Langfuse 监控** | 企业级 Agent 监控, 可以清晰看到每次 agent loop 细节, 可以一键转化为数据集 | [文档](https://ccb.agent-aura.top/docs/features/langfuse-monitoring) |
|
||||
| **Web Search** | 内置网页搜索工具, 支持 bing 和 brave 搜索 | [文档](https://ccb.agent-aura.top/docs/features/web-browser-tool) |
|
||||
| **Poor Mode** | 穷鬼模式,关闭记忆提取和键入建议,大幅度减少并发请求 | /poor 可以开关 |
|
||||
| **Channels 频道通知** | MCP 服务器推送外部消息到会话(飞书/Slack/Discord/微信等),`--channels plugin:name@marketplace` 启用 | [文档](https://ccb.agent-aura.top/docs/features/channels) |
|
||||
| **自定义模型供应商** | OpenAI/Anthropic/Gemini/Grok 兼容 | [文档](https://ccb.agent-aura.top/docs/features/custom-platform-login) |
|
||||
| Voice Mode | Push-to-Talk 语音输入 | [文档](https://ccb.agent-aura.top/docs/features/voice-mode) |
|
||||
| Computer Use / Chrome Use | 截图、键鼠控制、浏览器操控 | [Computer Use](https://ccb.agent-aura.top/docs/features/computer-use)<br>[Chrome Use](https://ccb.agent-aura.top/docs/features/claude-in-chrome-mcp) |
|
||||
| Sentry / GrowthBook 企业监控 | 企业级错误追踪与特性开关 | [Sentry](https://ccb.agent-aura.top/docs/internals/sentry-setup)<br>[GrowthBook](https://ccb.agent-aura.top/docs/internals/growthbook-adapter) |
|
||||
| Langfuse 监控 | LLM 调用/工具执行/多 Agent 全链路追踪 | [文档](https://ccb.agent-aura.top/docs/features/langfuse-monitoring) |
|
||||
| Poor Mode | 穷鬼模式,关闭记忆提取和键入建议 | /poor 可以开关 |
|
||||
|
||||
|
||||
- 🔮 [ ] V6 — 大规模重构石山代码,全面模块分包(全新分支,main 封存为历史版本)
|
||||
| Computer Use | 屏幕截图、键鼠控制 | [文档](https://ccb.agent-aura.top/docs/features/computer-use) |
|
||||
| Chrome Use | 浏览器自动化、表单填写、数据抓取 | [自托管](https://ccb.agent-aura.top/docs/features/chrome-use-mcp) [原生版](https://ccb.agent-aura.top/docs/features/claude-in-chrome-mcp) |
|
||||
| Sentry | 企业级错误追踪 | [文档](https://ccb.agent-aura.top/docs/internals/sentry-setup) |
|
||||
| GrowthBook | 企业级特性开关 | [文档](https://ccb.agent-aura.top/docs/internals/growthbook-adapter) |
|
||||
| /dream 记忆整理 | 自动整理和优化记忆文件 | [文档](https://ccb.agent-aura.top/docs/features/auto-dream) |
|
||||
|
||||
- 🚀 [想要启动项目](#快速开始源码版)
|
||||
- 🐛 [想要调试项目](#vs-code-调试)
|
||||
@@ -42,8 +43,9 @@
|
||||
```sh
|
||||
bun i -g claude-code-best
|
||||
bun pm -g trust claude-code-best
|
||||
ccb # 直接打开 claude code
|
||||
CLAUDE_BRIDGE_BASE_URL=https://remote-control.claude-code-best.win/ CLAUDE_BRIDGE_OAUTH_TOKEN=test-my-key bun run dev --remote-control # 我们有自部署的远程控制
|
||||
ccb # 以 nodejs 打开 claude code
|
||||
ccb-bun # 以 bun 形态打开
|
||||
CLAUDE_BRIDGE_BASE_URL=https://remote-control.claude-code-best.win/ CLAUDE_BRIDGE_OAUTH_TOKEN=test-my-key ccb --remote-control # 我们有自部署的远程控制
|
||||
```
|
||||
|
||||
## ⚡ 快速开始(源码版)
|
||||
|
||||
57
build.ts
57
build.ts
@@ -11,6 +11,7 @@ rmSync(outdir, { recursive: true, force: true })
|
||||
// Default features that match the official CLI build.
|
||||
// Additional features can be enabled via FEATURE_<NAME>=1 env vars.
|
||||
const DEFAULT_BUILD_FEATURES = [
|
||||
'BUDDY', 'TRANSCRIPT_CLASSIFIER', 'BRIDGE_MODE',
|
||||
'AGENT_TRIGGERS_REMOTE',
|
||||
'CHICAGO_MCP',
|
||||
'VOICE_MODE',
|
||||
@@ -30,16 +31,20 @@ const DEFAULT_BUILD_FEATURES = [
|
||||
'ULTRAPLAN',
|
||||
// P2: daemon + remote control server
|
||||
'DAEMON',
|
||||
// ACP (Agent Client Protocol) agent mode
|
||||
'ACP',
|
||||
// PR-package restored features
|
||||
'WORKFLOW_SCRIPTS',
|
||||
'HISTORY_SNIP',
|
||||
'CONTEXT_COLLAPSE',
|
||||
'MONITOR_TOOL',
|
||||
'FORK_SUBAGENT',
|
||||
'UDS_INBOX',
|
||||
// 'UDS_INBOX',
|
||||
'KAIROS',
|
||||
'COORDINATOR_MODE',
|
||||
'LAN_PIPES',
|
||||
'BG_SESSIONS',
|
||||
'TEMPLATES',
|
||||
// 'REVIEW_ARTIFACT', // API 请求无响应,需进一步排查 schema 兼容性
|
||||
// P3: poor mode (disable extract_memories + prompt_suggestion)
|
||||
'POOR',
|
||||
@@ -88,8 +93,27 @@ for (const file of files) {
|
||||
}
|
||||
}
|
||||
|
||||
// Also patch unguarded globalThis.Bun destructuring from third-party deps
|
||||
// (e.g. @anthropic-ai/sandbox-runtime) so Node.js doesn't crash at import time.
|
||||
let bunPatched = 0
|
||||
const BUN_DESTRUCTURE = /var \{([^}]+)\} = globalThis\.Bun;?/g
|
||||
const BUN_DESTRUCTURE_SAFE = 'var {$1} = typeof globalThis.Bun !== "undefined" ? globalThis.Bun : {};'
|
||||
for (const file of files) {
|
||||
if (!file.endsWith('.js')) continue
|
||||
const filePath = join(outdir, file)
|
||||
const content = await readFile(filePath, 'utf-8')
|
||||
if (BUN_DESTRUCTURE.test(content)) {
|
||||
await writeFile(
|
||||
filePath,
|
||||
content.replace(BUN_DESTRUCTURE, BUN_DESTRUCTURE_SAFE),
|
||||
)
|
||||
bunPatched++
|
||||
}
|
||||
}
|
||||
BUN_DESTRUCTURE.lastIndex = 0
|
||||
|
||||
console.log(
|
||||
`Bundled ${result.outputs.length} files to ${outdir}/ (patched ${patched} for Node.js compat)`,
|
||||
`Bundled ${result.outputs.length} files to ${outdir}/ (patched ${patched} for import.meta.require, ${bunPatched} for Bun destructure)`,
|
||||
)
|
||||
|
||||
// Step 4: Copy native .node addon files (audio-capture)
|
||||
@@ -97,18 +121,17 @@ const vendorDir = join(outdir, 'vendor', 'audio-capture')
|
||||
await cp('vendor/audio-capture', vendorDir, { recursive: true })
|
||||
console.log(`Copied vendor/audio-capture/ → ${vendorDir}/`)
|
||||
|
||||
// Step 5: Bundle download-ripgrep script as standalone JS for postinstall
|
||||
const rgScript = await Bun.build({
|
||||
entrypoints: ['scripts/download-ripgrep.ts'],
|
||||
outdir,
|
||||
target: 'node',
|
||||
})
|
||||
if (!rgScript.success) {
|
||||
console.error('Failed to bundle download-ripgrep script:')
|
||||
for (const log of rgScript.logs) {
|
||||
console.error(log)
|
||||
}
|
||||
// Non-fatal — postinstall fallback to bun run scripts/download-ripgrep.ts
|
||||
} else {
|
||||
console.log(`Bundled download-ripgrep script to ${outdir}/`)
|
||||
}
|
||||
// Step 5: Generate cli-bun and cli-node executable entry points
|
||||
const cliBun = join(outdir, 'cli-bun.js')
|
||||
const cliNode = join(outdir, 'cli-node.js')
|
||||
|
||||
await writeFile(cliBun, '#!/usr/bin/env bun\nimport "./cli.js"\n')
|
||||
|
||||
await writeFile(cliNode, '#!/usr/bin/env node\nimport "./cli.js"\n')
|
||||
|
||||
// Make both executable
|
||||
const { chmodSync } = await import('fs')
|
||||
chmodSync(cliBun, 0o755)
|
||||
chmodSync(cliNode, 0o755)
|
||||
|
||||
console.log(`Generated ${cliBun} (shebang: bun) and ${cliNode} (shebang: node)`)
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.6 MiB |
@@ -13,14 +13,14 @@ keywords: ["子 Agent", "AgentTool", "任务委派", "forkSubagent", "子进程
|
||||
```
|
||||
AI 生成 tool_use: { prompt: "修复 bug", subagent_type: "Explore" }
|
||||
↓
|
||||
AgentTool.call() ← 入口(AgentTool.tsx:239)
|
||||
AgentTool.call() ← 入口(AgentTool.tsx:387)
|
||||
├── 解析 effectiveType(fork vs 命名 agent vs GP 回退)
|
||||
├── filterDeniedAgents() ← 仅命名 Agent 路径执行:权限过滤
|
||||
├── 检查 requiredMcpServers ← MCP 依赖验证(最长等 30s)
|
||||
├── assembleToolPool(workerPermissionContext) ← 独立组装工具池
|
||||
├── createAgentWorktree() ← 可选 worktree 隔离
|
||||
↓
|
||||
runAgent() ← 核心执行(runAgent.ts:248)
|
||||
runAgent() ← 核心执行(runAgent.ts)
|
||||
├── getAgentSystemPrompt() ← 构建 agent 专属 system prompt
|
||||
├── initializeAgentMcpServers() ← agent 级 MCP 服务器
|
||||
├── executeSubagentStartHooks() ← Hook 注入
|
||||
@@ -54,7 +54,7 @@ Fork 实验的门控函数 `isForkSubagentEnabled()` 需要同时满足三个前
|
||||
Fork 路径的设计核心是 **Prompt Cache 共享**:所有 fork 子进程共享父 Agent 的完整 `assistant` 消息(所有 `tool_use` 块),用相同的占位符 `tool_result` 填充,只有最后一个 `text` 块包含各自的指令。这使得 API 请求前缀字节完全一致,最大化缓存命中。
|
||||
|
||||
```typescript
|
||||
// forkSubagent.ts:142 — 所有 fork 子进程的占位结果
|
||||
// forkSubagent.ts:93 — 所有 fork 子进程的占位结果
|
||||
const FORK_PLACEHOLDER_RESULT = 'Fork started — processing in background'
|
||||
|
||||
// buildForkedMessages() 构建:
|
||||
@@ -63,7 +63,7 @@ const FORK_PLACEHOLDER_RESULT = 'Fork started — processing in background'
|
||||
|
||||
### Fork 递归防护
|
||||
|
||||
Fork 子进程保留 Agent 工具(为了 cache-identical tool defs),但通过两道防线防止递归 fork(`AgentTool.tsx:332`):
|
||||
Fork 子进程保留 Agent 工具(为了 cache-identical tool defs),但通过两道防线防止递归 fork:
|
||||
|
||||
1. **`querySource` 检查**(压缩安全):`context.options.querySource === 'agent:builtin:fork'`
|
||||
2. **消息扫描**(降级兜底):检测 `<fork-boilerplate>` 标签
|
||||
@@ -88,7 +88,7 @@ Fork 子进程保留 Agent 工具(为了 cache-identical tool defs),但通
|
||||
|
||||
### 内置 Agent
|
||||
|
||||
系统预定义了几个内置 Agent(`src/tools/AgentTool/builtinAgents.ts`),各有明确的职责和模型配置:
|
||||
系统预定义了几个内置 Agent(`packages/builtin-tools/src/tools/AgentTool/builtInAgents.ts`),各有明确的职责和模型配置:
|
||||
|
||||
| Agent | 模型 | 权限 | 用途 |
|
||||
|-------|------|------|------|
|
||||
@@ -119,7 +119,7 @@ const workerTools = assembleToolPool(workerPermissionContext, appState.mcp.tools
|
||||
|
||||
### 工具过滤的 resolveAgentTools
|
||||
|
||||
`runAgent.ts:500-502` 在工具组装后进一步过滤:
|
||||
`runAgent.ts:508` 在工具组装后进一步过滤:
|
||||
|
||||
```typescript
|
||||
const resolvedTools = useExactTools
|
||||
@@ -142,7 +142,7 @@ const resolvedTools = useExactTools
|
||||
|
||||
## Worktree 隔离机制
|
||||
|
||||
`isolation: "worktree"` 参数让子 Agent 在独立的 git worktree 中工作(`AgentTool.tsx:590-593`):
|
||||
`isolation: "worktree"` 参数让子 Agent 在独立的 git worktree 中工作(`AgentTool.tsx:863`):
|
||||
|
||||
```typescript
|
||||
const slug = `agent-${earlyAgentId.slice(0, 8)}`
|
||||
@@ -183,7 +183,7 @@ runAsyncAgentLifecycle() ← 后台执行(agentToolUtils.ts)
|
||||
|
||||
### 同步 Agent(前台运行)
|
||||
|
||||
同步 Agent 的关键特性是 **可后台化**(`AgentTool.tsx:818-833`):
|
||||
同步 Agent 的关键特性是 **可后台化**(`AgentTool.tsx:1107`):
|
||||
|
||||
```typescript
|
||||
const registration = registerAgentForeground({
|
||||
@@ -218,7 +218,7 @@ const raceResult = await Promise.race([
|
||||
|
||||
## MCP 依赖的等待机制
|
||||
|
||||
如果 Agent 声明了 `requiredMcpServers`,`call()` 会等待这些服务器连接完成(`AgentTool.tsx:371-410`):
|
||||
如果 Agent 声明了 `requiredMcpServers`,`call()` 会等待这些服务器连接完成(`AgentTool.tsx:576`):
|
||||
|
||||
```typescript
|
||||
const MAX_WAIT_MS = 30_000 // 最长等 30 秒
|
||||
|
||||
@@ -37,7 +37,7 @@ Worktree 文件统一存放在仓库根目录下的 `.claude/worktrees/`:
|
||||
|
||||
## 创建流程:EnterWorktreeTool
|
||||
|
||||
`EnterWorktreeTool`(`src/tools/EnterWorktreeTool/EnterWorktreeTool.ts`)的执行链路:
|
||||
`EnterWorktreeTool`(`packages/builtin-tools/src/tools/EnterWorktreeTool/EnterWorktreeTool.ts`)的执行链路:
|
||||
|
||||
```
|
||||
EnterWorktreeTool.call({ name? })
|
||||
@@ -83,7 +83,7 @@ EnterWorktreeTool.call({ name? })
|
||||
|
||||
## 退出流程:ExitWorktreeTool
|
||||
|
||||
`ExitWorktreeTool`(`src/tools/ExitWorktreeTool/ExitWorktreeTool.ts`)支持两种退出策略:
|
||||
`ExitWorktreeTool`(`packages/builtin-tools/src/tools/ExitWorktreeTool/ExitWorktreeTool.ts`)支持两种退出策略:
|
||||
|
||||
### keep:保留 worktree
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ useInterval(checkForUpdates, 30 * 60 * 1000); // 每 30 分钟
|
||||
|
||||
任何更新尝试之前,系统会依次检查:
|
||||
|
||||
1. **自动更新是否被禁用?** — `getAutoUpdaterDisabledReason()`(`src/utils/config.ts:1735`)
|
||||
1. **自动更新是否被禁用?** — `getAutoUpdaterDisabledReason()`(`src/utils/config.ts:1737`)
|
||||
- `NODE_ENV === 'development'`
|
||||
- 设置了 `DISABLE_AUTOUPDATER` 环境变量
|
||||
- 仅限必要流量模式
|
||||
@@ -81,7 +81,7 @@ useInterval(checkForUpdates, 30 * 60 * 1000); // 每 30 分钟
|
||||
|
||||
`src/utils/autoUpdater.ts:70` — `assertMinVersion()`
|
||||
|
||||
从 `src/main.tsx:1775` 在启动时调用:
|
||||
定义于 `src/utils/autoUpdater.ts:70`,设计上在启动时调用(当前未接入启动流程):
|
||||
|
||||
```typescript
|
||||
void assertMinVersion();
|
||||
@@ -200,7 +200,7 @@ Windows 系统使用文件复制而非符号链接。
|
||||
|
||||
**文件**: `src/migrations/migrateAutoUpdatesToSettings.ts`
|
||||
|
||||
一次性将旧版 `globalConfig.autoUpdates = false` 迁移为 settings 中的 `DISABLE_AUTOUPDATER=1` 环境变量。从 `src/main.tsx:325` 在启动时调用。
|
||||
一次性将旧版 `globalConfig.autoUpdates = false` 迁移为 settings 中的 `DISABLE_AUTOUPDATER=1` 环境变量。定义于 `src/migrations/migrateAutoUpdatesToSettings.ts`(当前未接入启动流程)。
|
||||
|
||||
---
|
||||
|
||||
@@ -270,7 +270,7 @@ React hook `useUpdateNotification(updatedVersion)` — 确保每次 semver 变
|
||||
| `src/utils/releaseNotes.ts` | Changelog 获取、缓存与展示 |
|
||||
| `src/utils/semver.ts` | Semver 版本比较(Bun 原生 + npm 回退) |
|
||||
| `src/utils/doctorDiagnostic.ts` | 安装类型检测与健康诊断 |
|
||||
| `src/utils/config.ts:1735` | `getAutoUpdaterDisabledReason()` — 禁用检查逻辑 |
|
||||
| `src/utils/config.ts:1737` | `getAutoUpdaterDisabledReason()` — 禁用检查逻辑 |
|
||||
| `src/migrations/migrateAutoUpdatesToSettings.ts` | 旧版配置迁移 |
|
||||
| `src/screens/Doctor.tsx` | Doctor 命令 UI,展示自动更新状态 |
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ const messagesForCompact = microcompactResult.messages
|
||||
MicroCompact 不压缩整个对话,而是**清除旧工具输出的内容**。它维护一个白名单:
|
||||
|
||||
```typescript
|
||||
// src/services/compact/microCompact.ts:41-48
|
||||
// src/services/compact/microCompact.ts:41-50
|
||||
const COMPACTABLE_TOOLS = new Set([
|
||||
FILE_READ_TOOL_NAME, // 'Read' - 文件读取
|
||||
...SHELL_TOOL_NAMES, // 'Bash' - 命令输出
|
||||
@@ -143,7 +143,7 @@ const stripped2 = stripReinjectedAttachments(stripped) // 移除会被重新注
|
||||
压缩后,系统会从摘要中**重新注入关键上下文**:
|
||||
|
||||
```typescript
|
||||
// compact.ts:124-132
|
||||
// compact.ts:126-134
|
||||
export const POST_COMPACT_TOKEN_BUDGET = 50_000 // 总预算
|
||||
export const POST_COMPACT_MAX_FILES_TO_RESTORE = 5 // 最多恢复 5 个文件
|
||||
export const POST_COMPACT_MAX_TOKENS_PER_FILE = 5_000 // 每文件 5K token
|
||||
|
||||
@@ -39,7 +39,7 @@ Claude Code 的记忆系统是**纯文件**的——没有数据库、没有向
|
||||
`MEMORY.md` 是记忆的入口索引,每次对话都完整加载到上下文中:
|
||||
|
||||
```typescript
|
||||
// memdir.ts:35-38
|
||||
// memdir.ts:34-38
|
||||
export const ENTRYPOINT_NAME = 'MEMORY.md'
|
||||
export const MAX_ENTRYPOINT_LINES = 200
|
||||
export const MAX_ENTRYPOINT_BYTES = 25_000
|
||||
|
||||
@@ -20,12 +20,12 @@ buildSystemPromptBlocks() → TextBlockParam[] (分块 + cache_control 标
|
||||
|
||||
1. **`getSystemPrompt()`**(`src/constants/prompts.ts:444`)—— 收集静态段 + 动态段,插入 `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` 分界标记
|
||||
2. **`buildEffectiveSystemPrompt()`**(`src/utils/systemPrompt.ts:41`)—— 按 Override > Coordinator > Agent > Custom > Default 优先级选择
|
||||
3. **`buildSystemPromptBlocks()`**(`src/services/api/claude.ts:3214`)—— 调用 `splitSysPromptPrefix()` 分块,为每个块附加 `cache_control`
|
||||
3. **`buildSystemPromptBlocks()`**(`src/services/api/claude.ts:3279`)—— 调用 `splitSysPromptPrefix()` 分块,为每个块附加 `cache_control`
|
||||
|
||||
## SystemPrompt 品牌类型
|
||||
|
||||
```typescript
|
||||
// src/utils/systemPromptType.ts:8
|
||||
// packages/@ant/model-provider/src/types/systemPrompt.ts:4
|
||||
export type SystemPrompt = readonly string[] & {
|
||||
readonly __brand: 'SystemPrompt'
|
||||
}
|
||||
@@ -185,7 +185,7 @@ export function shouldUseGlobalCacheScope(): boolean {
|
||||
|
||||
### `getCacheControl()`:TTL 决策
|
||||
|
||||
`src/services/api/claude.ts:359` 生成的 `cache_control` 对象:
|
||||
`src/services/api/claude.ts:348` 生成的 `cache_control` 对象:
|
||||
|
||||
```typescript
|
||||
{
|
||||
@@ -195,14 +195,14 @@ export function shouldUseGlobalCacheScope(): boolean {
|
||||
}
|
||||
```
|
||||
|
||||
1 小时 TTL 的判定逻辑(`should1hCacheTTL()`,第 394 行):
|
||||
1 小时 TTL 的判定逻辑(`should1hCacheTTL()`,第 383 行):
|
||||
- **Bedrock 用户**:通过环境变量 `ENABLE_PROMPT_CACHING_1H_BEDROCK` 启用
|
||||
- **1P 用户**:通过 GrowthBook 配置的 `allowlist` 数组匹配 `querySource`,支持前缀通配符(如 `"repl_main_thread*"`)
|
||||
- **会话级锁定**:资格判定结果在 bootstrap state 中缓存,防止 GrowthBook 配置中途变化导致同一会话内 TTL 不一致
|
||||
|
||||
### 缓存破坏:Session-Specific Guidance 的放置
|
||||
|
||||
`getSessionSpecificGuidanceSection()`(`src/constants/prompts.ts:352`)的内容必须放在 `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` **之后**。因为它包含:
|
||||
`getSessionSpecificGuidanceSection()`(`src/constants/prompts.ts:354`)的内容必须放在 `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` **之后**。因为它包含:
|
||||
- 当前会话的 enabledTools 集合
|
||||
- `isForkSubagentEnabled()` 的运行时判定
|
||||
- `getIsNonInteractiveSession()` 的结果
|
||||
|
||||
@@ -32,7 +32,7 @@ message_stop ← 消息结束
|
||||
|
||||
### 事件处理状态机
|
||||
|
||||
`src/services/api/claude.ts` 中 `queryStreamRaw()` 函数的事件处理循环实现了一个基于 `switch(part.type)` 的状态机:
|
||||
`src/services/api/claude.ts` 中 `queryModelWithStreaming()` 函数的事件处理循环实现了一个基于 `switch(part.type)` 的状态机:
|
||||
|
||||
| 事件类型 | 处理逻辑 | 状态变更 |
|
||||
|----------|----------|----------|
|
||||
@@ -167,10 +167,13 @@ UI 层通过 `useToolCallProgress` hook 实时展示命令输出,而不是等
|
||||
|
||||
| Provider | 流式协议 | 特殊处理 |
|
||||
|----------|----------|----------|
|
||||
| **Anthropic Direct** | 原生 SSE | 延迟最低,TTFT 最快 |
|
||||
| **firstParty** (Anthropic Direct) | 原生 SSE | 延迟最低,TTFT 最快 |
|
||||
| **AWS Bedrock** | AWS SDK 流式接口 | 需要额外的 beta header 和认证 |
|
||||
| **Google Vertex** | gRPC → 事件流 | 通过 `getMergedBetas()` 适配 |
|
||||
| **Azure** | Anthropic 兼容 API | 自定义 base URL |
|
||||
| **foundry** | Anthropic 兼容 API | 内部部署 |
|
||||
| **openai** | OpenAI 流式适配器 | 转换为 Anthropic 内部格式 |
|
||||
| **gemini** | Gemini 流式适配器 | 转换为 Anthropic 内部格式 |
|
||||
| **grok** (xAI) | Grok 流式适配器 | 转换为 Anthropic 内部格式 |
|
||||
|
||||
所有 Provider 通过统一的 `Stream<BetaRawMessageStreamEvent>` 抽象层屏蔽差异。上层代码(QueryEngine、REPL)不需要关心底层用的是哪个 Provider。
|
||||
|
||||
|
||||
@@ -74,17 +74,17 @@ const toolUpdates = streamingToolExecutor
|
||||
|
||||
| 终止原因 | 触发位置 | 机制 |
|
||||
|----------|---------|------|
|
||||
| **blocking_limit** | 第 646 行 | Token 计数超过硬限制(非 autocompact 模式)→ 生成 PTL 错误消息 → 返回 |
|
||||
| **image_error** | 第 980 行 | `ImageSizeError` / `ImageResizeError` 异常 → 直接返回 |
|
||||
| **model_error** | 第 999 行 | `callModel()` 抛出不可恢复异常 → 生成错误消息 → 返回 |
|
||||
| **aborted_streaming** | 第 1054 行 | `abortController.signal.aborted`(流式阶段)→ 为未完成的 tool_use 生成合成 tool_result → 返回 |
|
||||
| **prompt_too_long** | 第 1178/1185 行 | 413 错误且 reactive compact 无法恢复 → 暂扣的错误消息被释放 → 返回 |
|
||||
| **completed** | 第 1267 行 | API 错误(限流、认证失败等)导致无法继续 → 返回 |
|
||||
| **stop_hook_prevented** | 第 1282 行 | Stop hook 返回 `preventContinuation: true` → 返回 |
|
||||
| **completed** | 第 1360 行 | 正常完成:AI 未发出 tool_use → `needsFollowUp = false` → 经过 stop hooks → 返回 |
|
||||
| **aborted_tools** | 第 1518 行 | `abortController.signal.aborted`(工具执行阶段)→ 返回 |
|
||||
| **hook_stopped** | 第 1523 行 | 工具执行期间 hook 返回 `shouldPreventContinuation` → 返回 |
|
||||
| **max_turns** | 第 1714 行 | 轮次计数超过 `maxTurns` 限制 → 返回 |
|
||||
| **blocking_limit** | 第 686 行 | Token 计数超过硬限制(非 autocompact 模式)→ 生成 PTL 错误消息 → 返回 |
|
||||
| **image_error** | 第 1021 行 | `ImageSizeError` / `ImageResizeError` 异常 → 直接返回 |
|
||||
| **model_error** | 第 1040 行 | `callModel()` 抛出不可恢复异常 → 生成错误消息 → 返回 |
|
||||
| **aborted_streaming** | 第 1095 行 | `abortController.signal.aborted`(流式阶段)→ 为未完成的 tool_use 生成合成 tool_result → 返回 |
|
||||
| **prompt_too_long** | 第 1219/1226 行 | 413 错误且 reactive compact 无法恢复 → 暂扣的错误消息被释放 → 返回 |
|
||||
| **completed** | 第 1308 行 | API 错误(限流、认证失败等)导致无法继续 → 返回 |
|
||||
| **stop_hook_prevented** | 第 1323 行 | Stop hook 返回 `preventContinuation: true` → 返回 |
|
||||
| **completed** | 第 1401 行 | 正常完成:AI 未发出 tool_use → `needsFollowUp = false` → 经过 stop hooks → 返回 |
|
||||
| **aborted_tools** | 第 1559 行 | `abortController.signal.aborted`(工具执行阶段)→ 返回 |
|
||||
| **hook_stopped** | 第 1564 行 | 工具执行期间 hook 返回 `shouldPreventContinuation` → 返回 |
|
||||
| **max_turns** | 第 1755 行 | 轮次计数超过 `maxTurns` 限制 → 返回 |
|
||||
|
||||
## 继续条件(恢复路径)
|
||||
|
||||
@@ -158,7 +158,7 @@ type State = {
|
||||
- **每一步都产生真实信息**:`runTools()` 返回的 `toolResults` 是 API 不可能预知的——命令输出、文件内容、错误信息
|
||||
- **动态上下文管理**:每轮迭代前都重新评估压缩需求(autocompact → microcompact → snip),基于最新的 token 计数
|
||||
- **错误即时恢复**:工具失败不需要推倒重来——stop hook 可以注入阻塞错误让 AI 修正策略
|
||||
- **用户可控**:`abortController.signal` 在循环的多个检查点被检测(第 1018、1048、1488 行),用户按 ESC 可以优雅中断
|
||||
- **用户可控**:`abortController.signal` 在循环的多个检查点被检测(第 1059、1095、1529 行),用户按 ESC 可以优雅中断
|
||||
- **成本控制**:Token Budget 在每轮终止前检查,防止 AI 无效循环
|
||||
|
||||
## 一个完整的迭代示例
|
||||
|
||||
17
docs/diagrams/agent-loop-simple.mmd
Normal file
17
docs/diagrams/agent-loop-simple.mmd
Normal file
@@ -0,0 +1,17 @@
|
||||
flowchart TB
|
||||
START((输入)) --> CTX["Context 管理"]
|
||||
CTX --> LLM["LLM 流式输出"]
|
||||
LLM --> TC{tool_use?}
|
||||
|
||||
TC --> |是| EXEC["执行工具"]
|
||||
EXEC --> CTX
|
||||
|
||||
TC --> |否| DONE((完成))
|
||||
|
||||
classDef proc fill:#eef,stroke:#66c,color:#224
|
||||
classDef decision fill:#fee,stroke:#c66,color:#422
|
||||
classDef io fill:#eff,stroke:#6cc,color:#244
|
||||
|
||||
class CTX,LLM,EXEC proc
|
||||
class TC decision
|
||||
class START,DONE io
|
||||
40
docs/diagrams/agent-loop.mmd
Normal file
40
docs/diagrams/agent-loop.mmd
Normal file
@@ -0,0 +1,40 @@
|
||||
flowchart TB
|
||||
START((输入)) --> CTX["Context 管理"]
|
||||
CTX --> PRE["Pre-sampling Hook"]
|
||||
PRE --> LLM["LLM 流式输出"]
|
||||
LLM --> TC{tool_use?}
|
||||
|
||||
TC --> |是| PERM{需权限?}
|
||||
PERM --> |是| USER["👤 用户审批"]
|
||||
USER --> |allow| TOOL_PRE
|
||||
USER --> |deny| DENIED["拒绝"]
|
||||
PERM --> |否| TOOL_PRE["Pre-tool Hook"]
|
||||
TOOL_PRE --> EXEC["并发执行工具"]
|
||||
EXEC --> TOOL_POST["Post-tool Hook"]
|
||||
TOOL_POST --> CTX
|
||||
DENIED --> CTX
|
||||
|
||||
TC --> |否| POST["Post-sampling Hook"]
|
||||
POST --> STOP{"Stop Hook"}
|
||||
STOP --> |不通过| CTX
|
||||
STOP --> |通过| BUDGET{"Token Budget"}
|
||||
BUDGET --> |继续| CTX
|
||||
BUDGET --> |完成| DONE((完成))
|
||||
|
||||
subgraph SUB["子 Agent"]
|
||||
FORK["AgentTool"] --> RECURSE["递归调用"]
|
||||
end
|
||||
|
||||
EXEC -.-> FORK
|
||||
|
||||
classDef proc fill:#eef,stroke:#66c,color:#224
|
||||
classDef decision fill:#fee,stroke:#c66,color:#422
|
||||
classDef hook fill:#ffe,stroke:#cc6,color:#442
|
||||
classDef io fill:#eff,stroke:#6cc,color:#244
|
||||
classDef sub fill:#efe,stroke:#6a6,color:#242
|
||||
|
||||
class CTX,LLM,EXEC proc
|
||||
class TC,PERM,STOP,BUDGET decision
|
||||
class PRE,TOOL_PRE,TOOL_POST,POST hook
|
||||
class START,DONE,USER,DENIED io
|
||||
class FORK,RECURSE sub
|
||||
@@ -12,7 +12,7 @@ Claude Code 的 Agent 不仅仅来自用户自定义——系统有三类来源
|
||||
|
||||
| 来源 | 位置 | 优先级 |
|
||||
|------|------|--------|
|
||||
| **Built-in** | `src/tools/AgentTool/built-in/` 硬编码 | 最低(可被覆盖) |
|
||||
| **Built-in** | `packages/builtin-tools/src/tools/AgentTool/built-in/` 硬编码 | 最低(可被覆盖) |
|
||||
| **Plugin** | 通过插件系统注册 | 中 |
|
||||
| **User/Project/Policy** | `.claude/agents/*.md` 或 settings.json | 最高 |
|
||||
|
||||
@@ -127,7 +127,7 @@ color: "blue" # 终端中的 Agent 颜色标识
|
||||
以内置 Explore Agent 为例:
|
||||
|
||||
```typescript
|
||||
// src/tools/AgentTool/built-in/exploreAgent.ts
|
||||
// packages/builtin-tools/src/tools/AgentTool/built-in/exploreAgent.ts
|
||||
disallowedTools: [
|
||||
'Agent', // 不能嵌套调用 Agent
|
||||
'ExitPlanMode', // 不需要 plan mode
|
||||
|
||||
@@ -240,7 +240,7 @@ SDK 非交互模式下信任是隐式的(`getIsNonInteractiveSession()` 为 tr
|
||||
|
||||
## Session Hook 的生命周期
|
||||
|
||||
Agent 和 Skill 的前置 Hook 通过 `registerFrontmatterHooks()` 注册(调用位置:`src/tools/AgentTool/runAgent.ts`;定义位置:`src/utils/hooks/registerFrontmatterHooks.ts`),绑定到 agent 的 session ID。Agent 结束时通过 `clearSessionHooks()`(定义位置:`src/utils/hooks/sessionHooks.ts`)清理。
|
||||
Agent 和 Skill 的前置 Hook 通过 `registerFrontmatterHooks()` 注册(调用位置:`packages/builtin-tools/src/tools/AgentTool/runAgent.ts`;定义位置:`src/utils/hooks/registerFrontmatterHooks.ts`),绑定到 agent 的 session ID。Agent 结束时通过 `clearSessionHooks()`(定义位置:`src/utils/hooks/sessionHooks.ts`)清理。
|
||||
|
||||
```typescript
|
||||
// runAgent.ts — 注册 agent 的前置 Hook
|
||||
|
||||
346
docs/extensibility/mcp-configuration.mdx
Normal file
346
docs/extensibility/mcp-configuration.mdx
Normal file
@@ -0,0 +1,346 @@
|
||||
---
|
||||
title: "MCP 配置 - 多来源合并、作用域与策略管控"
|
||||
description: "详细说明 Claude Code MCP 配置的来源层次、合并优先级、传输类型、企业策略管控、插件集成和保留名称机制。"
|
||||
keywords: ["MCP", "配置", "settings.json", ".mcp.json", "企业策略", "插件"]
|
||||
---
|
||||
|
||||
## 配置来源与作用域
|
||||
|
||||
Claude Code 的 MCP 配置来自多个来源,每个来源对应一个 `scope`(作用域)。配置按优先级合并,高优先级来源的同名配置覆盖低优先级。
|
||||
|
||||
### 来源列表
|
||||
|
||||
| 来源 | Scope | 文件/接口 | 说明 |
|
||||
|------|-------|----------|------|
|
||||
| 企业管控 | `enterprise` | 系统管理路径 `managed-mcp.json` | **排他模式**:存在时忽略所有其他来源 |
|
||||
| 本地项目 | `local` | `<project>/.claude/settings.local.json` | 项目级私有配置(不提交到 VCS) |
|
||||
| 项目配置 | `project` | `<project>/.mcp.json` | 项目级共享配置(可提交到 VCS) |
|
||||
| 用户全局 | `user` | `~/.claude/settings.json` | 用户级配置,所有项目共享 |
|
||||
| 插件 | `dynamic` | 插件 manifest 中 `.mcp.json` / `.mcpb` | 插件提供的 MCP 服务器 |
|
||||
| claude.ai | `claudeai` | 通过 API 获取 | claude.ai 网页端配置的连接器 |
|
||||
| 内置动态 | `dynamic` | 代码中注册 | Computer Use / Chrome 等内置服务器 |
|
||||
| IDE SDK | `sdk` | IDE 传入 | VS Code / JetBrains 嵌入模式 |
|
||||
|
||||
### 合并优先级(从低到高)
|
||||
|
||||
```
|
||||
claude.ai 连接器 ← 最低优先级
|
||||
↓ 去重
|
||||
插件服务器
|
||||
↓ 去重
|
||||
用户全局配置
|
||||
↓
|
||||
项目配置(.mcp.json) ← 需要用户审批
|
||||
↓
|
||||
本地项目配置
|
||||
↓
|
||||
动态配置(内置 MCP) ← 最高优先级
|
||||
```
|
||||
|
||||
`Object.assign({}, dedupedPluginServers, userServers, approvedProjectServers, localServers)` 实现合并——后出现的同名键覆盖前者。
|
||||
|
||||
## 企业管控模式
|
||||
|
||||
当 `managed-mcp.json` 文件存在时,进入 **排他模式**:
|
||||
|
||||
```typescript
|
||||
// config.ts:1084
|
||||
if (doesEnterpriseMcpConfigExist()) {
|
||||
// 只返回企业配置,忽略所有用户/项目/插件/claude.ai 配置
|
||||
return { servers: filtered, errors: [] }
|
||||
}
|
||||
```
|
||||
|
||||
特性:
|
||||
- 路径由系统管理决定(`getManagedFilePath()` + `managed-mcp.json`)
|
||||
- 覆盖所有用户级、项目级、插件和 claude.ai 配置
|
||||
- 仍然应用策略过滤(allowlist/denylist)
|
||||
- 无法通过 CLI 添加新服务器(`addMcpConfig` 会拒绝)
|
||||
|
||||
## 传输类型与配置 Schema
|
||||
|
||||
### stdio(默认)
|
||||
|
||||
启动子进程,通过 stdin/stdout JSON-RPC 通信。
|
||||
|
||||
```json
|
||||
{
|
||||
"my-server": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@my-org/mcp-server"],
|
||||
"env": { "API_KEY": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`type` 字段可省略(默认为 `stdio`)。环境变量通过 `env` 传递给子进程,会与当前进程环境合并。
|
||||
|
||||
**Windows 注意**:使用 `npx` 需要包装为 `cmd /c npx`,否则会报错。
|
||||
|
||||
### SSE(Server-Sent Events)
|
||||
|
||||
通过 HTTP SSE 连接远程 MCP 服务器。
|
||||
|
||||
```json
|
||||
{
|
||||
"my-remote": {
|
||||
"type": "sse",
|
||||
"url": "https://mcp.example.com/sse",
|
||||
"headers": { "Authorization": "Bearer ..." },
|
||||
"oauth": {
|
||||
"clientId": "...",
|
||||
"authServerMetadataUrl": "https://auth.example.com/.well-known/oauth-authorization-server"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
支持 OAuth 认证流程。认证失败时进入 `needs-auth` 状态,15 分钟 TTL 缓存避免重复提示。
|
||||
|
||||
### HTTP(Streamable HTTP)
|
||||
|
||||
HTTP 流式传输。
|
||||
|
||||
```json
|
||||
{
|
||||
"my-http": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.example.com/mcp",
|
||||
"headers": { "X-API-Key": "..." }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
支持与 SSE 相同的 OAuth 配置。
|
||||
|
||||
### WebSocket
|
||||
|
||||
```json
|
||||
{
|
||||
"my-ws": {
|
||||
"type": "ws",
|
||||
"url": "wss://mcp.example.com/ws"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### IDE 专用类型(内部)
|
||||
|
||||
`sse-ide` 和 `ws-ide` 是 IDE 扩展专用类型,不由用户直接配置。
|
||||
|
||||
- `sse-ide`:使用 lockfile token 认证
|
||||
- `ws-ide`:使用 `X-Claude-Code-Ide-Authorization` header
|
||||
|
||||
### SDK 类型(内部)
|
||||
|
||||
`type: "sdk"` 由 IDE 嵌入模式传入,不经过保留名称检查和企业管控排他限制。
|
||||
|
||||
### claude.ai 代理类型(内部)
|
||||
|
||||
`type: "claudeai-proxy"` 由 claude.ai 网页端配置的连接器使用,通过 OAuth bearer token 认证并支持 401 重试。
|
||||
|
||||
## 配置操作
|
||||
|
||||
### 添加 MCP 服务器
|
||||
|
||||
通过 CLI 命令 `claude mcp add` 或 API 调用 `addMcpConfig()`:
|
||||
|
||||
```bash
|
||||
# 添加到用户配置
|
||||
claude mcp add my-server -s user -- npx @my-org/mcp-server
|
||||
|
||||
# 添加到项目配置
|
||||
claude mcp add my-server -s project -- npx @my-org/mcp-server
|
||||
|
||||
# 添加 HTTP 类型
|
||||
claude mcp add my-remote -s user -t http -u https://mcp.example.com/mcp
|
||||
```
|
||||
|
||||
添加时的验证流程:
|
||||
|
||||
1. **名称校验**:只允许字母、数字、连字符和下划线
|
||||
2. **保留名检查**:`claude-in-chrome` 和 `computer-use` 被保留
|
||||
3. **企业管控检查**:企业模式下拒绝添加
|
||||
4. **Schema 验证**:Zod 校验配置格式
|
||||
5. **策略检查**:denylist 拒绝、allowlist 验证
|
||||
|
||||
### 移除 MCP 服务器
|
||||
|
||||
```bash
|
||||
claude mcp remove my-server -s user
|
||||
```
|
||||
|
||||
### 列出 MCP 服务器
|
||||
|
||||
```bash
|
||||
claude mcp list
|
||||
```
|
||||
|
||||
## 项目配置审批
|
||||
|
||||
`.mcp.json` 中的项目配置需要用户显式审批才能生效:
|
||||
|
||||
```typescript
|
||||
// config.ts:1166
|
||||
const approvedProjectServers: Record<string, ScopedMcpServerConfig> = {}
|
||||
for (const [name, config] of Object.entries(projectServers)) {
|
||||
if (getProjectMcpServerStatus(name) === 'approved') {
|
||||
approvedProjectServers[name] = config
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
首次打开项目时,Claude Code 会提示用户审批 `.mcp.json` 中的每个服务器。审批状态持久化在本地配置中。
|
||||
|
||||
## 插件 MCP 集成
|
||||
|
||||
插件通过 manifest 中的 `.mcp.json` 或 `.mcpb` 文件声明 MCP 服务器:
|
||||
|
||||
```typescript
|
||||
// 插件 MCP 加载流程
|
||||
const pluginResult = await loadAllPluginsCacheOnly()
|
||||
const pluginServerResults = await Promise.all(
|
||||
pluginResult.enabled.map(plugin => getPluginMcpServers(plugin, mcpErrors))
|
||||
)
|
||||
```
|
||||
|
||||
### 插件命名空间
|
||||
|
||||
插件 MCP 服务器名格式为 `plugin:<pluginName>:<serverName>`,不会与手动配置的名称冲突。
|
||||
|
||||
### 去重机制
|
||||
|
||||
插件服务器通过内容签名去重(`dedupPluginMcpServers`):
|
||||
|
||||
- **stdio 类型**:签名 = `stdio:` + JSON.stringify([command, ...args])
|
||||
- **URL 类型**:签名 = `url:` + 原始 URL(unwrap CCR proxy URL)
|
||||
- **sdk 类型**:签名为 null,不去重
|
||||
|
||||
去重规则:
|
||||
1. 手动配置优先于插件配置
|
||||
2. 先加载的插件优先于后加载的
|
||||
3. 被抑制的插件服务器在 `/plugin` UI 中显示提示
|
||||
|
||||
### claude.ai 连接器去重
|
||||
|
||||
claude.ai 连接器使用相同的内容签名机制去重(`dedupClaudeAiMcpServers`):
|
||||
- 仅启用的手动配置参与去重(禁用的手动配置不应抑制连接器)
|
||||
- 连接器名格式为 `claude.ai <DisplayName>`
|
||||
|
||||
## 策略管控
|
||||
|
||||
### Allowlist / Denylist
|
||||
|
||||
企业策略通过 allowlist 和 denylist 控制可用的 MCP 服务器:
|
||||
|
||||
```typescript
|
||||
// config.ts:1243 - 最终策略过滤
|
||||
for (const [name, serverConfig] of Object.entries(configs)) {
|
||||
if (!isMcpServerAllowedByPolicy(name, serverConfig)) {
|
||||
continue // 跳过策略禁止的服务器
|
||||
}
|
||||
filtered[name] = serverConfig
|
||||
}
|
||||
```
|
||||
|
||||
策略检查考虑:
|
||||
- 服务器名称匹配
|
||||
- stdio 类型的 command + args 匹配
|
||||
- URL 类型的 URL 模式匹配(支持通配符)
|
||||
|
||||
### 插件专用模式
|
||||
|
||||
`isRestrictedToPluginOnly('mcp')` 启用时,只允许插件提供的 MCP 服务器——用户/项目级配置被忽略。
|
||||
|
||||
## 环境变量展开
|
||||
|
||||
MCP 配置中的环境变量支持 `$VAR` 和 `${VAR}` 语法展开:
|
||||
|
||||
```json
|
||||
{
|
||||
"my-server": {
|
||||
"command": "npx",
|
||||
"args": ["@my-org/mcp-server"],
|
||||
"env": {
|
||||
"API_KEY": "$MY_API_KEY",
|
||||
"DB_URL": "${DATABASE_URL}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
展开时缺失的变量会生成警告信息,但不阻止配置加载。
|
||||
|
||||
## 内置 MCP 动态注册
|
||||
|
||||
内置 MCP 服务器在 `main.tsx` 启动流程中动态注入配置:
|
||||
|
||||
### Computer Use MCP
|
||||
|
||||
```typescript
|
||||
// src/utils/computerUse/setup.ts
|
||||
export function setupComputerUseMCP(): {
|
||||
mcpConfig: Record<string, ScopedMcpServerConfig>
|
||||
allowedTools: string[]
|
||||
} {
|
||||
return {
|
||||
mcpConfig: {
|
||||
"computer-use": {
|
||||
type: "stdio",
|
||||
command: process.execPath,
|
||||
args: ["--computer-use-mcp"],
|
||||
scope: "dynamic",
|
||||
}
|
||||
},
|
||||
allowedTools: ["mcp__computer-use__screenshot", ...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
启用条件:
|
||||
- Feature flag `CHICAGO_MCP` 开启
|
||||
- `getPlatform() !== "unknown"`(macOS/Windows/Linux)
|
||||
- 非非交互式会话
|
||||
- GrowthBook gate `getChicagoEnabled()` 返回 true
|
||||
|
||||
### Claude in Chrome MCP
|
||||
|
||||
```typescript
|
||||
// 类似 Computer Use,在 main.tsx 中注册
|
||||
const { mcpConfig, allowedTools, systemPrompt } = setupClaudeInChrome()
|
||||
dynamicMcpConfig = { ...dynamicMcpConfig, ...mcpConfig }
|
||||
```
|
||||
|
||||
启用条件:
|
||||
- `--chrome` 参数或 `claudeInChromeDefaultEnabled` 配置
|
||||
- Chrome 扩展已安装
|
||||
|
||||
### VSCode SDK MCP
|
||||
|
||||
IDE 嵌入模式通过初始化消息传入 `type:'sdk'` 的配置,由 `setupVscodeSdkMcp()` 设置双向通知。
|
||||
|
||||
## 保留名称
|
||||
|
||||
以下 MCP 服务器名称被保留,用户无法手动配置同名服务器:
|
||||
|
||||
| 名称 | 用途 | 检查条件 |
|
||||
|------|------|---------|
|
||||
| `claude-in-chrome` | Chrome 浏览器控制 | 始终检查 |
|
||||
| `computer-use` | 桌面自动化 | `CHICAGO_MCP` feature flag 开启时检查 |
|
||||
| `claude-vscode` | VSCode IDE 集成 | 由 SDK 传入,不经过名称检查 |
|
||||
|
||||
保留名检查在两个位置:
|
||||
1. `addMcpConfig()`(`config.ts:636-648`)— 运行时拒绝
|
||||
2. `main.tsx` 启动检查(`main.tsx:2351-2368`)— 启动时退出
|
||||
|
||||
## 关键源文件索引
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `src/services/mcp/config.ts` | 配置管理核心:合并、去重、策略、添加/删除 |
|
||||
| `src/services/mcp/types.ts` | Zod Schema 定义、类型声明 |
|
||||
| `src/services/mcp/client.ts` | 连接管理、传输层选择 |
|
||||
| `src/utils/plugins/mcpPluginIntegration.ts` | 插件 MCP 配置加载 |
|
||||
| `src/utils/computerUse/setup.ts` | Computer Use 动态注册 |
|
||||
| `src/utils/claudeInChrome/common.ts` | Chrome MCP 保留名与工具名 |
|
||||
| `src/services/mcp/vscodeSdkMcp.ts` | VSCode SDK 双向通知 |
|
||||
@@ -1,25 +1,32 @@
|
||||
---
|
||||
title: "MCP 协议 - 连接管理、工具发现与执行链路"
|
||||
description: "从源码角度解析 Claude Code 的 MCP 集成:7 种传输层实现、connectToServer 的 memoize 缓存、工具发现的 LRU 策略、认证状态机、以及 MCP 工具如何进入权限检查链路。"
|
||||
keywords: ["MCP", "Model Context Protocol", "工具扩展", "MCP 客户端", "工具发现"]
|
||||
description: "从源码角度解析 Claude Code 的 MCP 集成:内置 MCP 与外部 MCP 的区别、7 种传输层实现、connectToServer 的 memoize 缓存、工具发现的 LRU 策略、认证状态机、以及 MCP 工具如何进入权限检查链路。"
|
||||
keywords: ["MCP", "Model Context Protocol", "工具扩展", "MCP 客户端", "工具发现", "内置 MCP", "外部 MCP"]
|
||||
---
|
||||
|
||||
{/* 本章目标:从源码角度揭示 MCP 客户端的连接管理、工具发现协议和执行链路 */}
|
||||
{/* 本章目标:从源码角度揭示 MCP 客户端的两种运行模式(内置/外部)、连接管理、工具发现协议和执行链路 */}
|
||||
|
||||
## 架构总览:从配置到可用工具
|
||||
|
||||
```
|
||||
settings.json: { mcpServers: { "my-db": { command: "npx", args: [...] } } }
|
||||
配置层(多来源合并)
|
||||
├── settings.json: { mcpServers: { "my-db": { command: "npx", args: [...] } } } ← 外部
|
||||
├── .mcp.json: 项目级 MCP 配置 ← 外部
|
||||
├── 插件 manifest (.mcp.json / .mcpb) ← 外部(插件)
|
||||
├── claude.ai connectors ← 外部(远程)
|
||||
├── enterprise managed-mcp.json ← 外部(企业管控)
|
||||
├── setupComputerUseMCP() / setupClaudeInChrome() ← 内置(动态注册)
|
||||
└── SDK 传入 (type:'sdk') ← 内置(IDE 嵌入)
|
||||
↓
|
||||
getAllMcpConfigs() ← enterprise 独占或合并 user/project/local + plugin + claude.ai
|
||||
getAllMcpConfigs() ← enterprise 独占 或 合并 user/project/local + plugin + claude.ai
|
||||
↓
|
||||
useManageMCPConnections() ← React Hook 管理连接生命周期
|
||||
↓
|
||||
connectToServer(name, config) ← memoize 缓存(lodash memoize)
|
||||
├── 创建 Transport(stdio/sse/http/...)
|
||||
├── new Client() ← @modelcontextprotocol/sdk
|
||||
├── client.connect(transport) ← 超时控制(MCP_TIMEOUT, 默认 30s)
|
||||
└── 返回 MCPServerConnection ← { connected | failed | needs-auth | pending }
|
||||
├── 判断:内置 MCP → InProcessTransport(同进程)
|
||||
├── 判断:外部 stdio → StdioClientTransport(子进程)
|
||||
├── 判断:远程 SSE/HTTP/WS → 网络传输
|
||||
└── 返回 MCPServerConnection ← { connected | failed | needs-auth | pending | disabled }
|
||||
↓
|
||||
fetchToolsForClient(client) ← LRU(20) 缓存
|
||||
├── client.request({ method: 'tools/list' })
|
||||
@@ -30,19 +37,208 @@ assembleToolPool() ← 合并内置工具 + MCP 工具
|
||||
工具名格式: mcp__<serverName>__<toolName> ← buildMcpToolName()
|
||||
```
|
||||
|
||||
## 两种 MCP 模式:内置 vs 外部
|
||||
|
||||
Claude Code 的 MCP 实现区分 **内置 MCP 服务器** 和 **外部 MCP 服务器**。两者使用相同的客户端协议和工具发现机制,但在连接方式、生命周期管理和配置来源上完全不同。
|
||||
|
||||
### 内置 MCP 服务器
|
||||
|
||||
内置 MCP 服务器由 Claude Code 自身提供,无需用户手动配置。它们在启动时自动注册为 `dynamic` scope 的配置,并在同进程内运行。
|
||||
|
||||
| 服务器 | 名称 | 包路径 | Feature Flag | 启用方式 |
|
||||
|--------|------|--------|-------------|---------|
|
||||
| Computer Use | `computer-use` | `@ant/computer-use-mcp` | `CHICAGO_MCP` | GrowthBook gate + macOS + interactive |
|
||||
| Claude in Chrome | `claude-in-chrome` | `@ant/claude-for-chrome-mcp` | — | `--chrome` 参数或 `claudeInChromeDefaultEnabled` 配置 |
|
||||
| VSCode SDK | `claude-vscode` | — | — | IDE 嵌入模式 (type:`sdk`) |
|
||||
|
||||
#### InProcessTransport:零开销同进程通信
|
||||
|
||||
内置服务器通过 `InProcessTransport`(`src/services/mcp/InProcessTransport.ts`)运行,**不启动子进程**:
|
||||
|
||||
```typescript
|
||||
// 创建一对 linked transport —— 消息在两端之间直接传递
|
||||
const [clientTransport, serverTransport] = createLinkedTransportPair()
|
||||
|
||||
// server 端连接到 serverTransport
|
||||
inProcessServer = createComputerUseMcpServerForCli()
|
||||
await inProcessServer.connect(serverTransport)
|
||||
|
||||
// client 端使用 clientTransport(与外部 MCP 的 Client 相同接口)
|
||||
transport = clientTransport
|
||||
```
|
||||
|
||||
`InProcessTransport` 的核心设计:
|
||||
- `send()` 通过 `queueMicrotask()` 异步投递消息到对端,避免同步请求/响应的栈深度问题
|
||||
- `close()` 双向关闭,任一端关闭都会触发两端的 `onclose` 回调
|
||||
- 无网络开销、无 IPC 序列化、无进程启动时间
|
||||
|
||||
#### 动态注册流程
|
||||
|
||||
内置服务器在 `main.tsx` 的启动流程中注册,注入 `dynamicMcpConfig`:
|
||||
|
||||
```typescript
|
||||
// main.tsx: Computer Use MCP 动态注册
|
||||
if (feature("CHICAGO_MCP") && getPlatform() !== "unknown" && !getIsNonInteractiveSession()) {
|
||||
const { getChicagoEnabled } = await import("src/utils/computerUse/gates.js")
|
||||
if (getChicagoEnabled()) {
|
||||
const { setupComputerUseMCP } = await import("src/utils/computerUse/setup.js")
|
||||
const { mcpConfig, allowedTools } = setupComputerUseMCP()
|
||||
dynamicMcpConfig = { ...dynamicMcpConfig, ...mcpConfig }
|
||||
allowedTools.push(...cuTools)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`setupComputerUseMCP()` 返回的配置(`src/utils/computerUse/setup.ts`):
|
||||
|
||||
```typescript
|
||||
{
|
||||
"computer-use": {
|
||||
type: "stdio", // 类型标记为 stdio(但 client.ts 会拦截为 InProcessTransport)
|
||||
command: process.execPath,
|
||||
args: ["--computer-use-mcp"],
|
||||
scope: "dynamic", // 动态作用域,不持久化
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 连接时拦截
|
||||
|
||||
`connectToServer()` 在 `client.ts:906-944` 中根据服务器名拦截内置服务器:
|
||||
|
||||
```typescript
|
||||
// Chrome MCP — 在 process 内运行,避免 ~325MB 子进程
|
||||
if (isClaudeInChromeMCPServer(name)) {
|
||||
const { createChromeContext } = await import('../../utils/claudeInChrome/mcpServer.js')
|
||||
const { createClaudeForChromeMcpServer } = await import('@ant/claude-for-chrome-mcp')
|
||||
const { createLinkedTransportPair } = await import('./InProcessTransport.js')
|
||||
const context = createChromeContext(config.env)
|
||||
inProcessServer = createClaudeForChromeMcpServer(context)
|
||||
const [clientTransport, serverTransport] = createLinkedTransportPair()
|
||||
await inProcessServer.connect(serverTransport)
|
||||
transport = clientTransport
|
||||
}
|
||||
|
||||
// Computer Use MCP — 同理
|
||||
if (feature('CHICAGO_MCP') && isComputerUseMCPServer(name)) {
|
||||
const { createComputerUseMcpServerForCli } = await import('../../utils/computerUse/mcpServer.js')
|
||||
const { createLinkedTransportPair } = await import('./InProcessTransport.js')
|
||||
inProcessServer = await createComputerUseMcpServerForCli()
|
||||
const [clientTransport, serverTransport] = createLinkedTransportPair()
|
||||
await inProcessServer.connect(serverTransport)
|
||||
transport = clientTransport
|
||||
}
|
||||
```
|
||||
|
||||
#### 保留名称保护
|
||||
|
||||
内置服务器的名称被保留,用户无法手动添加同名配置(`config.ts:636-648`):
|
||||
|
||||
```typescript
|
||||
// 添加 MCP 配置时检查保留名
|
||||
if (isClaudeInChromeMCPServer(name)) {
|
||||
throw new Error(`Cannot add MCP server "${name}": this name is reserved.`)
|
||||
}
|
||||
if (feature('CHICAGO_MCP') && isComputerUseMCPServer(name)) {
|
||||
throw new Error(`Cannot add MCP server "${name}": this name is reserved.`)
|
||||
}
|
||||
```
|
||||
|
||||
启动时也有全局检查(`main.tsx:2351-2368`):如果用户配置中包含保留名(非 `type:'sdk'`),直接 `process.exit(1)`。
|
||||
|
||||
#### VSCode SDK MCP
|
||||
|
||||
VSCode SDK MCP 是特殊的内置模式。IDE(如 VS Code、JetBrains)通过嵌入方式启动 Claude Code,并传入 `type:'sdk'` 的 MCP 配置。这类配置:
|
||||
- 不经过保留名称检查(IDE 可以使用任意名称)
|
||||
- 不参与 enterprise MCP 的排他控制
|
||||
- 通过 VSCode SDK transport 连接
|
||||
- 支持双向通知(如 `file_updated`、`experiment_gates`)
|
||||
|
||||
```typescript
|
||||
// src/services/mcp/vscodeSdkMcp.ts
|
||||
export function setupVscodeSdkMcp(sdkClients: MCPServerConnection[]): void {
|
||||
const client = sdkClients.find(client => client.name === 'claude-vscode')
|
||||
if (client && client.type === 'connected') {
|
||||
// 注册 log_event 通知处理器
|
||||
client.client.setNotificationHandler(LogEventNotificationSchema(), ...)
|
||||
// 发送实验门控到 VSCode
|
||||
client.client.notification({ method: 'experiment_gates', params: { gates } })
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 外部 MCP 服务器
|
||||
|
||||
外部 MCP 服务器由用户在配置文件中声明,通过子进程或网络连接运行。
|
||||
|
||||
#### 配置来源
|
||||
|
||||
| 来源 | Scope | 文件位置 | 优先级 |
|
||||
|------|-------|---------|--------|
|
||||
| 项目配置 | `project` | `<project>/.mcp.json` | 最高(同名覆盖) |
|
||||
| 本地配置 | `local` | `<project>/.claude/settings.local.json` | 高 |
|
||||
| 用户配置 | `user` | `~/.claude/settings.json` | 中 |
|
||||
| 插件 | `dynamic` | 插件 manifest 中 `.mcp.json` | 中 |
|
||||
| claude.ai | `claudeai` | 通过 API 获取 | 低 |
|
||||
| 企业管控 | `enterprise` | 系统管理路径 `managed-mcp.json` | 排他(存在时覆盖全部) |
|
||||
|
||||
#### 配置示例
|
||||
|
||||
```json
|
||||
// settings.json / .mcp.json 中的 MCP 配置
|
||||
{
|
||||
"mcpServers": {
|
||||
// stdio 类型 — 启动子进程
|
||||
"my-database": {
|
||||
"command": "npx",
|
||||
"args": ["@my-org/db-mcp-server"],
|
||||
"env": { "DB_URL": "postgres://..." }
|
||||
},
|
||||
|
||||
// HTTP 流类型 — 远程服务器
|
||||
"remote-api": {
|
||||
"type": "http",
|
||||
"url": "https://api.example.com/mcp"
|
||||
},
|
||||
|
||||
// SSE 类型 — Server-Sent Events
|
||||
"realtime-feed": {
|
||||
"type": "sse",
|
||||
"url": "https://feed.example.com/sse"
|
||||
},
|
||||
|
||||
// WebSocket 类型
|
||||
"ws-service": {
|
||||
"type": "ws",
|
||||
"url": "wss://ws.example.com/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 配置合并与去重
|
||||
|
||||
`getAllMcpConfigs()`(`config.ts`)按优先级合并多个来源的配置:
|
||||
|
||||
1. 企业管控配置存在时,**独占返回**(忽略所有其他来源)
|
||||
2. 否则合并:user → project → local → plugin → claude.ai
|
||||
3. 插件与手动配置去重:通过 `getMcpServerSignature()` 生成内容签名(基于 command/args/url),插件配置被同名手动配置抑制
|
||||
4. `addScopeToServers()` 为每个配置项标注来源 scope
|
||||
|
||||
## 7 种传输层实现
|
||||
|
||||
`connectToServer()`(`client.ts:596-1643`)根据 `config.type` 分发到不同的 Transport 实现:
|
||||
|
||||
| 传输类型 | Transport 类 | 适用场景 | 认证方式 |
|
||||
|----------|-------------|---------|---------|
|
||||
| `stdio`(默认) | `StdioClientTransport` | 本地子进程 | 无 |
|
||||
| `stdio`(默认) | `StdioClientTransport` | 外部本地子进程 | 无 |
|
||||
| `sse` | `SSEClientTransport` | 远程 SSE 服务 | `ClaudeAuthProvider` + OAuth |
|
||||
| `http` | `StreamableHTTPClientTransport` | HTTP 流 | `ClaudeAuthProvider` + OAuth |
|
||||
| `sse-ide` | `SSEClientTransport` | IDE 集成 | lockfile token |
|
||||
| `ws-ide` | `WebSocketTransport` | IDE WebSocket | `X-Claude-Code-Ide-Authorization` |
|
||||
| `ws` | `WebSocketTransport` | WebSocket 服务 | session ingress token |
|
||||
| `claudeai-proxy` | `StreamableHTTPClientTransport` | claude.ai 代理 | OAuth bearer + 401 重试 |
|
||||
| InProcess(内置) | `InProcessTransport` | Computer Use / Chrome | 无(同进程) |
|
||||
|
||||
### stdio 传输的进程管理
|
||||
|
||||
@@ -108,13 +304,21 @@ timer.unref?.() // 不阻止进程退出
|
||||
|
||||
## 工具发现:从 MCP 到 Tool 接口
|
||||
|
||||
`fetchToolsForClient()`(`client.ts:1745-2000`)使用 `memoizeWithLRU` 缓存(上限 20),将 MCP 工具转换为 Claude Code 的统一 Tool 接口:
|
||||
`fetchToolsForClient()`(`client.ts:1744-2000`)使用 `memoizeWithLRU` 缓存(上限 100),将 MCP 工具转换为 Claude Code 的统一 Tool 接口:
|
||||
|
||||
```typescript
|
||||
const fullyQualifiedName = buildMcpToolName(client.name, tool.name)
|
||||
// 结果: "mcp__my-db__query"
|
||||
// 结果: "mcp__my-database__query"
|
||||
```
|
||||
|
||||
### 内置 MCP 的工具发现
|
||||
|
||||
内置 MCP 服务器虽然使用 InProcessTransport,但工具发现流程与外部服务器完全一致:
|
||||
|
||||
- **Computer Use**:`createComputerUseMcpServerForCli()` 在 `src/utils/computerUse/mcpServer.ts` 中构建 MCP Server 对象,注册 `ListToolsRequestSchema` handler。工具描述包含平台特定的已安装应用列表(1s 超时枚举)。
|
||||
- **Claude in Chrome**:`createClaudeForChromeMcpServer()` 在 `@ant/claude-for-chrome-mcp` 包中构建 Server,提供 17+ 个浏览器控制工具。
|
||||
- **VSCode SDK**:由 IDE 端提供工具列表,通过 SDK transport 传递。
|
||||
|
||||
### 工具描述截断
|
||||
|
||||
MCP 工具描述上限 2048 字符(`MAX_MCP_DESCRIPTION_LENGTH`)。OpenAPI 生成的 MCP 服务器曾观察到 15-60KB 的描述文档。
|
||||
@@ -134,6 +338,8 @@ MCP 工具描述上限 2048 字符(`MAX_MCP_DESCRIPTION_LENGTH`)。OpenAPI
|
||||
|
||||
MCP 工具默认返回 `{ behavior: 'passthrough' }`(`client.ts:1816-1834`),意味着它们始终进入权限确认流程。工具名使用 `mcp__` 前缀精确匹配权限规则。
|
||||
|
||||
内置 MCP 服务器的工具通过 `allowedTools` 列表自动授权——在 `main.tsx` 启动时加入,绕过普通权限提示。例如 Computer Use 工具的 `request_access` 自行处理会话级审批。
|
||||
|
||||
## MCP 工具的执行链路
|
||||
|
||||
```
|
||||
@@ -169,23 +375,33 @@ getRemoteMcpServerConnectionBatchSize() // 默认 20
|
||||
|
||||
本地 MCP 服务器(stdio)是重量级的子进程,默认限制 3 个并发连接。远程服务器是轻量级 HTTP 请求,允许 20 个并发。
|
||||
|
||||
## 实际配置示例
|
||||
## 内置 vs 外部 MCP 对比总结
|
||||
|
||||
```json
|
||||
// settings.json 中的 MCP 配置
|
||||
{
|
||||
"mcpServers": {
|
||||
"my-database": {
|
||||
"command": "npx",
|
||||
"args": ["@my-org/db-mcp-server"],
|
||||
"env": { "DB_URL": "postgres://..." }
|
||||
},
|
||||
"remote-api": {
|
||||
"type": "http",
|
||||
"url": "https://api.example.com/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
| 维度 | 内置 MCP | 外部 MCP |
|
||||
|------|---------|---------|
|
||||
| **Transport** | `InProcessTransport`(同进程) | stdio / SSE / HTTP / WebSocket |
|
||||
| **配置来源** | `setupComputerUseMCP()` / `setupClaudeInChrome()` 等动态注册 | settings.json / .mcp.json / 插件 / claude.ai |
|
||||
| **Scope** | `dynamic` | `user` / `project` / `local` / `enterprise` / `claudeai` |
|
||||
| **进程模型** | 同进程,零开销 | 子进程(stdio)或网络连接 |
|
||||
| **名称保护** | 保留名,用户不可添加同名 | 自由命名(字母数字 + `-_`) |
|
||||
| **生命周期** | 随 CLI 启停 | 连接缓存 + 按需重连 |
|
||||
| **权限** | `allowedTools` 自动授权 | `passthrough` 进入权限确认 |
|
||||
| **Feature Flag** | `CHICAGO_MCP`(Computer Use)等 | 无(始终可用) |
|
||||
| **工具发现** | 与外部相同(MCP 协议) | 标准 MCP `tools/list` |
|
||||
| **清理** | `inProcessServer.close()` | 信号升级策略 SIGINT→SIGTERM→SIGKILL |
|
||||
|
||||
配置后,AI 的工具列表中会出现 `mcp__my-database__query` 和 `mcp__remote-api__*` 工具——与内置工具使用相同的权限检查链路和 UI 渲染。
|
||||
## 关键源文件索引
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `src/services/mcp/client.ts` | 核心客户端:connectToServer、fetchToolsForClient、MCPTool.call |
|
||||
| `src/services/mcp/config.ts` | 配置管理:getAllMcpConfigs、addMcpConfig、removeMcpConfig |
|
||||
| `src/services/mcp/types.ts` | 类型定义:配置 Schema、连接状态类型 |
|
||||
| `src/services/mcp/InProcessTransport.ts` | 内置 MCP 传输层:linked transport pair |
|
||||
| `src/services/mcp/vscodeSdkMcp.ts` | VSCode SDK MCP:双向通知、实验门控 |
|
||||
| `src/services/mcp/useManageMCPConnections.ts` | React Hook:连接生命周期、重连 |
|
||||
| `src/utils/computerUse/mcpServer.ts` | Computer Use MCP Server 构建 |
|
||||
| `src/utils/computerUse/setup.ts` | Computer Use 动态注册 |
|
||||
| `src/utils/claudeInChrome/mcpServer.ts` | Chrome MCP Server 构建 + Bridge 配置 |
|
||||
| `src/tools/MCPTool/MCPTool.ts` | MCP 工具包装:统一 Tool 接口 |
|
||||
| `src/entrypoints/mcp.ts` | MCP server 入口(Claude Code 作为 MCP server) |
|
||||
|
||||
@@ -22,7 +22,7 @@ Skill 的核心洞见:**复杂任务的关键不在代码逻辑,而在 Promp
|
||||
|
||||
### 1. 内置命令(Built-in Commands)
|
||||
|
||||
硬编码在 `src/commands.ts:258` 的 `COMMANDS` memoize 数组中,包含 70+ 条命令(`/commit`、`/review`、`/compact` 等)。这些是 TypeScript 模块而非 Markdown,但实现了相同的 `Command` 接口(`src/types/command.ts`)。
|
||||
硬编码在 `src/commands.ts:299` 的 `COMMANDS` memoize 数组中,包含 70+ 条命令(`/commit`、`/review`、`/compact` 等)。这些是 TypeScript 模块而非 Markdown,但实现了相同的 `Command` 接口(`src/types/command.ts`)。
|
||||
|
||||
### 2. Bundled Skills(编译时打包)
|
||||
|
||||
@@ -98,7 +98,7 @@ shell: ["bash"] # Shell 执行环境
|
||||
|
||||
## 两条执行路径:Inline vs Fork
|
||||
|
||||
SkillTool(`src/tools/SkillTool/SkillTool.ts:332`)在 `call()` 中根据 `command.context` 分流:
|
||||
SkillTool(`packages/builtin-tools/src/tools/SkillTool/SkillTool.ts:332`)在 `call()` 中根据 `command.context` 分流:
|
||||
|
||||
### Inline 模式(默认)
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
| 11 | BigQuery Metrics | `api.anthropic.com/api/claude_code/metrics` | HTTPS | 默认启用 |
|
||||
| 12 | MCP Proxy | `mcp-proxy.anthropic.com` | HTTPS+WS | 使用 MCP 工具时 |
|
||||
| 13 | MCP Registry | `api.anthropic.com/mcp-registry` | HTTPS | 查询 MCP 服务器时 |
|
||||
| 14 | Bing Search | `www.bing.com` | HTTPS | WebSearch 工具 |
|
||||
| 14 | Web Search Pages | `www.bing.com`, `search.brave.com` | HTTPS | WebSearch 工具,可通过 `WEB_SEARCH_ADAPTER=bing|brave` 切换 |
|
||||
| 15 | Google Cloud Storage (更新) | `storage.googleapis.com` | HTTPS | 版本检查 |
|
||||
| 16 | GitHub Raw (Changelog/Stats) | `raw.githubusercontent.com` | HTTPS | 更新提示 |
|
||||
| 17 | Claude in Chrome Bridge | `bridge.claudeusercontent.com` | WSS | Chrome 集成 |
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
- **端点**: `{region}-aiplatform.googleapis.com`
|
||||
- **认证**: `GoogleAuth` + `cloud-platform` scope
|
||||
- **文件**: `src/services/api/client.ts:228-298`
|
||||
- **文件**: `src/services/api/client.ts:221-298`
|
||||
|
||||
### 4. Azure Foundry
|
||||
|
||||
@@ -121,16 +121,20 @@ Anthropic 托管的 MCP 服务器代理。
|
||||
- **端点**: `https://api.anthropic.com/mcp-registry/v0/servers?version=latest&visibility=commercial`
|
||||
- **文件**: `src/services/mcp/officialRegistry.ts`
|
||||
|
||||
### 14. Bing Search
|
||||
### 14. Web Search Pages
|
||||
|
||||
WebSearch 工具的默认适配器,抓取 Bing 搜索结果。
|
||||
WebSearch 工具支持直接抓取 Bing 搜索结果页面,也支持通过 Brave 的 LLM Context API
|
||||
获取搜索上下文;可通过 `WEB_SEARCH_ADAPTER=bing|brave` 显式切换后端。
|
||||
|
||||
- **端点**: `https://www.bing.com/search?q={query}&setmkt=en-US`
|
||||
- **文件**: `src/tools/WebSearchTool/adapters/bingAdapter.ts`
|
||||
- **Bing 端点**: `https://www.bing.com/search?q={query}&setmkt=en-US`
|
||||
- **Brave 端点**: `https://api.search.brave.com/res/v1/llm/context?q={query}`
|
||||
- **文件**:
|
||||
- `packages/builtin-tools/src/tools/WebSearchTool/adapters/bingAdapter.ts`
|
||||
- `packages/builtin-tools/src/tools/WebSearchTool/adapters/braveAdapter.ts`
|
||||
|
||||
另外还有 Domain Blocklist 查询:
|
||||
- **端点**: `https://api.anthropic.com/api/web/domain_info?domain={domain}`
|
||||
- **文件**: `src/tools/WebFetchTool/utils.ts`
|
||||
- **文件**: `packages/builtin-tools/src/tools/WebFetchTool/utils.ts`
|
||||
|
||||
### 15. Google Cloud Storage (自动更新)
|
||||
|
||||
@@ -201,6 +205,7 @@ WebSearch 工具的默认适配器,抓取 Bing 搜索结果。
|
||||
| `{region}-aiplatform.googleapis.com` | Google Vertex AI | HTTPS |
|
||||
| `{resource}.services.ai.azure.com` | Azure Foundry | HTTPS |
|
||||
| `www.bing.com` | Bing 搜索 | HTTPS |
|
||||
| `search.brave.com` | Brave 搜索 | HTTPS |
|
||||
| `storage.googleapis.com` | 自动更新 | HTTPS |
|
||||
| `raw.githubusercontent.com` | Changelog / 插件统计 | HTTPS |
|
||||
| `bridge.claudeusercontent.com` | Chrome Bridge | WSS |
|
||||
|
||||
201
docs/features/acp-link.md
Normal file
201
docs/features/acp-link.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# acp-link — ACP 代理服务器
|
||||
|
||||
> 源码目录:`packages/acp-link/`
|
||||
> PR: #292
|
||||
> 新增时间:2026-04-18
|
||||
|
||||
## 一、功能概述
|
||||
|
||||
`acp-link` 是一个 ACP (Agent Client Protocol) 代理服务器,将 WebSocket 客户端桥接到 ACP agent 的 stdio 接口。它让 ACP agent(如 Claude Code)可以通过 WebSocket 远程访问,而不仅限于本地 stdio。
|
||||
|
||||
### 核心特性
|
||||
|
||||
- **WebSocket → stdio 桥接**:将浏览器/远程客户端的 WebSocket 连接转换为 ACP agent 的 stdin/stdout NDJSON 流
|
||||
- **会话管理**:创建、加载、恢复、列出、关闭会话
|
||||
- **权限审批流程**:客户端可远程审批 agent 的工具权限请求
|
||||
- **RCS 集成**:可与 Remote Control Server (RCS) 连接,将 ACP agent 注册到 RCS 并通过 Web UI 交互
|
||||
- **HTTPS 支持**:内置自签名证书生成,支持安全连接
|
||||
- **Token 认证**:自动生成或通过环境变量配置认证 token
|
||||
|
||||
## 二、架构
|
||||
|
||||
### 独立模式
|
||||
|
||||
```
|
||||
┌──────────────────┐ WebSocket ┌──────────────────┐ stdio/NDJSON ┌──────────────┐
|
||||
│ 浏览器/客户端 │ ◄──────────────►│ acp-link │ ◄────────────────►│ ACP Agent │
|
||||
│ (WS Client) │ ws://host:port │ (Proxy Server) │ spawn subprocess │ (Claude等) │
|
||||
└──────────────────┘ └──────────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
### RCS 集成模式
|
||||
|
||||
```
|
||||
┌──────────────┐ WebSocket ┌──────────────────┐ stdio/NDJSON ┌──────────────┐
|
||||
│ RCS Web UI │ ◄──────────────►│ Remote Control │ ◄─────────────────►│ acp-link │
|
||||
│ (/code/*) │ ACP Relay WS │ Server (RCS) │ ACP events │ + Agent │
|
||||
└──────────────┘ └──────────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
### 文件结构
|
||||
|
||||
```
|
||||
packages/acp-link/
|
||||
├── src/
|
||||
│ ├── server.ts # 主服务器:WS 连接管理、会话管理、权限处理、消息桥接
|
||||
│ ├── rcs-upstream.ts # RCS 上游客户端:REST 注册 + WS identify 两步流程
|
||||
│ ├── cert.ts # TLS 证书生成(自签名)
|
||||
│ ├── logger.ts # 日志模块
|
||||
│ ├── types.ts # JSON-RPC 和 ACP 协议类型定义
|
||||
│ ├── cli/
|
||||
│ │ ├── bin.ts # CLI 入口
|
||||
│ │ ├── command.ts # 命令行参数解析
|
||||
│ │ ├── app.ts # 应用启动
|
||||
│ │ └── context.ts # 上下文配置
|
||||
│ └── __tests__/ # 测试(cert, server, types)
|
||||
├── package.json
|
||||
└── tsconfig.json
|
||||
```
|
||||
|
||||
## 三、安装与使用
|
||||
|
||||
### 基本用法
|
||||
|
||||
```bash
|
||||
# 直接运行(在 monorepo 中)
|
||||
# 注意:claude 本身不支持 ACP,需要用 ccb-bun --acp 启动 ACP agent
|
||||
bun packages/acp-link/src/cli/bin.ts ccb-bun -- --acp
|
||||
|
||||
# 指定端口和主机
|
||||
acp-link --port 9000 --host 0.0.0.0 ccb-bun -- --acp
|
||||
|
||||
# 启用 HTTPS(自签名证书)
|
||||
acp-link --https ccb-bun -- --acp
|
||||
|
||||
# 调试模式
|
||||
acp-link --debug ccb-bun -- --acp
|
||||
```
|
||||
|
||||
### CLI 参考
|
||||
|
||||
```
|
||||
USAGE
|
||||
acp-link [--port value] [--host value] [--debug] [--no-auth] [--https] <command>...
|
||||
acp-link --help
|
||||
acp-link --version
|
||||
|
||||
FLAGS
|
||||
[--port] Port to listen on [default = 9315]
|
||||
[--host] Host to bind to [default = localhost]
|
||||
[--debug] Enable debug logging to file
|
||||
[--no-auth] Disable authentication (dangerous)
|
||||
[--https] Enable HTTPS with self-signed cert
|
||||
-h --help Print help information and exit
|
||||
-v --version Print version information and exit
|
||||
|
||||
ARGUMENTS
|
||||
command... Agent command followed by its arguments (e.g. "ccb-bun -- --acp")
|
||||
```
|
||||
|
||||
## 四、认证
|
||||
|
||||
默认启动时自动生成随机 token。客户端连接时需通过 query 参数传递:
|
||||
|
||||
```
|
||||
ws://localhost:9315/ws?token=<your-token>
|
||||
```
|
||||
|
||||
配置固定 token:
|
||||
|
||||
```bash
|
||||
ACP_AUTH_TOKEN=my-fixed-token acp-link ccb-bun -- --acp
|
||||
```
|
||||
|
||||
禁用认证(不推荐,仅用于开发):
|
||||
|
||||
```bash
|
||||
acp-link --no-auth ccb-bun -- --acp
|
||||
```
|
||||
|
||||
## 五、RCS 集成
|
||||
|
||||
acp-link 支持将 ACP agent 注册到 Remote Control Server,通过 Web UI 远程操控。
|
||||
|
||||
### 连接方式
|
||||
|
||||
```bash
|
||||
# 通过环境变量配置 RCS 连接
|
||||
ACP_RCS_URL=http://localhost:3000 \
|
||||
ACP_RCS_TOKEN=sk-rcs-your-key \
|
||||
acp-link ccb-bun -- --acp
|
||||
```
|
||||
|
||||
### 注册流程(两步)
|
||||
|
||||
1. **REST 注册**:通过 `POST /v1/environments/bridge` 向 RCS 注册环境
|
||||
2. **WS identify**:建立 WebSocket 连接后发送 `identify` 消息(携带 agentId),替代完整 `register`
|
||||
|
||||
```
|
||||
acp-link RCS
|
||||
│ │
|
||||
│── POST /v1/environments/bridge ──►│ (REST 注册)
|
||||
│◄── { agentId, sessionId } ───────│
|
||||
│ │
|
||||
│── WS connect ─────────────────►│ (WebSocket)
|
||||
│── identify { agentId } ────────►│ (WS 标识)
|
||||
│◄── identified ─────────────────│
|
||||
│ │
|
||||
│── ACP events ─────────────────►│ (双向消息转发)
|
||||
│◄── user prompts/permissions ───│
|
||||
```
|
||||
|
||||
## 六、权限模式
|
||||
|
||||
### permissionMode 传递链
|
||||
|
||||
权限模式通过整条链路传递:Web UI → RCS → acp-link → ACP agent。
|
||||
|
||||
支持的权限模式:
|
||||
- `default` — 每次请求权限确认
|
||||
- `auto` — 自动判断
|
||||
- `acceptEdits` — 自动接受编辑
|
||||
- `plan` — 规划模式
|
||||
- `dontAsk` — 不询问
|
||||
- `bypassPermissions` — 绕过权限(需 sandbox 环境)
|
||||
|
||||
### fallback 链
|
||||
|
||||
当客户端未显式传递 permissionMode 时,使用以下 fallback 链:
|
||||
|
||||
```
|
||||
客户端传值 > config.permissionMode > ACP_PERMISSION_MODE 环境变量
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
```bash
|
||||
ACP_PERMISSION_MODE=auto acp-link ccb-bun -- --acp
|
||||
```
|
||||
|
||||
## 七、权限管道(2026-04-18 改进)
|
||||
|
||||
### 模式同步
|
||||
|
||||
`applySessionMode` 在 agent 切换权限模式时同步 `appState.toolPermissionContext.mode`,确保内部权限上下文与 ACP 客户端状态一致。
|
||||
|
||||
### 统一权限流水线
|
||||
|
||||
`createAcpCanUseTool` 接入 `hasPermissionsToUseTool` 统一权限流水线,替代原来分散的处理逻辑。支持 `onModeChange` 回调,模式变更时实时同步。
|
||||
|
||||
### bypass 检测
|
||||
|
||||
`bypassPermissions` 模式增加可用性检测 — 仅在非 root 或 sandbox 环境中允许启用,防止权限绕过的安全风险。
|
||||
|
||||
## 八、环境变量
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `ACP_AUTH_TOKEN` | 固定认证 token(默认自动生成) |
|
||||
| `ACP_PERMISSION_MODE` | 默认权限模式 fallback |
|
||||
| `ACP_RCS_URL` | RCS 服务器地址(启用 RCS 集成) |
|
||||
| `ACP_RCS_TOKEN` | RCS API token |
|
||||
189
docs/features/acp-zed.md
Normal file
189
docs/features/acp-zed.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# ACP (Agent Client Protocol) — Zed / IDE 集成
|
||||
|
||||
> Feature Flag: `FEATURE_ACP=1`(build 和 dev 模式默认启用)
|
||||
> 实现状态:可用(支持 Zed、Cursor 等 ACP 客户端)
|
||||
> 源码目录:`src/services/acp/`
|
||||
|
||||
## 一、功能概述
|
||||
|
||||
ACP (Agent Client Protocol) 是一种标准化的 stdio 协议,允许 IDE 和编辑器通过 stdin/stdout 的 NDJSON 流驱动 AI Agent。CCB 实现了完整的 ACP agent 端,可以被 Zed、Cursor 等支持 ACP 的客户端直接调用。
|
||||
|
||||
### 核心特性
|
||||
|
||||
- **会话管理**:新建 / 恢复 / 加载 / 分叉 / 关闭会话
|
||||
- **历史回放**:恢复会话时自动加载并回放对话历史
|
||||
- **权限桥接**:ACP 客户端的权限决策映射到 CCB 的工具权限系统
|
||||
- **斜杠命令 & Skills**:加载真实命令列表,支持 `/commit`、`/review` 等 prompt 型 skill
|
||||
- **Context Window 跟踪**:精确的 usage_update,含 model prefix matching
|
||||
- **Prompt 排队**:支持连续发送多条 prompt,自动排队处理
|
||||
- **模式切换**:auto / default / acceptEdits / plan / dontAsk / bypassPermissions
|
||||
- **模型切换**:运行时切换 AI 模型
|
||||
|
||||
## 二、架构
|
||||
|
||||
```
|
||||
┌──────────────┐ NDJSON/stdio ┌──────────────────┐
|
||||
│ Zed / IDE │ ◄────────────────► │ CCB ACP Agent │
|
||||
│ (Client) │ stdin / stdout │ (Agent) │
|
||||
└──────────────┘ │ │
|
||||
│ entry.ts │ ← stdio → NDJSON stream
|
||||
│ agent.ts │ ← ACP protocol handler
|
||||
│ bridge.ts │ ← SDKMessage → ACP SessionUpdate
|
||||
│ permissions.ts │ ← 权限桥接
|
||||
│ utils.ts │ ← 通用工具
|
||||
│ │
|
||||
│ QueryEngine │ ← 内部查询引擎
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
### 文件职责
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `entry.ts` | 入口,创建 stdio → NDJSON stream,启动 `AgentSideConnection` |
|
||||
| `agent.ts` | 实现 ACP `Agent` 接口:会话 CRUD、prompt、cancel、模式/模型切换 |
|
||||
| `bridge.ts` | `SDKMessage` → ACP `SessionUpdate` 转换:文本/思考/工具/用量/编辑 diff |
|
||||
| `permissions.ts` | ACP `requestPermission()` → CCB `CanUseToolFn` 桥接 |
|
||||
| `utils.ts` | Pushable、流转换、权限模式解析、session fingerprint、路径显示 |
|
||||
|
||||
## 三、配置 Zed 编辑器
|
||||
|
||||
### 3.1 Zed settings.json 配置
|
||||
|
||||
打开 Zed 的 `settings.json`(`Cmd+,` → Open Settings),添加 `agent_servers` 配置:
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_servers": {
|
||||
"ccb": {
|
||||
"type": "custom",
|
||||
"command": "ccb",
|
||||
"args": ["--acp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 API 认证配置
|
||||
|
||||
CCB 的 ACP agent 在启动时会自动加载 `settings.json` 中的环境变量(`ANTHROPIC_BASE_URL`、`ANTHROPIC_AUTH_TOKEN` 等)。确保已通过 `/login` 配置好 API 供应商。
|
||||
|
||||
也可通过环境变量传入:
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_servers": {
|
||||
"claude-code": {
|
||||
"command": "ccb",
|
||||
"args": ["--acp"],
|
||||
"env": {
|
||||
"ANTHROPIC_BASE_URL": "https://api.example.com/v1",
|
||||
"ANTHROPIC_AUTH_TOKEN": "sk-xxx"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 在 Zed 中使用
|
||||
|
||||
1. 配置完成后重启 Zed
|
||||
2. 打开任意项目目录
|
||||
3. 按 `Cmd+'`(macOS)或 `Ctrl+'`(Linux)打开 Agent Panel
|
||||
4. 在 Agent Panel 顶部的下拉菜单中选择 **claude-code**
|
||||
5. 开始对话
|
||||
|
||||
### 3.5 功能说明
|
||||
|
||||
| 功能 | 操作 |
|
||||
|------|------|
|
||||
| 对话 | 在 Agent Panel 中直接输入消息 |
|
||||
| 斜杠命令 | 输入 `/` 查看可用 skills 列表(如 `/commit`、`/review`) |
|
||||
| 工具权限 | 弹出权限请求时选择 Allow / Reject / Always Allow |
|
||||
| 模式切换 | 通过 Agent Panel 的设置菜单切换 auto/default/plan 等模式 |
|
||||
| 模型切换 | 通过 Agent Panel 的设置菜单切换 AI 模型 |
|
||||
| 会话恢复 | 关闭重开 Zed 后,之前的会话可自动恢复(含历史消息) |
|
||||
|
||||
## 四、配置其他 ACP 客户端
|
||||
|
||||
ACP 是开放协议,任何支持 ACP 的客户端都可以连接 CCB。通用配置模式:
|
||||
|
||||
```
|
||||
命令: ccb --acp
|
||||
参数: ["--acp"]
|
||||
通信: stdin/stdout NDJSON
|
||||
协议版本: ACP v1
|
||||
```
|
||||
|
||||
### 4.1 Cursor
|
||||
|
||||
在 Cursor 的设置中配置 MCP / Agent Server,使用同样的 `ccb --acp` 命令。
|
||||
|
||||
### 4.2 自定义客户端
|
||||
|
||||
使用 `@agentclientprotocol/sdk` 可以快速构建 ACP 客户端:
|
||||
|
||||
```typescript
|
||||
import { ClientSideConnection, ndJsonStream } from '@agentclientprotocol/sdk'
|
||||
|
||||
// 创建连接(将 ccb --acp 作为子进程启动)
|
||||
const child = spawn('ccb', ['--acp'])
|
||||
const stream = ndJsonStream(
|
||||
Writable.toWeb(child.stdin),
|
||||
Readable.toWeb(child.stdout),
|
||||
)
|
||||
|
||||
const client = new ClientSideConnection(stream)
|
||||
|
||||
// 初始化
|
||||
await client.initialize({ clientCapabilities: {} })
|
||||
|
||||
// 创建会话
|
||||
const { sessionId } = await client.newSession({
|
||||
cwd: '/path/to/project',
|
||||
})
|
||||
|
||||
// 发送 prompt
|
||||
const response = await client.prompt({
|
||||
sessionId,
|
||||
prompt: [{ type: 'text', text: 'Hello, explain this project' }],
|
||||
})
|
||||
|
||||
// 监听 session 更新
|
||||
client.on('sessionUpdate', (update) => {
|
||||
console.log('Update:', update)
|
||||
})
|
||||
```
|
||||
|
||||
## 五、ACP 协议支持矩阵
|
||||
|
||||
| 方法 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| `initialize` | ✅ | 返回 agent 信息和能力 |
|
||||
| `authenticate` | ✅ | 无需认证(自托管) |
|
||||
| `newSession` | ✅ | 创建新会话 |
|
||||
| `resumeSession` | ✅ | 恢复已有会话(含历史回放) |
|
||||
| `loadSession` | ✅ | 加载指定会话(含历史回放) |
|
||||
| `listSessions` | ✅ | 列出可用会话 |
|
||||
| `forkSession` | ✅ | 分叉会话 |
|
||||
| `closeSession` | ✅ | 关闭会话 |
|
||||
| `prompt` | ✅ | 发送消息,支持排队 |
|
||||
| `cancel` | ✅ | 取消当前/排队的 prompt |
|
||||
| `setSessionMode` | ✅ | 切换权限模式 |
|
||||
| `setSessionModel` | ✅ | 切换 AI 模型 |
|
||||
| `setSessionConfigOption` | ✅ | 动态修改配置 |
|
||||
|
||||
### SessionUpdate 类型
|
||||
|
||||
| 类型 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| `agent_message_chunk` | ✅ | 助手文本消息 |
|
||||
| `agent_thought_chunk` | ✅ | 思考/推理内容 |
|
||||
| `user_message_chunk` | ✅ | 用户消息(历史回放) |
|
||||
| `tool_call` | ✅ | 工具调用开始 |
|
||||
| `tool_call_update` | ✅ | 工具调用结果/状态更新 |
|
||||
| `usage_update` | ✅ | token 用量 + context window |
|
||||
| `plan` | ✅ | TodoWrite → plan entries |
|
||||
| `available_commands_update` | ✅ | 斜杠命令 & skills 列表 |
|
||||
| `current_mode_update` | ✅ | 模式切换通知 |
|
||||
| `config_option_update` | ✅ | 配置更新通知 |
|
||||
@@ -516,25 +516,37 @@ AI 也可通过 `SnipTool` 自动截断过长的对话:
|
||||
|
||||
| Flag | 默认 | 说明 |
|
||||
|------|------|------|
|
||||
| `BUDDY` | ✅ dev/build | 伴侣系统 |
|
||||
| `BRIDGE_MODE` | ✅ dev/build | 远程控制 |
|
||||
| `VOICE_MODE` | ✅ dev/build | 语音模式 |
|
||||
| `CHICAGO_MCP` | ✅ dev/build | Computer Use + Chrome |
|
||||
| `AGENT_TRIGGERS_REMOTE` | ✅ dev/build | 定时任务 |
|
||||
| `SHOT_STATS` | ✅ dev/build | API 统计 |
|
||||
| `TOKEN_BUDGET` | ✅ dev/build | Token 预算 |
|
||||
| `PROMPT_CACHE_BREAK_DETECTION` | ✅ dev/build | 缓存检测 |
|
||||
| `ULTRAPLAN` | ✅ dev/build | 高级规划 |
|
||||
| `DAEMON` | ✅ dev/build | 后台守护 |
|
||||
| `UDS_INBOX` | ✅ dev/build | Pipe IPC |
|
||||
| `LAN_PIPES` | ✅ dev/build | LAN 群控 |
|
||||
| `MONITOR_TOOL` | ✅ dev/build | 后台监控 |
|
||||
| `WORKFLOW_SCRIPTS` | ✅ dev/build | 工作流脚本 |
|
||||
| `FORK_SUBAGENT` | ✅ dev/build | 子 Agent |
|
||||
| `KAIROS` | ✅ dev/build | Kairos 调度 |
|
||||
| `COORDINATOR_MODE` | ✅ dev/build | 多 Worker |
|
||||
| `HISTORY_SNIP` | ✅ dev/build | 历史管理 |
|
||||
| `CONTEXT_COLLAPSE` | ✅ dev/build | 上下文折叠 |
|
||||
| `BUDDY` | ✅ dev only | 伴侣系统 |
|
||||
| `BRIDGE_MODE` | ✅ dev only | 远程控制 |
|
||||
| `VOICE_MODE` | ✅ dev+build | 语音模式 |
|
||||
| `CHICAGO_MCP` | ✅ dev+build | Computer Use + Chrome |
|
||||
| `AGENT_TRIGGERS_REMOTE` | ✅ dev+build | 定时任务 |
|
||||
| `SHOT_STATS` | ✅ dev+build | API 统计 |
|
||||
| `TOKEN_BUDGET` | ✅ dev+build | Token 预算 |
|
||||
| `PROMPT_CACHE_BREAK_DETECTION` | ✅ dev+build | 缓存检测 |
|
||||
| `ULTRAPLAN` | ✅ dev+build | 高级规划 |
|
||||
| `DAEMON` | ✅ dev+build | 后台守护 |
|
||||
| `UDS_INBOX` | ✅ dev only | Pipe IPC |
|
||||
| `LAN_PIPES` | ✅ dev only | LAN 群控 |
|
||||
| `MONITOR_TOOL` | ✅ dev+build | 后台监控 |
|
||||
| `WORKFLOW_SCRIPTS` | ✅ dev+build | 工作流脚本 |
|
||||
| `FORK_SUBAGENT` | ✅ dev+build | 子 Agent |
|
||||
| `KAIROS` | ✅ dev+build | Kairos 调度 |
|
||||
| `COORDINATOR_MODE` | ✅ dev+build | 多 Worker |
|
||||
| `HISTORY_SNIP` | ✅ dev+build | 历史管理 |
|
||||
| `CONTEXT_COLLAPSE` | ✅ dev+build | 上下文折叠 |
|
||||
| `ULTRATHINK` | ✅ dev+build | 扩展思考 |
|
||||
| `EXTRACT_MEMORIES` | ✅ dev+build | 自动记忆提取 |
|
||||
| `VERIFICATION_AGENT` | ✅ dev+build | 验证 Agent |
|
||||
| `KAIROS_BRIEF` | ✅ dev+build | Brief 模式 |
|
||||
| `AWAY_SUMMARY` | ✅ dev+build | 离开摘要 |
|
||||
| `ACP` | ✅ dev+build | ACP 协议 |
|
||||
| `LODESTONE` | ✅ dev+build | 深度链接 |
|
||||
| `BUILTIN_EXPLORE_PLAN_AGENTS` | ✅ dev+build | 内置 Explore/Plan agent |
|
||||
| `AGENT_TRIGGERS` | ✅ dev+build | 本地定时任务 |
|
||||
| `BG_SESSIONS` | ✅ dev only | 后台会话 |
|
||||
| `TEMPLATES` | ✅ dev only | 模板系统 |
|
||||
| `TRANSCRIPT_CLASSIFIER` | ✅ dev only | 对话分类 |
|
||||
|
||||
手动启用任意 flag:
|
||||
```bash
|
||||
|
||||
@@ -102,6 +102,6 @@ FEATURE_BASH_CLASSIFIER=1 FEATURE_TREE_SITTER_BASH=1 bun run dev
|
||||
| `src/utils/permissions/bashClassifier.ts` | — | Bash 分类器(stub,ANT-ONLY) |
|
||||
| `src/utils/permissions/yoloClassifier.ts` | 1496 | YOLO 分类器(完整参考实现) |
|
||||
| `src/utils/classifierApprovals.ts` | — | 分类器审批信号管理 |
|
||||
| `src/components/permissions/BashPermissionRequest.tsx:261-469` | — | 分类器 UI |
|
||||
| `src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx` | — | 分类器 UI |
|
||||
| `src/hooks/toolPermission/handlers/interactiveHandler.ts` | — | 交互式权限处理 |
|
||||
| `src/services/api/withRetry.ts:81` | — | API beta 标头 |
|
||||
| `src/services/api/withRetry.ts` | — | API beta 标头 |
|
||||
|
||||
@@ -30,7 +30,7 @@ BRIDGE_MODE 将本地 CLI 注册为"bridge 环境",可从 claude.ai 或其他
|
||||
|
||||
文件:`src/bridge/bridgeApi.ts`
|
||||
|
||||
Bridge API Client 提供 7 个核心操作:
|
||||
Bridge API Client 提供 9 个核心操作:
|
||||
|
||||
| 操作 | HTTP | 说明 |
|
||||
|------|------|------|
|
||||
@@ -137,7 +137,7 @@ FEATURE_BRIDGE_MODE=1 FEATURE_DAEMON=1 bun run dev
|
||||
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
| `src/bridge/bridgeApi.ts` | 540 | API Client(核心) |
|
||||
| `src/bridge/bridgeApi.ts` | 541 | API Client(核心) |
|
||||
| `src/bridge/sessionRunner.ts` | — | 会话运行器 |
|
||||
| `src/bridge/bridgeConfig.ts` | — | 配置管理 |
|
||||
| `src/bridge/replBridgeTransport.ts` | — | 传输层 |
|
||||
|
||||
@@ -78,10 +78,13 @@ FEATURE_BUDDY=1 bun run dev
|
||||
|
||||
| 文件 | 说明 |
|
||||
|---|---|
|
||||
| `src/commands/buddy/index.ts` | `/buddy` 命令注册 |
|
||||
| `src/commands/buddy/buddy.ts` | `/buddy` 命令处理 |
|
||||
| `src/buddy/companion.ts` | 宠物生成与加载 |
|
||||
| `src/buddy/companionReact.ts` | 宠物反应系统(REPL 每轮查询后触发) |
|
||||
| `src/buddy/types.ts` | 类型定义(物种、稀有度、属性) |
|
||||
| `src/buddy/sprites.ts` | 终端像素画渲染 |
|
||||
| `src/buddy/CompanionSprite.tsx` | React 组件(输入框旁显示) |
|
||||
| `src/buddy/CompanionCard.tsx` | 宠物信息卡片(`/buddy` 无参数时展示) |
|
||||
| `src/buddy/useBuddyNotification.tsx` | 启动提示通知 |
|
||||
| `src/buddy/prompt.ts` | 宠物相关 prompt 模板 |
|
||||
|
||||
89
docs/features/channels.md
Normal file
89
docs/features/channels.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Channels — 外部频道消息接入
|
||||
|
||||
> 启动参数:`--channels` / `--dangerously-load-development-channels`
|
||||
> 状态:已解除 feature flag 和 OAuth 限制,可直接使用
|
||||
|
||||
## 概述
|
||||
|
||||
Channel 是一个 MCP 服务器,它将外部事件推送到你运行中的 Claude Code 会话中,以便 Claude 可以在你不在终端时做出反应。详细使用说明请参考以下文档:
|
||||
|
||||
- **官方文档**:[使用 channels 将事件推送到运行中的会话](https://code.claude.com/docs/zh-CN/channels)
|
||||
- **飞书插件**:[claude-code-feishu-channel](https://github.com/whobot-ai/claude-code-feishu-channel) — 社区首个飞书 Channel 插件,支持双向消息、配对认证、群组聊天、文件附件
|
||||
|
||||
本仓库现在内置了 **微信 WeChat channel**,不需要单独安装外部 marketplace 插件。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 启用频道监听(plugin 格式)
|
||||
ccb --channels plugin:feishu@claude-code-feishu-channel
|
||||
|
||||
# 启用内置微信 channel
|
||||
ccb weixin login
|
||||
ccb --channels plugin:weixin@builtin
|
||||
|
||||
# 启用频道监听(server 格式)
|
||||
ccb --channels server:my-slack-bridge
|
||||
|
||||
# 同时启用多个频道
|
||||
ccb --channels plugin:feishu@claude-code-feishu-channel --channels server:discord-bot
|
||||
|
||||
# 开发模式(跳过 allowlist 检查,用于测试自定义 channel)
|
||||
ccb --dangerously-load-development-channels server:my-custom-channel
|
||||
```
|
||||
|
||||
## 支持的 Channel
|
||||
|
||||
| Channel | 说明 | 来源 |
|
||||
|---------|------|------|
|
||||
| **Telegram** | 官方 Telegram Bot 集成 | `/plugin install telegram@claude-plugins-official` |
|
||||
| **Discord** | 官方 Discord Bot 集成 | `/plugin install discord@claude-plugins-official` |
|
||||
| **iMessage** | macOS 原生消息 | `/plugin install imessage@claude-plugins-official` |
|
||||
| **飞书 (Feishu/Lark)** | 双向消息、群组聊天、文件附件 | `/plugin install feishu@claude-code-feishu-channel` |
|
||||
| **微信 (WeChat)** | 内置 channel,支持扫码登录、双向消息、附件透传 | `ccb weixin login` + `ccb --channels plugin:weixin@builtin` |
|
||||
|
||||
## 微信内置 Channel
|
||||
|
||||
### 登录
|
||||
|
||||
```bash
|
||||
ccb weixin login
|
||||
```
|
||||
|
||||
已登录状态可清除:
|
||||
|
||||
```bash
|
||||
ccb weixin login clear
|
||||
```
|
||||
|
||||
### 会话启用
|
||||
|
||||
```bash
|
||||
ccb --channels plugin:weixin@builtin
|
||||
```
|
||||
|
||||
### 配对授权
|
||||
|
||||
首次收到未授权微信用户消息时,weixin channel 会回一条 6 位 pairing code。运营侧可在终端执行:
|
||||
|
||||
```bash
|
||||
ccb weixin access pair <code>
|
||||
```
|
||||
|
||||
确认后,该微信用户后续消息才会进入 Claude Code 会话。
|
||||
|
||||
## 相关文件
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `src/services/mcp/channelNotification.ts` | 频道 gate 逻辑、消息包装 |
|
||||
| `src/services/mcp/channelAllowlist.ts` | 频道开关(已默认开启) |
|
||||
| `src/services/mcp/useManageMCPConnections.ts` | MCP 连接管理中的频道注册 |
|
||||
| `src/components/LogoV2/ChannelsNotice.tsx` | 启动时频道状态提示 |
|
||||
| `src/main.tsx` | `--channels` 参数解析 |
|
||||
| `src/interactiveHelpers.tsx` | Dev channels 确认对话框 |
|
||||
|
||||
## 参考链接
|
||||
|
||||
- [官方 Channels 文档](https://code.claude.com/docs/zh-CN/channels) — 完整使用说明、安全性、Enterprise 控制
|
||||
- [飞书 Channel 插件](https://github.com/whobot-ai/claude-code-feishu-channel) — 安装配置教程、MCP 工具、Skill 命令参考
|
||||
30
docs/features/chrome-use-mcp.md
Normal file
30
docs/features/chrome-use-mcp.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Chrome Use — 浏览器自动化快速指南
|
||||
|
||||
让 Claude Code 直接控制你的 Chrome 浏览器,用自然语言完成网页操作。
|
||||
|
||||
## 快速开始(3 分钟)
|
||||
|
||||
### 第一步:安装 Chrome 扩展
|
||||
|
||||
1. 下载扩展:https://github.com/hangwin/mcp-chrome/releases
|
||||
2. 解压 zip 文件
|
||||
3. 打开 Chrome 访问 `chrome://extensions/`
|
||||
4. 开启右上角「开发者模式」
|
||||
5. 点击「加载已解压的扩展程序」,选择解压后的文件夹
|
||||
|
||||
### 第二步:启动 Claude Code
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
ccb # 或者 ccb 安装版也行
|
||||
```
|
||||
|
||||
### 第三步:启用 Chrome MCP
|
||||
|
||||
1. 在 REPL 中输入 `/mcp` 打开 MCP 面板
|
||||
2. 找到 `mcp-chrome`,按空格键启用
|
||||
3. 按 Enter 确认
|
||||
|
||||
## 相关文档
|
||||
|
||||
- GitHub 仓库:https://github.com/hangwin/mcp-chrome
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
## 概览
|
||||
|
||||
Computer Use 提供 37 个工具,分为三类:
|
||||
Computer Use 提供 38 个工具,分为三类:
|
||||
|
||||
| 分类 | 平台 | 工具数 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| 通用工具 | 全平台 | 24 | 官方 Computer Use 标准能力 |
|
||||
| Windows 专属工具 | Win32 | 10 | 绑定窗口模式下的增强能力 |
|
||||
| Windows 专属工具 | Win32 | 11 | 绑定窗口模式下的增强能力 |
|
||||
| 教学工具 | 全平台 | 3 | 分步引导模式(需 teachMode 开启) |
|
||||
|
||||
---
|
||||
@@ -82,7 +82,7 @@ Computer Use 提供 37 个工具,分为三类:
|
||||
|
||||
---
|
||||
|
||||
## 二、Windows 专属工具(10 个)
|
||||
## 二、Windows 专属工具(12 个)
|
||||
|
||||
仅 Windows 平台可见。核心能力:**绑定窗口后的独立操作——不抢占用户鼠标键盘**。
|
||||
|
||||
@@ -235,8 +235,19 @@ Computer Use 提供 37 个工具,分为三类:
|
||||
|
||||
| 工具 | 参数 | 说明 |
|
||||
|------|------|------|
|
||||
| `open_terminal` | `agent`, `command?` | 打开新终端窗口并启动 AI agent(claude/codex/gemini/custom)。自动绑定窗口并截图验证 |
|
||||
| `activate_window` | `click_x?`, `click_y?` | 激活绑定窗口:SetForegroundWindow + BringWindowToTop + 点击确保焦点 |
|
||||
| `prompt_respond` | `response_type`, `arrow_direction?`, `arrow_count?`, `text?` | 处理终端 Yes/No/选择提示 |
|
||||
|
||||
**open_terminal agent 类型:**
|
||||
|
||||
| agent | 命令 | 说明 |
|
||||
|-------|------|------|
|
||||
| `claude` | `claude` | 启动 Claude Code |
|
||||
| `codex` | `codex` | 启动 Codex |
|
||||
| `gemini` | `gemini` | 启动 Gemini |
|
||||
| `custom` | 用户指定 | 自定义命令 |
|
||||
|
||||
**response_type 详情:**
|
||||
|
||||
| response_type | 操作 | 场景 |
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
- ✅ `@ant/computer-use-input` 拆为 dispatcher + backends(darwin + win32)
|
||||
- ✅ `@ant/computer-use-swift` 拆为 dispatcher + backends(darwin + win32)
|
||||
- ✅ `CHICAGO_MCP` 编译开关已开
|
||||
- ❌ `src/` 层有 6 处 macOS 硬编码阻塞
|
||||
- ✅ `src/` 层 macOS 硬编码已移除(Phase 2 已完成)
|
||||
|
||||
## 2. 阻塞点全景
|
||||
|
||||
@@ -19,25 +19,25 @@
|
||||
|
||||
| # | 文件:行号 | 阻塞代码 | 影响 |
|
||||
|---|----------|---------|------|
|
||||
| 1 | `src/main.tsx:1605` | `getPlatform() === 'macos'` | 整个 CU 初始化被跳过 |
|
||||
| 1 | `src/main.tsx:2366` | `feature("CHICAGO_MCP")` 门控 | CU 初始化入口 |
|
||||
|
||||
### 2.2 加载层
|
||||
|
||||
| # | 文件:行号 | 阻塞代码 | 影响 |
|
||||
|---|----------|---------|------|
|
||||
| 2 | `src/utils/computerUse/swiftLoader.ts:16` | `process.platform !== 'darwin'` → throw | 截图、应用管理全部不可用 |
|
||||
| 3 | `src/utils/computerUse/executor.ts:263` | `process.platform !== 'darwin'` → throw | 整个 executor 工厂函数不可用 |
|
||||
| 2 | `src/utils/computerUse/swiftLoader.ts` | macOS-only loader(已改为仅 darwin 加载) | 非 darwin 使用 platforms/ 替代 |
|
||||
| 3 | `src/utils/computerUse/executor.ts:302` | `process.platform !== 'darwin'` → cross-platform executor | 非 darwin 走跨平台路径 |
|
||||
|
||||
### 2.3 macOS 特有依赖
|
||||
|
||||
| # | 文件:行号 | 依赖 | macOS 实现 | 需要替代方案 |
|
||||
|---|----------|------|-----------|------------|
|
||||
| 4 | `executor.ts:70-88` | 剪贴板 | `pbcopy`/`pbpaste` | Win: PowerShell `Get/Set-Clipboard`;Linux: `xclip`/`wl-copy` |
|
||||
| 5 | `drainRunLoop.ts:21` | CFRunLoop pump | `cu._drainMainRunLoop()` | 非 darwin:直接执行 fn(),不需要 pump |
|
||||
| 6 | `escHotkey.ts:28` | ESC 热键 | CGEventTap | 非 darwin:返回 false(已有 Ctrl+C fallback) |
|
||||
| 7 | `hostAdapter.ts:48-54` | 系统权限 | TCC accessibility + screenRecording | Win:直接 granted;Linux:检查 xdotool |
|
||||
| 8 | `common.ts:56` | 平台标识 | `platform: 'darwin'` 硬编码 | 动态获取 |
|
||||
| 9 | `executor.ts:180` | 粘贴快捷键 | `command+v` | Win/Linux:`ctrl+v` |
|
||||
| 4 | `executor.ts:72-96` | 剪贴板 | `pbcopy`/`pbpaste` / PowerShell / xclip | Win: PowerShell `Get/Set-Clipboard`;Linux: `xclip`/`wl-copy` |
|
||||
| 5 | `drainRunLoop.ts` | CFRunLoop pump | `cu._drainMainRunLoop()` | 非 darwin:直接执行 fn(),不需要 pump |
|
||||
| 6 | `escHotkey.ts` | ESC 热键 | CGEventTap | 非 darwin:返回 false(已有 Ctrl+C fallback) |
|
||||
| 7 | `hostAdapter.ts` | 系统权限 | TCC accessibility + screenRecording | Win:直接 granted;Linux:检查 xdotool |
|
||||
| 8 | `common.ts:55-58` | 平台标识 | 动态获取 | 已改为 `process.platform` 分发 |
|
||||
| 9 | `executor.ts:232` | 粘贴快捷键 | `command`/`ctrl` 分发 | 已按平台分发粘贴快捷键 |
|
||||
|
||||
### 2.4 缺失的 Linux 后端
|
||||
|
||||
@@ -100,19 +100,19 @@
|
||||
|
||||
| 步骤 | 文件 | 改动 |
|
||||
|------|------|------|
|
||||
| 2.1 | `src/main.tsx:1605` | `getPlatform() === 'macos'` → 去掉平台限制,或改为 `!== 'unknown'` |
|
||||
| 2.2 | `src/utils/computerUse/swiftLoader.ts:16-18` | 移除 `process.platform !== 'darwin'` throw。`@ant/computer-use-swift/index.ts` 已有跨平台 dispatch |
|
||||
| 2.3 | `src/utils/computerUse/executor.ts:263-267` | 移除 `process.platform !== 'darwin'` throw。改为检查 input/swift isSupported |
|
||||
| 2.4 | `src/utils/computerUse/executor.ts:70-88` | 剪贴板函数按平台分发:darwin→pbcopy/pbpaste,win32→PowerShell Get/Set-Clipboard,linux→xclip |
|
||||
| 2.5 | `src/utils/computerUse/executor.ts:180` | `typeViaClipboard` 中 `command+v` → 非 darwin 时用 `ctrl+v` |
|
||||
| 2.6 | `src/utils/computerUse/executor.ts:273` | `const cu = requireComputerUseSwift()` → 改为 `new ComputerUseAPI()`(从 package 直接实例化,不走 swiftLoader throw) |
|
||||
| 2.7 | `src/utils/computerUse/drainRunLoop.ts` | 开头加 `if (process.platform !== 'darwin') return fn()` |
|
||||
| 2.8 | `src/utils/computerUse/escHotkey.ts` | `registerEscHotkey` 非 darwin 返回 false(已有 Ctrl+C fallback) |
|
||||
| 2.9 | `src/utils/computerUse/hostAdapter.ts:48-54` | `ensureOsPermissions` 非 darwin 返回 `{ granted: true }` |
|
||||
| 2.10 | `src/utils/computerUse/common.ts:56` | `platform: 'darwin'` → `platform: process.platform === 'win32' ? 'windows' : process.platform === 'linux' ? 'linux' : 'darwin'` |
|
||||
| 2.11 | `src/utils/computerUse/common.ts:55` | `screenshotFiltering: 'native'` → 非 darwin 时 `'none'`(Windows/Linux 截图不支持 per-app 过滤) |
|
||||
| 2.12 | `src/utils/computerUse/gates.ts:13` | `enabled: false` → `enabled: true`(无 GrowthBook 时默认可用) |
|
||||
| 2.13 | `src/utils/computerUse/gates.ts:39-43` | `hasRequiredSubscription()` → 直接返回 `true` |
|
||||
| 2.1 | `src/main.tsx:2366` | `feature("CHICAGO_MCP")` → 已为跨平台入口 |
|
||||
| 2.2 | `src/utils/computerUse/swiftLoader.ts` | 已改为仅 darwin 加载,非 darwin 使用 platforms/ |
|
||||
| 2.3 | `src/utils/computerUse/executor.ts:302-309` | 已改为 cross-platform dispatch(非 darwin → createCrossPlatformExecutor) |
|
||||
| 2.4 | `src/utils/computerUse/executor.ts:72-96` | 剪贴板已按平台分发:darwin→pbcopy/pbpaste,win32→PowerShell,linux→xclip |
|
||||
| 2.5 | `src/utils/computerUse/executor.ts:232` | 粘贴快捷键已按平台分发:darwin→command,其他→ctrl |
|
||||
| 2.6 | `src/utils/computerUse/executor.ts:302-309` | 非 darwin 已改为 `createCrossPlatformExecutor()` |
|
||||
| 2.7 | `src/utils/computerUse/drainRunLoop.ts` | 非 darwin 无需 pump(直接执行 fn) |
|
||||
| 2.8 | `src/utils/computerUse/escHotkey.ts` | 非 darwin 返回 false(已有 Ctrl+C fallback) |
|
||||
| 2.9 | `src/utils/computerUse/hostAdapter.ts` | 非 darwin 权限检查逻辑已实现 |
|
||||
| 2.10 | `src/utils/computerUse/common.ts:58` | 已改为动态 `process.platform` 分发 |
|
||||
| 2.11 | `src/utils/computerUse/common.ts:55` | 已改为 darwin→'native',其他→'none' |
|
||||
| 2.12 | `src/utils/computerUse/gates.ts:55` | 已更新(需验证 enabled 默认值) |
|
||||
| 2.13 | `src/utils/computerUse/gates.ts:39` | `hasRequiredSubscription()` 已更新 |
|
||||
|
||||
### Phase 3:新增 Linux 后端
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ CONTEXT_COLLAPSE 让模型内省上下文窗口使用情况,并智能压缩旧
|
||||
| 折叠核心 | `src/services/contextCollapse/index.ts` | **Stub** — 接口完整(`ContextCollapseStats`、`CollapseResult`、`DrainResult`),函数全部空操作 |
|
||||
| 折叠操作 | `src/services/contextCollapse/operations.ts` | **Stub** — `projectView` 为恒等函数 |
|
||||
| 折叠持久化 | `src/services/contextCollapse/persist.ts` | **Stub** — `restoreFromEntries` 为空操作 |
|
||||
| CtxInspectTool | `src/tools/CtxInspectTool/` | **缺失** — 目录不存在 |
|
||||
| CtxInspectTool | `packages/builtin-tools/src/tools/CtxInspectTool/CtxInspectTool.ts` | **实现** — 上下文内省工具 |
|
||||
| SnipTool 提示 | `src/tools/SnipTool/prompt.ts` | **Stub** — 空工具名 |
|
||||
| SnipTool 实现 | `src/tools/SnipTool/SnipTool.ts` | **缺失** |
|
||||
| force-snip 命令 | `src/commands/force-snip.js` | **缺失** |
|
||||
@@ -106,7 +106,7 @@ SnipTool 提供手动折叠能力:
|
||||
| 1 | `services/contextCollapse/index.ts` | 大 | 折叠状态机、LLM 调用、消息压缩 |
|
||||
| 2 | `services/contextCollapse/operations.ts` | 中 | `projectView()` 消息过滤 |
|
||||
| 3 | `services/contextCollapse/persist.ts` | 小 | `restoreFromEntries()` 磁盘持久化 |
|
||||
| 4 | `tools/CtxInspectTool/` | 中 | 上下文内省工具(token 计数、已折叠范围) |
|
||||
| 4 | `tools/CtxInspectTool/` | 已完成 | 上下文内省工具已实现(`packages/builtin-tools/src/tools/CtxInspectTool/`) |
|
||||
| 5 | `tools/SnipTool/SnipTool.ts` | 中 | Snip 工具实现 |
|
||||
| 6 | `commands/force-snip.js` | 小 | `/force-snip` 命令 |
|
||||
|
||||
|
||||
318
docs/features/daemon-restructure-design.md
Normal file
318
docs/features/daemon-restructure-design.md
Normal file
@@ -0,0 +1,318 @@
|
||||
# Daemon 重构设计方案
|
||||
|
||||
> 分支: `feat/integrate-5-branches`
|
||||
> 基于: `f41745cb` (= main `11bb3f62` 内容)
|
||||
> 日期: 2026-04-13
|
||||
|
||||
## 一、问题概述
|
||||
|
||||
### 1.1 命令结构散乱
|
||||
|
||||
当前后台进程相关的命令分布在三个不同的位置,没有统一的命名空间:
|
||||
|
||||
| 命令 | 注册位置 | 入口 |
|
||||
|------|---------|------|
|
||||
| `claude daemon start/status/stop` | `cli.tsx` 快速路径 L203 | `daemon/main.ts` |
|
||||
| `claude ps` | `cli.tsx` 快速路径 L220 | `cli/bg.ts` |
|
||||
| `claude logs <x>` | `cli.tsx` 快速路径 L232 | `cli/bg.ts` |
|
||||
| `claude attach <x>` | `cli.tsx` 快速路径 L236 | `cli/bg.ts` |
|
||||
| `claude kill <x>` | `cli.tsx` 快速路径 L238 | `cli/bg.ts` |
|
||||
| `claude --bg` | `cli.tsx` 快速路径 L244 | `cli/bg.ts` |
|
||||
| `claude new/list/reply` | `cli.tsx` 快速路径 L250 | `cli/handlers/templateJobs.ts` |
|
||||
| `claude rollback` | `main.tsx` Commander.js L6525 | `cli/rollback.ts` |
|
||||
| `claude up` | `main.tsx` Commander.js L6511 | `cli/up.ts` |
|
||||
|
||||
**问题**:
|
||||
- `ps/logs/attach/kill` 与 `daemon` 逻辑上都是后台进程管理,但互不关联
|
||||
- 这些命令都**只有 CLI 入口**,REPL 里输入 `/daemon` 或 `/ps` 不存在
|
||||
- `new/list/reply` 是模板任务系统的顶级命令,容易与其他命令冲突(特别是 `list`)
|
||||
|
||||
### 1.2 Windows 不支持
|
||||
|
||||
`--bg` 和 `attach` 硬依赖 tmux:
|
||||
- `bg.ts:handleBgFlag()` 第一步就检查 tmux,不可用直接报错退出
|
||||
- `bg.ts:attachHandler()` 用 `tmux attach-session`,无 tmux 替代方案
|
||||
- Windows (包括 VS Code 终端) 完全无法使用后台会话功能
|
||||
|
||||
### 1.3 无 REPL 入口
|
||||
|
||||
对比 `/mcp` 的双注册模式:
|
||||
- **CLI**: `claude mcp serve/add/remove/list` (Commander.js, `main.tsx:5760`)
|
||||
- **REPL**: `/mcp enable/disable/reconnect` (slash command, `commands/mcp/index.ts`)
|
||||
|
||||
`daemon`/`bg`/`job` 系列只有 CLI 快速路径,REPL 中完全不可用。
|
||||
|
||||
## 二、目标
|
||||
|
||||
1. **层级化命令结构**: 参照 `/mcp` 模式,将后台管理收归 `/daemon`,模板任务收归 `/job`
|
||||
2. **跨平台后台会话**: Windows / macOS / Linux 都能启动、附着、终止后台会话
|
||||
3. **双注册**: CLI (`claude daemon ...`) + REPL (`/daemon ...`) 同时可用
|
||||
4. **向后兼容**: 旧命令保留但输出 deprecation 提示
|
||||
|
||||
## 三、命令结构设计
|
||||
|
||||
### 3.1 `/daemon` — 后台进程管理
|
||||
|
||||
合并 daemon supervisor + bg sessions 为统一命名空间:
|
||||
|
||||
```
|
||||
claude daemon <subcommand> ← CLI 入口 (cli.tsx 快速路径)
|
||||
/daemon <subcommand> ← REPL 入口 (slash command, local-jsx)
|
||||
|
||||
子命令:
|
||||
status 综合状态面板 (daemon + 所有会话)
|
||||
start [--dir <path>] 启动 daemon supervisor
|
||||
stop 停止 daemon
|
||||
bg [args...] 启动后台会话
|
||||
attach [target] 附着到后台会话
|
||||
logs [target] 查看会话日志
|
||||
kill [target] 终止会话
|
||||
(无参数) 等同于 status
|
||||
```
|
||||
|
||||
**CLI 快速路径路由** (`cli.tsx`):
|
||||
```typescript
|
||||
// 新: 统一入口
|
||||
if (feature('DAEMON') && args[0] === 'daemon') {
|
||||
const sub = args[1] || 'status'
|
||||
switch (sub) {
|
||||
case 'start': case 'stop': case 'status':
|
||||
await daemonMain([sub, ...args.slice(2)])
|
||||
break
|
||||
case 'bg':
|
||||
await bg.handleBgStart(args.slice(2))
|
||||
break
|
||||
case 'attach': case 'logs': case 'kill':
|
||||
await bg[`${sub}Handler`](args[2])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 向后兼容 (deprecated)
|
||||
if (feature('BG_SESSIONS') && ['ps','logs','attach','kill'].includes(args[0])) {
|
||||
console.warn(`[deprecated] Use: claude daemon ${args[0] === 'ps' ? 'status' : args[0]}`)
|
||||
// ... delegate to daemon subcommand
|
||||
}
|
||||
```
|
||||
|
||||
**REPL 斜杠命令** (`commands/daemon/index.ts`):
|
||||
```typescript
|
||||
const daemon = {
|
||||
type: 'local-jsx',
|
||||
name: 'daemon',
|
||||
description: 'Manage background sessions and daemon',
|
||||
argumentHint: '[status|start|stop|bg|attach|logs|kill]',
|
||||
isEnabled: () => feature('DAEMON') || feature('BG_SESSIONS'),
|
||||
load: () => import('./daemon.js'),
|
||||
} satisfies Command
|
||||
```
|
||||
|
||||
### 3.2 `/job` — 模板任务管理
|
||||
|
||||
```
|
||||
claude job <subcommand> ← CLI 入口
|
||||
/job <subcommand> ← REPL 入口
|
||||
|
||||
子命令:
|
||||
list 列出模板和活跃任务
|
||||
new <template> [args] 从模板创建任务
|
||||
reply <id> <text> 回复任务
|
||||
status <id> 查看任务状态
|
||||
(无参数) 等同于 list
|
||||
```
|
||||
|
||||
### 3.3 独立命令 (不变)
|
||||
|
||||
```
|
||||
claude up 保持顶级 (简短的 bootstrap 命令)
|
||||
claude rollback [target] 保持顶级 (低频运维命令)
|
||||
```
|
||||
|
||||
## 四、跨平台后台引擎
|
||||
|
||||
### 4.1 引擎抽象
|
||||
|
||||
```typescript
|
||||
// src/cli/bg/engine.ts
|
||||
export interface BgEngine {
|
||||
readonly name: string
|
||||
|
||||
/** 当前平台是否可用 */
|
||||
available(): Promise<boolean>
|
||||
|
||||
/** 启动后台会话 */
|
||||
start(opts: BgStartOptions): Promise<BgStartResult>
|
||||
|
||||
/** 附着到后台会话(blocking) */
|
||||
attach(session: SessionEntry): Promise<void>
|
||||
}
|
||||
|
||||
export interface BgStartOptions {
|
||||
sessionName: string
|
||||
args: string[]
|
||||
env: Record<string, string | undefined>
|
||||
logPath: string
|
||||
cwd: string
|
||||
}
|
||||
|
||||
export interface BgStartResult {
|
||||
pid: number
|
||||
sessionName: string
|
||||
logPath: string
|
||||
engineUsed: string
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 三种引擎实现
|
||||
|
||||
| 引擎 | 平台 | 启动方式 | attach 方式 |
|
||||
|------|------|---------|------------|
|
||||
| TmuxEngine | macOS/Linux (有 tmux) | `tmux new-session -d` | `tmux attach-session` |
|
||||
| DetachedEngine | Windows / 无 tmux 的 macOS/Linux | `spawn({ detached, stdio→logFile })` | `tail -f` 日志文件 |
|
||||
|
||||
#### DetachedEngine 详细设计
|
||||
|
||||
**启动 (`start`)**:
|
||||
```typescript
|
||||
// 1. 打开日志文件 fd
|
||||
const logFd = fs.openSync(logPath, 'a')
|
||||
// 2. detached spawn, stdout/stderr 重定向到日志
|
||||
const child = spawn(process.execPath, execArgs, {
|
||||
detached: true,
|
||||
stdio: ['ignore', logFd, logFd],
|
||||
env,
|
||||
cwd,
|
||||
})
|
||||
child.unref()
|
||||
fs.closeSync(logFd)
|
||||
// 3. 写 sessions/<PID>.json
|
||||
```
|
||||
|
||||
**附着 (`attach`)**:
|
||||
```typescript
|
||||
// 跨平台 tail -f 实现
|
||||
// 1. 读取已有日志内容输出到 stdout
|
||||
// 2. fs.watch(logPath) 监听变化
|
||||
// 3. 每次变化读取新增内容
|
||||
// 4. Ctrl+C 退出 tail(不杀后台进程)
|
||||
```
|
||||
|
||||
#### 引擎选择逻辑
|
||||
|
||||
```typescript
|
||||
// src/cli/bg/engines/index.ts
|
||||
export async function selectEngine(): Promise<BgEngine> {
|
||||
if (process.platform === 'win32') {
|
||||
return new DetachedEngine()
|
||||
}
|
||||
|
||||
const tmux = new TmuxEngine()
|
||||
if (await tmux.available()) {
|
||||
return tmux
|
||||
}
|
||||
|
||||
return new DetachedEngine()
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 SessionEntry 扩展
|
||||
|
||||
```typescript
|
||||
interface SessionEntry {
|
||||
// ... 现有字段
|
||||
engine: 'tmux' | 'detached' // 新增: 记录使用的引擎
|
||||
tmuxSessionName?: string // tmux 引擎才有
|
||||
logPath?: string // 两种引擎都有
|
||||
}
|
||||
```
|
||||
|
||||
`attach` 时根据 `session.engine` 选择对应的 attach 策略。
|
||||
|
||||
## 五、文件变更清单
|
||||
|
||||
### 新增文件 (10 个)
|
||||
|
||||
```
|
||||
src/cli/bg/engine.ts BgEngine 接口定义
|
||||
src/cli/bg/engines/tmux.ts TmuxEngine (从 bg.ts 提取)
|
||||
src/cli/bg/engines/detached.ts DetachedEngine (新实现)
|
||||
src/cli/bg/engines/index.ts 引擎选择 + re-export
|
||||
src/cli/bg/tail.ts 跨平台日志 tail (用于 detached attach)
|
||||
src/commands/daemon/index.ts /daemon REPL 斜杠命令注册
|
||||
src/commands/daemon/daemon.tsx /daemon 子命令路由 + status UI
|
||||
src/commands/job/index.ts /job REPL 斜杠命令注册
|
||||
src/commands/job/job.tsx /job 子命令路由 + UI
|
||||
docs/features/daemon-restructure-design.md 本设计文档
|
||||
```
|
||||
|
||||
### 修改文件 (6 个)
|
||||
|
||||
```
|
||||
src/cli/bg.ts 重构: handler 函数改为调用 BgEngine
|
||||
src/entrypoints/cli.tsx 快速路径: daemon 统一入口 + 向后兼容
|
||||
src/commands.ts 注册 /daemon 和 /job 斜杠命令
|
||||
src/daemon/main.ts daemonMain() 增加 bg/ps/logs 子命令分发
|
||||
src/main.tsx Commander.js: 可选注册 daemon/job 子命令
|
||||
src/cli/handlers/templateJobs.ts 适配 /job 入口 (可能不需改)
|
||||
```
|
||||
|
||||
### 不动的文件
|
||||
|
||||
```
|
||||
src/daemon/state.ts daemon PID 状态管理 (无需改)
|
||||
src/jobs/state.ts job 状态管理 (无需改)
|
||||
src/jobs/templates.ts 模板发现 (无需改)
|
||||
src/jobs/classifier.ts 任务分类器 (无需改)
|
||||
src/cli/rollback.ts 保持顶级命令 (无需改)
|
||||
src/cli/up.ts 保持顶级命令 (无需改)
|
||||
```
|
||||
|
||||
## 六、可行性分析
|
||||
|
||||
### 6.1 风险评估
|
||||
|
||||
| 风险 | 级别 | 缓解措施 |
|
||||
|------|------|---------|
|
||||
| cli.tsx 快速路径修改影响启动性能 | 低 | 仅改路由逻辑,import 仍然 lazy |
|
||||
| DetachedEngine 的 attach 在 Windows 上 fs.watch 不可靠 | 中 | 使用轮询 fallback (setInterval + fs.stat) |
|
||||
| 向后兼容的 deprecation 可能破坏脚本 | 低 | 旧命令保持可用,仅输出 stderr 警告 |
|
||||
| REPL 中 /daemon bg 需要 spawn 子进程 | 中 | 参考 /assistant 的 NewInstallWizard (已有 spawn 先例) |
|
||||
| tsc 类型兼容 | 低 | 接口定义清晰,不引入 any |
|
||||
|
||||
### 6.2 工作量估计
|
||||
|
||||
| Task | 文件数 | 复杂度 |
|
||||
|------|--------|--------|
|
||||
| Task 013: BgEngine 抽象 + 引擎实现 | 5 新增 + 1 修改 | 中 |
|
||||
| Task 014: /daemon 命令层级化 | 3 新增 + 3 修改 | 中 |
|
||||
| Task 015: /job 命令层级化 | 2 新增 + 2 修改 | 低 |
|
||||
| Task 016: 向后兼容 + 测试 | 0 新增 + 2 修改 | 低 |
|
||||
|
||||
### 6.3 依赖关系
|
||||
|
||||
```
|
||||
Task 013 (BgEngine) ← 无依赖,可独立开发
|
||||
Task 014 (/daemon) ← 依赖 Task 013 (引擎选择)
|
||||
Task 015 (/job) ← 无依赖,可与 013 并行
|
||||
Task 016 (兼容) ← 依赖 Task 014 + 015
|
||||
```
|
||||
|
||||
## 七、设计决策记录
|
||||
|
||||
### D1: 为什么 daemon + bg sessions 合为一个命名空间?
|
||||
|
||||
用户视角:都是"后台运行的东西"。分开会导致 `claude daemon status` 看 supervisor + `claude ps` 看会话,割裂感强。合并后 `claude daemon status` 一次性展示 supervisor 状态 + 所有会话列表。
|
||||
|
||||
### D2: 为什么 rollback/up 不收入 daemon?
|
||||
|
||||
它们本质是**版本管理/环境初始化**,不是后台进程管理。`claude up` 是同步阻塞的 setup 脚本,不涉及 daemon 或后台会话。保持顶级更直观。
|
||||
|
||||
### D3: 为什么 DetachedEngine 的 attach 用 tail 而不是 IPC?
|
||||
|
||||
1. 日志文件是最简单的跨平台方案,无需额外依赖
|
||||
2. UDS Pipe IPC 系统 (usePipeIpc) 设计用于实例间通信,不是终端附着
|
||||
3. tmux attach 的体验(完整 PTY)无法在纯 detached 模式下复制,tail 是最诚实的替代
|
||||
|
||||
### D4: 为什么不用 Windows Terminal 的 tab/pane API?
|
||||
|
||||
Windows Terminal 的 `wt.exe` 新窗口/标签功能不够通用——用户可能在 VS Code、ConEmu、cmder 等终端中。detached + log 是唯一跨终端方案。
|
||||
@@ -1,12 +1,12 @@
|
||||
# DAEMON — 后台守护进程
|
||||
|
||||
> Feature Flag: `FEATURE_DAEMON=1`
|
||||
> 实现状态:主进程和 worker 注册为 Stub,CLI 路由完整
|
||||
> 实现状态:Supervisor 和 remoteControl Worker 已实现
|
||||
> 引用数:3
|
||||
|
||||
## 一、功能概述
|
||||
|
||||
DAEMON 将 Claude Code 变为后台守护进程。主进程(supervisor)管理多个 worker 进程的生命周期,通过 Unix 域套接字进行 IPC。适用于持续运行的后台服务场景(如配合 BRIDGE_MODE 提供远程控制服务)。
|
||||
DAEMON 将 Claude Code 变为后台守护进程。主进程(supervisor)管理多个 worker 子进程的生命周期,通过文件系统状态文件进行通信。适用于持续运行的后台服务场景(如配合 BRIDGE_MODE 提供远程控制服务)。
|
||||
|
||||
## 二、实现架构
|
||||
|
||||
@@ -14,8 +14,9 @@ DAEMON 将 Claude Code 变为后台守护进程。主进程(supervisor)管
|
||||
|
||||
| 模块 | 文件 | 状态 |
|
||||
|------|------|------|
|
||||
| 守护主进程 | `src/daemon/main.ts` | **Stub** — `daemonMain: () => Promise.resolve()` |
|
||||
| Worker 注册 | `src/daemon/workerRegistry.ts` | **Stub** — `runDaemonWorker: () => Promise.resolve()` |
|
||||
| 守护主进程 | `src/daemon/main.ts` | **已实现** — Supervisor 含子命令、Worker 生命周期管理、指数退避重启 |
|
||||
| Worker 注册 | `src/daemon/workerRegistry.ts` | **已实现** — remoteControl Worker(headless bridge) |
|
||||
| Daemon 状态 | `src/daemon/state.ts` | **已实现** — PID/状态文件的读写与查询 |
|
||||
| CLI 路由 | `src/entrypoints/cli.tsx` | **布线** — `--daemon-worker` 和 `daemon` 子命令 |
|
||||
| 命令注册 | `src/commands.ts` | **布线** — DAEMON + BRIDGE_MODE 门控 |
|
||||
|
||||
@@ -23,34 +24,49 @@ DAEMON 将 Claude Code 变为后台守护进程。主进程(supervisor)管
|
||||
|
||||
```
|
||||
# 启动守护进程
|
||||
claude daemon
|
||||
claude daemon start
|
||||
|
||||
# 以 worker 身份启动
|
||||
claude --daemon-worker=<kind>
|
||||
# 查看状态(默认子命令)
|
||||
claude daemon status
|
||||
claude daemon ps
|
||||
|
||||
# 停止守护进程
|
||||
claude daemon stop
|
||||
|
||||
# 以 worker 身份启动(由 supervisor 自动调用)
|
||||
claude --daemon-worker=remoteControl
|
||||
|
||||
# 后台会话管理
|
||||
claude daemon bg
|
||||
claude daemon attach <session>
|
||||
claude daemon logs <session>
|
||||
claude daemon kill <session>
|
||||
```
|
||||
|
||||
### 2.3 预期架构
|
||||
### 2.3 架构
|
||||
|
||||
```
|
||||
Supervisor (daemonMain)
|
||||
│
|
||||
├── Worker 1: assistant-mode
|
||||
│ └── 接收和处理 assistant 会话
|
||||
│
|
||||
├── Worker 2: bridge-sync
|
||||
│ └── bridge 消息同步
|
||||
│
|
||||
└── Worker 3: proactive
|
||||
└── 主动任务执行
|
||||
├── Worker: remoteControl
|
||||
│ └── runBridgeHeadless() — 远程控制 headless 模式
|
||||
│ 接收远程会话、处理消息、权限审批
|
||||
│
|
||||
▼
|
||||
IPC via Unix Domain Sockets
|
||||
- 生命周期管理(启动、停止、重启)
|
||||
- 工作分发
|
||||
- 状态报告
|
||||
文件系统状态文件 (daemon-state.json)
|
||||
- PID、CWD、启动时间、Worker 类型
|
||||
- queryDaemonStatus() / stopDaemonByPid()
|
||||
```
|
||||
|
||||
### 2.4 与 BRIDGE_MODE 的关系
|
||||
### 2.4 Worker 生命周期管理
|
||||
|
||||
Supervisor 为每个 worker 实现:
|
||||
- **指数退避重启**:初始 2s,上限 120s,倍数 ×2
|
||||
- **快速失败检测**:10s 内连续崩溃 5 次则 parking(不再重启)
|
||||
- **永久错误退出码**:78 (EXIT_CODE_PERMANENT) 导致直接 parking
|
||||
- **优雅关闭**:SIGTERM/SIGINT → abort signal → 30s 强制 SIGKILL
|
||||
|
||||
### 2.5 与 BRIDGE_MODE 的关系
|
||||
|
||||
DAEMON 和 BRIDGE_MODE 常组合使用:
|
||||
|
||||
@@ -63,40 +79,39 @@ if (feature('DAEMON') && feature('BRIDGE_MODE')) {
|
||||
|
||||
双重门控:两个 feature 都需要开启才能使用远程控制服务器。
|
||||
|
||||
## 三、需要补全的内容
|
||||
|
||||
| 模块 | 工作量 | 说明 |
|
||||
|------|--------|------|
|
||||
| `daemon/main.ts` | 大 | Supervisor 主进程:启动 worker、生命周期管理、IPC |
|
||||
| `daemon/workerRegistry.ts` | 中 | Worker 类型分发(assistant/bridge-sync/proactive) |
|
||||
| Worker 实现 | 大 | 各类型 worker 的具体实现 |
|
||||
| IPC 协议 | 中 | Supervisor-Worker 通信层 |
|
||||
|
||||
## 四、关键设计决策
|
||||
## 三、关键设计决策
|
||||
|
||||
1. **多进程架构**:一个 supervisor + 多个 worker,进程隔离
|
||||
2. **Unix 域套接字 IPC**:本地进程间通信,低延迟
|
||||
2. **文件系统状态通信**:通过 `daemon-state.json` 文件进行状态共享(非 Unix 域套接字)
|
||||
3. **与 BRIDGE_MODE 强绑定**:守护进程最常见的用途是提供远程控制服务
|
||||
4. **CLI 子命令路由**:`daemon` 子命令和 `--daemon-worker` 参数在 `cli.tsx` 中路由
|
||||
5. **Worker 环境变量**:supervisor 通过环境变量(`DAEMON_WORKER_*`)向 worker 传递配置
|
||||
|
||||
## 五、使用方式
|
||||
## 四、使用方式
|
||||
|
||||
```bash
|
||||
# 启用守护进程模式
|
||||
FEATURE_DAEMON=1 FEATURE_BRIDGE_MODE=1 bun run dev
|
||||
|
||||
# 启动守护进程
|
||||
claude daemon
|
||||
claude daemon start
|
||||
|
||||
# 以特定 worker 启动
|
||||
claude --daemon-worker=assistant
|
||||
# 查看状态
|
||||
claude daemon status
|
||||
|
||||
# 停止守护进程
|
||||
claude daemon stop
|
||||
|
||||
# 以特定 worker 启动(通常由 supervisor 自动调用)
|
||||
claude --daemon-worker=remoteControl
|
||||
```
|
||||
|
||||
## 六、文件索引
|
||||
## 五、文件索引
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `src/daemon/main.ts` | Supervisor 主进程(stub) |
|
||||
| `src/daemon/workerRegistry.ts` | Worker 注册(stub) |
|
||||
| `src/entrypoints/cli.tsx:95,149` | CLI 路由 |
|
||||
| `src/commands.ts:77` | 命令注册(双重门控) |
|
||||
| `src/daemon/main.ts` | Supervisor 主进程:子命令分发、Worker 生命周期管理、退避重启 |
|
||||
| `src/daemon/workerRegistry.ts` | Worker 入口:remoteControl worker 实现 |
|
||||
| `src/daemon/state.ts` | Daemon 状态管理:PID 文件读写、状态查询 |
|
||||
| `src/entrypoints/cli.tsx` | CLI 路由 |
|
||||
| `src/commands.ts` | 命令注册(双重门控) |
|
||||
|
||||
@@ -27,13 +27,15 @@ bun run dev:inspect
|
||||
|
||||
## 原理
|
||||
|
||||
`dev:inspect` 脚本实际执行的是:
|
||||
`dev:inspect` 脚本实际执行的是 `scripts/dev-debug.ts`:
|
||||
|
||||
```bash
|
||||
bun --inspect-wait=localhost:8888/<token> run scripts/dev.ts
|
||||
```typescript
|
||||
// scripts/dev-debug.ts
|
||||
process.env.BUN_INSPECT = "localhost:8888/<token>"
|
||||
await import("./dev")
|
||||
```
|
||||
|
||||
Bun 的 `--inspect-wait` 参数启动一个 Chrome DevTools Protocol 兼容的 inspect 服务,等待调试器连接后才开始执行。VS Code 的 `bun` 扩展通过 WebSocket 连接到这个地址实现 attach。
|
||||
通过设置 `BUN_INSPECT` 环境变量启动一个 Chrome DevTools Protocol 兼容的 inspect 服务,然后导入 dev 模式入口。VS Code 的 `bun` 扩展通过 WebSocket 连接到输出的地址实现 attach。
|
||||
|
||||
## JetBrains IDE
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ Claude Code 使用三层门控系统:
|
||||
|------|------|----------|
|
||||
| COMPLETE | 22 | BRIDGE_MODE, COORDINATOR_MODE, CONTEXT_COLLAPSE, VOICE_MODE, TEAMMEM, COMMIT_ATTRIBUTION, ULTRAPLAN, BASH_CLASSIFIER, TRANSCRIPT_CLASSIFIER, EXTRACT_MEMORIES, CACHED_MICROCOMPACT, TOKEN_BUDGET, AGENT_TRIGGERS, REACTIVE_COMPACT, KAIROS_BRIEF, CCR_REMOTE_SETUP, SHOT_STATS, BG_SESSIONS, PROACTIVE, CHICAGO_MCP, VERIFICATION_AGENT, PROMPT_CACHE_BREAK_DETECTION |
|
||||
| PARTIAL | 19 | KAIROS, BUDDY, MONITOR_TOOL, HISTORY_SNIP, WORKFLOW_SCRIPTS, UDS_INBOX, KAIROS_CHANNELS, FORK_SUBAGENT, EXPERIMENTAL_SKILL_SEARCH, WEB_BROWSER_TOOL, MCP_SKILLS, REVIEW_ARTIFACT, KAIROS_GITHUB_WEBHOOKS, CONNECTOR_TEXT, TEMPLATES, LODESTONE, HISTORY_PICKER, MESSAGE_ACTIONS, TERMINAL_PANEL |
|
||||
| STUB | 51 | TORCH, KAIROS_DREAM, KAIROS_PUSH_NOTIFICATION, DAEMON, DIRECT_CONNECT, SSH_REMOTE, STREAMLINED_OUTPUT, ANTI_DISTILLATION_CC, NATIVE_CLIENT_ATTESTATION, ABLATION_BASELINE, AGENT_MEMORY_SNAPSHOT, AGENT_TRIGGERS_REMOTE, ALLOW_TEST_VERSIONS, AUTO_THEME, AWAY_SUMMARY, BREAK_CACHE_COMMAND, BUILDING_CLAUDE_APPS, BUILTIN_EXPLORE_PLAN_AGENTS, BYOC_ENVIRONMENT_RUNNER, CCR_AUTO_CONNECT, CCR_MIRROR, COMPACTION_REMINDERS, COWORKER_TYPE_TELEMETRY, DOWNLOAD_USER_SETTINGS, DUMP_SYSTEM_PROMPT, ENHANCED_TELEMETRY_BETA, FILE_PERSISTENCE, HARD_FAIL, HOOK_PROMPTS, IS_LIBC_GLIBC, IS_LIBC_MUSL, MCP_RICH_OUTPUT, MEMORY_SHAPE_TELEMETRY, NATIVE_CLIPBOARD_IMAGE, NEW_INIT, OVERFLOW_TEST_TOOL, PERFETTO_TRACING, POWERSHELL_AUTO_MODE, QUICK_SEARCH, RUN_SKILL_GENERATOR, SELF_HOSTED_RUNNER, SKILL_IMPROVEMENT, SLOW_OPERATION_LOGGING, TREE_SITTER_BASH, TREE_SITTER_BASH_SHADOW, ULTRATHINK, UNATTENDED_RETRY, UPLOAD_USER_SETTINGS, SKIP_DETECTION_WHEN_AUTOUPDATES_DISABLED |
|
||||
| STUB | 38 | TORCH, KAIROS_DREAM, KAIROS_PUSH_NOTIFICATION, DIRECT_CONNECT, SSH_REMOTE, STREAMLINED_OUTPUT, ANTI_DISTILLATION_CC, NATIVE_CLIENT_ATTESTATION, ABLATION_BASELINE, AGENT_MEMORY_SNAPSHOT, ALLOW_TEST_VERSIONS, AUTO_THEME, BREAK_CACHE_COMMAND, BUILDING_CLAUDE_APPS, BYOC_ENVIRONMENT_RUNNER, CCR_AUTO_CONNECT, CCR_MIRROR, COMPACTION_REMINDERS, COWORKER_TYPE_TELEMETRY, DOWNLOAD_USER_SETTINGS, DUMP_SYSTEM_PROMPT, ENHANCED_TELEMETRY_BETA, FILE_PERSISTENCE, HARD_FAIL, HOOK_PROMPTS, IS_LIBC_GLIBC, IS_LIBC_MUSL, MCP_RICH_OUTPUT, MEMORY_SHAPE_TELEMETRY, NATIVE_CLIPBOARD_IMAGE, NEW_INIT, OVERFLOW_TEST_TOOL, PERFETTO_TRACING, POWERSHELL_AUTO_MODE, QUICK_SEARCH, RUN_SKILL_GENERATOR, SELF_HOSTED_RUNNER, SKILL_IMPROVEMENT, SLOW_OPERATION_LOGGING, TREE_SITTER_BASH, TREE_SITTER_BASH_SHADOW, UNATTENDED_RETRY, UPLOAD_USER_SETTINGS, SKIP_DETECTION_WHEN_AUTOUPDATES_DISABLED |
|
||||
|
||||
---
|
||||
|
||||
@@ -51,14 +51,31 @@ Claude Code 使用三层门控系统:
|
||||
| SHOT_STATS | **ON** | **ON** | compile-only, 已验证 | 纯本地统计 |
|
||||
| PROMPT_CACHE_BREAK_DETECTION | **ON** | **ON** | compile-only, 已验证 | 内部诊断 |
|
||||
| TOKEN_BUDGET | **ON** | **ON** | compile-only, 已验证 | 支持 `+500k` 语法 |
|
||||
| AGENT_TRIGGERS | **ON** | **ON** | compile+GB gate, 已验证 | 本轮新增,定时任务系统 |
|
||||
| EXTRACT_MEMORIES | **ON** | **ON** | compile+GB gate, 已验证 | 本轮新增,自动记忆提取 |
|
||||
| VERIFICATION_AGENT | **ON** | **ON** | compile+GB gate, 已验证 | 本轮新增,对抗性验证代理 |
|
||||
| KAIROS_BRIEF | **ON** | **ON** | compile+GB gate, 已验证 | 本轮新增,Brief 精简模式 |
|
||||
| AWAY_SUMMARY | **ON** | **ON** | compile+GB gate, 已验证 | 本轮新增,离开摘要 |
|
||||
| AGENT_TRIGGERS | **ON** | **ON** | compile+GB gate, 已验证 | 本地定时任务系统 |
|
||||
| ULTRATHINK | **ON** | **ON** | compile-only | 扩展思考模式 |
|
||||
| BUILTIN_EXPLORE_PLAN_AGENTS | **ON** | **ON** | compile-only | 内置 Explore/Plan agent |
|
||||
| LODESTONE | **ON** | **ON** | compile-only | 深度链接 URL 协议 |
|
||||
| EXTRACT_MEMORIES | **ON** | **ON** | compile+GB gate, 已验证 | 自动记忆提取 |
|
||||
| VERIFICATION_AGENT | **ON** | **ON** | compile+GB gate, 已验证 | 对抗性验证代理 |
|
||||
| KAIROS_BRIEF | **ON** | **ON** | compile+GB gate, 已验证 | Brief 精简模式 |
|
||||
| AWAY_SUMMARY | **ON** | **ON** | compile+GB gate, 已验证 | 离开摘要 |
|
||||
| ULTRAPLAN | **ON** | **ON** | compile+remote | 高级规划,需 CCR 基础设施 |
|
||||
| DAEMON | **ON** | **ON** | compile-only | 后台守护进程 |
|
||||
| ACP | **ON** | **ON** | compile-only | ACP 协议支持 |
|
||||
| WORKFLOW_SCRIPTS | **ON** | **ON** | compile-only | 工作流脚本 |
|
||||
| HISTORY_SNIP | **ON** | **ON** | compile-only | 历史管理 |
|
||||
| CONTEXT_COLLAPSE | **ON** | **ON** | compile-only | 上下文折叠(核心 stub) |
|
||||
| MONITOR_TOOL | **ON** | **ON** | compile-only | 后台监控 |
|
||||
| FORK_SUBAGENT | **ON** | **ON** | compile-only | 子 Agent |
|
||||
| KAIROS | **ON** | **ON** | compile-only | Kairos 调度 |
|
||||
| COORDINATOR_MODE | **ON** | **ON** | compile-only | 多 Worker 协调 |
|
||||
| BUDDY | off | **ON** | compile+GrowthBook | 仅 dev 模式 |
|
||||
| TRANSCRIPT_CLASSIFIER | off | **ON** | compile+GrowthBook | 仅 dev 模式 |
|
||||
| BRIDGE_MODE | off | **ON** | compile+remote | 仅 dev 模式,需 claude.ai 订阅 |
|
||||
| UDS_INBOX | off | **ON** | compile-only | 仅 dev 模式 |
|
||||
| LAN_PIPES | off | **ON** | compile-only | 仅 dev 模式 |
|
||||
| BG_SESSIONS | off | **ON** | compile+GB gate | 仅 dev 模式 |
|
||||
| TEMPLATES | off | **ON** | compile-only | 仅 dev 模式 |
|
||||
|
||||
---
|
||||
|
||||
@@ -124,9 +141,9 @@ Claude Code 使用三层门控系统:
|
||||
8. src/hooks/useReplBridge.tsx — REPL 桥接 Hook
|
||||
9. src/main.tsx — 主入口中的桥接模式启动
|
||||
10. src/screens/REPL.tsx — REPL 屏幕中的桥接集成
|
||||
11. src/tools/BriefTool/attachments.ts — Brief 工具附件处理
|
||||
12. src/tools/BriefTool/upload.ts — Brief 工具上传
|
||||
13. src/tools/ConfigTool/supportedSettings.ts — 配置工具中的桥接设置
|
||||
11. packages/builtin-tools/src/tools/BriefTool/attachments.ts — Brief 工具附件处理
|
||||
12. packages/builtin-tools/src/tools/BriefTool/upload.ts — Brief 工具上传
|
||||
13. packages/builtin-tools/src/tools/ConfigTool/supportedSettings.ts — 配置工具中的桥接设置
|
||||
|
||||
**启用所需操作**: 仅需将编译标志 `BRIDGE_MODE` 设为 `true`。所有代码完整,命令入口 `src/commands/bridge/index.ts`(604 行)和 `src/commands/bridge/bridge.tsx`(46,907 行)均存在。
|
||||
|
||||
@@ -185,8 +202,8 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
7. src/screens/REPL.tsx — REPL 屏幕中的协调器集成
|
||||
8. src/screens/ResumeConversation.tsx — 恢复对话时的协调器处理
|
||||
9. src/tools.ts — 工具注册中的协调器工具
|
||||
10. src/tools/AgentTool/AgentTool.tsx — Agent 工具中的协调器模式分支
|
||||
11. src/tools/AgentTool/builtInAgents.ts — 内置代理定义
|
||||
10. packages/builtin-tools/src/tools/AgentTool/AgentTool.tsx — Agent 工具中的协调器模式分支
|
||||
11. packages/builtin-tools/src/tools/AgentTool/builtInAgents.ts — 内置代理定义
|
||||
12. src/utils/processUserInput/processSlashCommand.tsx — 斜杠命令处理中的协调器
|
||||
13. src/utils/sessionRestore.ts — 会话恢复中的协调器状态
|
||||
14. src/utils/systemPrompt.ts — 系统提示中的协调器指令
|
||||
@@ -259,9 +276,9 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
9. src/screens/REPL.tsx — REPL 中的语音模式集成
|
||||
10. src/services/voiceStreamSTT.ts — STT 服务
|
||||
11. src/state/AppState.tsx — 应用状态中的语音状态
|
||||
12. src/tools/ConfigTool/ConfigTool.ts — 配置工具中的语音设置
|
||||
13. src/tools/ConfigTool/prompt.ts — 配置工具提示
|
||||
14. src/tools/ConfigTool/supportedSettings.ts — 支持的设置项
|
||||
12. packages/builtin-tools/src/tools/ConfigTool/ConfigTool.ts — 配置工具中的语音设置
|
||||
13. packages/builtin-tools/src/tools/ConfigTool/prompt.ts — 配置工具提示
|
||||
14. packages/builtin-tools/src/tools/ConfigTool/supportedSettings.ts — 支持的设置项
|
||||
15. src/utils/settings/types.ts — 设置类型定义
|
||||
16. src/voice/voiceModeEnabled.ts — 语音模式启用逻辑
|
||||
|
||||
@@ -385,8 +402,8 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
11. src/hooks/toolPermission/permissionLogging.ts — 权限日志
|
||||
12. src/hooks/useCanUseTool.tsx — 工具可用性检查
|
||||
13. src/services/api/withRetry.ts — API 重试中的分类器
|
||||
14. src/tools/BashTool/bashPermissions.ts — Bash 权限逻辑
|
||||
15. src/tools/BashTool/pathValidation.ts — 路径验证
|
||||
14. packages/builtin-tools/src/tools/BashTool/bashPermissions.ts — Bash 权限逻辑
|
||||
15. packages/builtin-tools/src/tools/BashTool/pathValidation.ts — 路径验证
|
||||
16. src/utils/classifierApprovals.ts — 分类器审批记录
|
||||
17. src/utils/messages.ts — 消息处理
|
||||
18. src/utils/permissions/permissions.ts — 权限核心
|
||||
@@ -431,11 +448,11 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
22. src/screens/REPL.tsx — REPL 屏幕
|
||||
23. src/services/api/claude.ts — Claude API 服务
|
||||
24. src/services/tools/toolExecution.ts — 工具执行
|
||||
25. src/tools/AgentTool/AgentTool.tsx — Agent 工具
|
||||
26. src/tools/AgentTool/agentToolUtils.ts — Agent 工具工具函数
|
||||
27. src/tools/AgentTool/runAgent.ts — 运行 Agent
|
||||
28. src/tools/BashTool/bashPermissions.ts — Bash 权限
|
||||
29. src/tools/ConfigTool/supportedSettings.ts — 支持的设置
|
||||
25. packages/builtin-tools/src/tools/AgentTool/AgentTool.tsx — Agent 工具
|
||||
26. packages/builtin-tools/src/tools/AgentTool/agentToolUtils.ts — Agent 工具工具函数
|
||||
27. packages/builtin-tools/src/tools/AgentTool/runAgent.ts — 运行 Agent
|
||||
28. packages/builtin-tools/src/tools/BashTool/bashPermissions.ts — Bash 权限
|
||||
29. packages/builtin-tools/src/tools/ConfigTool/supportedSettings.ts — 支持的设置
|
||||
30. src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts — 退出计划模式工具
|
||||
31. src/tools/NotebookEditTool/NotebookEditTool.ts — Notebook 编辑工具
|
||||
32. src/types/permissions.ts — 权限类型
|
||||
@@ -543,11 +560,10 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
|
||||
| 文件路径 | 行数 | 功能说明 |
|
||||
|----------|------|----------|
|
||||
| src/tools/ScheduleCronTool/CronCreateTool.ts | 157 行 | Cron 创建工具 |
|
||||
| src/tools/ScheduleCronTool/prompt.ts | 135 行 | Cron 工具提示词 |
|
||||
| src/tools/ScheduleCronTool/CronListTool.ts | 97 行 | Cron 列表工具 |
|
||||
| src/tools/ScheduleCronTool/CronDeleteTool.ts | 95 行 | Cron 删除工具 |
|
||||
| src/tools/ScheduleCronTool/UI.tsx | 59 行 | Cron UI 组件 |
|
||||
| packages/builtin-tools/src/tools/ScheduleCronTool/CronCreateTool.ts | 157 行 | Cron 创建工具 |
|
||||
| packages/builtin-tools/src/tools/ScheduleCronTool/prompt.ts | 135 行 | Cron 工具提示词 |
|
||||
| packages/builtin-tools/src/tools/ScheduleCronTool/CronListTool.ts | 97 行 | Cron 列表工具 |
|
||||
| packages/builtin-tools/src/tools/ScheduleCronTool/CronDeleteTool.ts | 95 行 | Cron 删除工具 |
|
||||
|
||||
**引用该标志的文件(6 个)**:
|
||||
1. src/cli/print.ts — CLI 输出
|
||||
@@ -598,7 +614,7 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
|
||||
| 文件路径 | 行数 | 功能说明 |
|
||||
|----------|------|----------|
|
||||
| src/tools/BriefTool/BriefTool.ts | 204 行 | Brief 工具核心 |
|
||||
| packages/builtin-tools/src/tools/BriefTool/BriefTool.ts | 204 行 | Brief 工具核心 |
|
||||
| src/commands/brief.ts | 130 行 | Brief 命令实现 |
|
||||
|
||||
**引用该标志的文件(20 个)**:
|
||||
@@ -616,7 +632,7 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
12. src/hooks/useGlobalKeybindings.tsx — 全局键绑定
|
||||
13. src/keybindings/defaultBindings.ts — 默认键绑定
|
||||
14. src/main.tsx — 主入口
|
||||
15. src/tools/BriefTool/BriefTool.ts — Brief 工具
|
||||
15. packages/builtin-tools/src/tools/BriefTool/BriefTool.ts — Brief 工具
|
||||
16. src/tools/ToolSearchTool/prompt.ts — 工具搜索提示
|
||||
17. src/utils/attachments.ts — 附件
|
||||
18. src/utils/conversationRecovery.ts — 对话恢复
|
||||
@@ -718,7 +734,7 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
9. src/screens/REPL.tsx — REPL(多处引用,通过 require 加载 proactive 模块)
|
||||
10. src/services/compact/prompt.ts — 压缩提示
|
||||
11. src/tools.ts — 工具注册
|
||||
12. src/tools/AgentTool/AgentTool.tsx — Agent 工具
|
||||
12. packages/builtin-tools/src/tools/AgentTool/AgentTool.tsx — Agent 工具
|
||||
13. src/utils/sessionStorage.ts — 会话存储
|
||||
14. src/utils/settings/types.ts — 设置类型
|
||||
15. src/utils/systemPrompt.ts — 系统提示
|
||||
@@ -771,11 +787,11 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
| 文件路径 | 行数 | 功能说明 |
|
||||
|----------|------|----------|
|
||||
| src/tools/TaskUpdateTool/TaskUpdateTool.ts | 406 行 | 任务更新工具 |
|
||||
| src/tools/AgentTool/builtInAgents.ts | 72 行 | 内置代理定义 |
|
||||
| packages/builtin-tools/src/tools/AgentTool/builtInAgents.ts | 72 行 | 内置代理定义 |
|
||||
|
||||
**引用该标志的文件(4 个)**:
|
||||
1. src/constants/prompts.ts — 提示词
|
||||
2. src/tools/AgentTool/builtInAgents.ts — 内置代理
|
||||
2. packages/builtin-tools/src/tools/AgentTool/builtInAgents.ts — 内置代理
|
||||
3. src/tools/TaskUpdateTool/TaskUpdateTool.ts — 任务更新工具
|
||||
4. src/tools/TodoWriteTool/TodoWriteTool.ts — TodoWrite 工具
|
||||
|
||||
@@ -796,7 +812,7 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
3. src/services/compact/autoCompact.ts — 自动压缩
|
||||
4. src/services/compact/compact.ts — 压缩核心
|
||||
5. src/services/compact/microCompact.ts — 微压缩
|
||||
6. src/tools/AgentTool/runAgent.ts — 运行 Agent
|
||||
6. packages/builtin-tools/src/tools/AgentTool/runAgent.ts — 运行 Agent
|
||||
|
||||
**启用所需操作**: 仅需将编译标志 `PROMPT_CACHE_BREAK_DETECTION` 设为 `true`。
|
||||
|
||||
@@ -856,11 +872,11 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
38. src/services/mcp/useManageMCPConnections.ts
|
||||
39. src/skills/bundled/index.ts
|
||||
40. src/tools.ts
|
||||
41. src/tools/AgentTool/AgentTool.tsx
|
||||
41. packages/builtin-tools/src/tools/AgentTool/AgentTool.tsx
|
||||
42. src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx
|
||||
43. src/tools/BashTool/BashTool.tsx
|
||||
44. src/tools/BriefTool/BriefTool.ts
|
||||
45. src/tools/ConfigTool/supportedSettings.ts
|
||||
43. packages/builtin-tools/src/tools/BashTool/BashTool.tsx
|
||||
44. packages/builtin-tools/src/tools/BriefTool/BriefTool.ts
|
||||
45. packages/builtin-tools/src/tools/ConfigTool/supportedSettings.ts
|
||||
46. src/tools/EnterPlanModeTool/EnterPlanModeTool.ts
|
||||
47. src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts
|
||||
48. src/tools/PowerShellTool/PowerShellTool.tsx
|
||||
@@ -877,8 +893,8 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
59. src/utils/systemPrompt.ts
|
||||
|
||||
**缺失文件**:
|
||||
- src/commands/assistant/index.ts — 完全缺失(src/commands.ts 第 69 行引用了 `commands/assistant/index.js`)
|
||||
- src/commands/assistant/gate.ts — 完全缺失
|
||||
- ~~src/commands/assistant/index.ts~~ — 已补全
|
||||
- ~~src/commands/assistant/gate.ts~~ — 已补全
|
||||
|
||||
**启用所需修复**: 需要创建 `src/commands/assistant/` 目录及其 `index.ts` 和 `gate.ts` 文件。
|
||||
|
||||
@@ -930,7 +946,7 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
| 文件路径 | 行数 | 功能说明 |
|
||||
|----------|------|----------|
|
||||
| src/tasks/LocalShellTask/LocalShellTask.tsx | 522 行 | 本地 Shell 任务完整实现 |
|
||||
| src/tools/MonitorTool/MonitorTool.ts | 1 行 | 监控工具(桩) |
|
||||
| packages/builtin-tools/src/tools/MonitorTool/MonitorTool.ts | 1 行 | 监控工具(桩) |
|
||||
| src/tasks/MonitorMcpTask/MonitorMcpTask.ts | 5 行 | MCP 监控任务(桩) |
|
||||
| src/components/tasks/MonitorMcpDetailDialog.tsx | 3 行 | MCP 详情对话框(桩) |
|
||||
| src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.tsx | 3 行 | 监控权限请求(桩) |
|
||||
@@ -941,12 +957,12 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
3. src/tasks.ts — 任务注册
|
||||
4. src/tasks/LocalShellTask/LocalShellTask.tsx — Shell 任务
|
||||
5. src/tools.ts — 工具注册
|
||||
6. src/tools/AgentTool/runAgent.ts — Agent 运行
|
||||
7. src/tools/BashTool/BashTool.tsx — Bash 工具
|
||||
8. src/tools/BashTool/prompt.ts — Bash 提示
|
||||
6. packages/builtin-tools/src/tools/AgentTool/runAgent.ts — Agent 运行
|
||||
7. packages/builtin-tools/src/tools/BashTool/BashTool.tsx — Bash 工具
|
||||
8. packages/builtin-tools/src/tools/BashTool/prompt.ts — Bash 提示
|
||||
9. src/tools/PowerShellTool/PowerShellTool.tsx — PowerShell 工具
|
||||
|
||||
**启用所需修复**: 需要实现 `src/tools/MonitorTool/MonitorTool.ts`、`src/tasks/MonitorMcpTask/MonitorMcpTask.ts`、`src/components/tasks/MonitorMcpDetailDialog.tsx` 和 `src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.tsx`。
|
||||
**启用所需修复**: 需要实现 `packages/builtin-tools/src/tools/MonitorTool/MonitorTool.ts`、`src/tasks/MonitorMcpTask/MonitorMcpTask.ts`、`src/components/tasks/MonitorMcpDetailDialog.tsx` 和 `src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.tsx`。
|
||||
|
||||
---
|
||||
|
||||
@@ -988,10 +1004,11 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
| src/components/WorkflowMultiselectDialog.tsx | 127 行 | 工作流多选对话框(有内容) |
|
||||
| src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts | 5 行 | 本地工作流任务(桩) |
|
||||
| src/components/tasks/WorkflowDetailDialog.tsx | 3 行 | 工作流详情对话框(桩) |
|
||||
| src/tools/WorkflowTool/WorkflowPermissionRequest.tsx | 3 行 | 工作流权限请求(桩) |
|
||||
| src/tools/WorkflowTool/createWorkflowCommand.ts | 3 行 | 创建工作流命令(桩) |
|
||||
| src/tools/WorkflowTool/WorkflowTool.ts | 1 行 | 工作流工具(桩) |
|
||||
| src/tools/WorkflowTool/constants.ts | 1 行 | 常量(桩) |
|
||||
| packages/builtin-tools/src/tools/WorkflowTool/WorkflowPermissionRequest.tsx | ~80 行 | 工作流权限请求组件 |
|
||||
| packages/builtin-tools/src/tools/WorkflowTool/createWorkflowCommand.ts | 41 行 | 创建工作流命令(已实现) |
|
||||
| packages/builtin-tools/src/tools/WorkflowTool/WorkflowTool.ts | 74 行 | 工作流工具(部分实现,call 需运行时) |
|
||||
| packages/builtin-tools/src/tools/WorkflowTool/constants.ts | ~10 行 | 常量定义 |
|
||||
| packages/builtin-tools/src/tools/WorkflowTool/bundled/index.ts | ~20 行 | 内置工作流初始化 |
|
||||
|
||||
**引用该标志的文件(7 个)**:
|
||||
1. src/commands.ts — 命令注册(引用 `commands/workflows/index.js`)
|
||||
@@ -1086,13 +1103,13 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
|
||||
| 文件路径 | 行数 | 功能说明 |
|
||||
|----------|------|----------|
|
||||
| src/tools/AgentTool/forkSubagent.ts | 210 行 | 分叉子代理核心逻辑 |
|
||||
| packages/builtin-tools/src/tools/AgentTool/forkSubagent.ts | 210 行 | 分叉子代理核心逻辑 |
|
||||
|
||||
**引用该标志的文件(5 个)**:
|
||||
1. src/commands.ts — 命令注册
|
||||
2. src/commands/branch/index.ts — 分支命令入口
|
||||
3. src/components/messages/UserTextMessage.tsx — 用户消息
|
||||
4. src/tools/AgentTool/forkSubagent.ts — 分叉逻辑
|
||||
4. packages/builtin-tools/src/tools/AgentTool/forkSubagent.ts — 分叉逻辑
|
||||
5. src/tools/ToolSearchTool/prompt.ts — 工具搜索提示
|
||||
|
||||
**缺失文件**:
|
||||
@@ -1116,7 +1133,7 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
4. src/query.ts — 查询
|
||||
5. src/services/compact/compact.ts — 压缩
|
||||
6. src/services/mcp/useManageMCPConnections.ts — MCP 连接管理
|
||||
7. src/tools/SkillTool/SkillTool.ts — 技能工具(1,108 行)
|
||||
7. packages/builtin-tools/src/tools/SkillTool/SkillTool.ts — 技能工具(1,108 行)
|
||||
8. src/utils/attachments.ts — 附件
|
||||
9. src/utils/messages.ts — 消息
|
||||
|
||||
@@ -1336,21 +1353,24 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
**引用文件**:
|
||||
1. src/components/Settings/Config.tsx — 设置
|
||||
2. src/tools.ts — 工具注册
|
||||
3. src/tools/ConfigTool/supportedSettings.ts — 支持的设置
|
||||
3. packages/builtin-tools/src/tools/ConfigTool/supportedSettings.ts — 支持的设置
|
||||
**代码量**: 0 行专属代码,仅在设置中预留了开关位
|
||||
|
||||
---
|
||||
|
||||
## 45. DAEMON
|
||||
## 45. DAEMON `[build: ON] [dev: ON]`
|
||||
|
||||
**编译时引用次数**: 3
|
||||
**功能描述**: 守护进程模式。
|
||||
**分类**: STUB
|
||||
**功能描述**: 守护进程模式。允许 Claude Code 作为后台长驻 supervisor 进程运行,管理多个 worker。
|
||||
**分类**: COMPLETE(已恢复)
|
||||
**核心实现文件**:
|
||||
1. src/daemon/main.ts — 413 行,daemon 主入口,管理生命周期
|
||||
2. src/daemon/workerRegistry.ts — 112 行,worker 注册和管理
|
||||
3. src/commands/daemon/index.ts — daemon 子命令入口
|
||||
**引用文件**:
|
||||
1. src/commands.ts — 条件注册命令(与 BRIDGE_MODE 组合)
|
||||
2. src/entrypoints/cli.tsx — CLI 入口
|
||||
**代码量**: 0 行专属代码
|
||||
**说明**: 在 commands.ts 中,`DAEMON` 与 `BRIDGE_MODE` 一起用于条件加载 `commands/remoteControlServer/index.js`,该文件不存在。
|
||||
1. src/commands.ts — 条件注册命令
|
||||
2. src/entrypoints/cli.tsx — CLI 入口中的 `--daemon-worker` 路径
|
||||
**说明**: 已从 stub 恢复为完整实现,支持 `daemon start/status/stop` 子命令、exponential backoff、state file 持久化。
|
||||
|
||||
---
|
||||
|
||||
@@ -1421,7 +1441,7 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
**分类**: STUB
|
||||
**引用文件**:
|
||||
1. src/main.tsx — 主入口
|
||||
2. src/tools/AgentTool/loadAgentsDir.ts — 加载代理目录
|
||||
2. packages/builtin-tools/src/tools/AgentTool/loadAgentsDir.ts — 加载代理目录
|
||||
**代码量**: 0 行专属代码
|
||||
|
||||
---
|
||||
@@ -1456,7 +1476,7 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
**引用文件**:
|
||||
1. src/components/ThemePicker.tsx — 主题选择器
|
||||
2. src/components/design-system/ThemeProvider.tsx — 主题提供者
|
||||
3. src/tools/ConfigTool/supportedSettings.ts — 支持的设置
|
||||
3. packages/builtin-tools/src/tools/ConfigTool/supportedSettings.ts — 支持的设置
|
||||
**代码量**: 0 行专属代码
|
||||
|
||||
---
|
||||
@@ -1498,7 +1518,7 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
**编译时引用次数**: 1
|
||||
**功能描述**: 内置探索和计划代理。
|
||||
**分类**: STUB
|
||||
**引用文件**: src/tools/AgentTool/builtInAgents.ts — 内置代理定义
|
||||
**引用文件**: packages/builtin-tools/src/tools/AgentTool/builtInAgents.ts — 内置代理定义
|
||||
**代码量**: 0 行专属代码
|
||||
|
||||
---
|
||||
@@ -1789,7 +1809,7 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
**功能描述**: Tree-sitter Bash 影子模式(并行运行 tree-sitter 和传统解析器进行对比)。
|
||||
**分类**: STUB
|
||||
**引用文件**:
|
||||
1. src/tools/BashTool/bashPermissions.ts — Bash 权限
|
||||
1. packages/builtin-tools/src/tools/BashTool/bashPermissions.ts — Bash 权限
|
||||
2. src/utils/bash/parser.ts — Bash 解析器
|
||||
**代码量**: 0 行专属代码
|
||||
|
||||
@@ -1863,16 +1883,16 @@ src/utils/swarm/ 目录(22 个文件):
|
||||
|
||||
| 文件路径 | 行数 | 所属标志 |
|
||||
|----------|------|----------|
|
||||
| src/tools/MonitorTool/MonitorTool.ts | 1 行 | MONITOR_TOOL |
|
||||
| src/tools/WorkflowTool/WorkflowTool.ts | 1 行 | WORKFLOW_SCRIPTS |
|
||||
| src/tools/WorkflowTool/constants.ts | 1 行 | WORKFLOW_SCRIPTS |
|
||||
| packages/builtin-tools/src/tools/MonitorTool/MonitorTool.ts | 1 行 | MONITOR_TOOL |
|
||||
| packages/builtin-tools/src/tools/WorkflowTool/WorkflowTool.ts | 1 行 | WORKFLOW_SCRIPTS |
|
||||
| packages/builtin-tools/src/tools/WorkflowTool/constants.ts | 1 行 | WORKFLOW_SCRIPTS |
|
||||
| src/tools/ReviewArtifactTool/ReviewArtifactTool.ts | 1 行 | REVIEW_ARTIFACT |
|
||||
| src/utils/udsMessaging.ts | 1 行 | UDS_INBOX |
|
||||
| src/utils/udsClient.ts | 3 行 | UDS_INBOX |
|
||||
| src/utils/udsMessaging.ts | 已实现 | UDS_INBOX |
|
||||
| src/utils/udsClient.ts | 已实现 | UDS_INBOX |
|
||||
| src/skills/mcpSkills.ts | 3 行 | MCP_SKILLS |
|
||||
| src/tools/WebBrowserTool/WebBrowserPanel.tsx | 3 行 | WEB_BROWSER_TOOL |
|
||||
| src/tools/WorkflowTool/createWorkflowCommand.ts | 3 行 | WORKFLOW_SCRIPTS |
|
||||
| src/tools/WorkflowTool/WorkflowPermissionRequest.tsx | 3 行 | WORKFLOW_SCRIPTS |
|
||||
| packages/builtin-tools/src/tools/WorkflowTool/createWorkflowCommand.ts | 3 行 | WORKFLOW_SCRIPTS |
|
||||
| packages/builtin-tools/src/tools/WorkflowTool/WorkflowPermissionRequest.tsx | 3 行 | WORKFLOW_SCRIPTS |
|
||||
| src/components/tasks/WorkflowDetailDialog.tsx | 3 行 | WORKFLOW_SCRIPTS |
|
||||
| src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.tsx | 3 行 | MONITOR_TOOL |
|
||||
| src/components/tasks/MonitorMcpDetailDialog.tsx | 3 行 | MONITOR_TOOL |
|
||||
|
||||
@@ -37,7 +37,7 @@ Agent({ subagent_type: "general-purpose", prompt: "..." })
|
||||
|
||||
### 3.1 门控与互斥
|
||||
|
||||
文件:`src/tools/AgentTool/forkSubagent.ts:32-39`
|
||||
文件:`packages/builtin-tools/src/tools/AgentTool/forkSubagent.ts:32-39`
|
||||
|
||||
```ts
|
||||
export function isForkSubagentEnabled(): boolean {
|
||||
@@ -105,7 +105,7 @@ isForkSubagentEnabled() && !subagent_type?
|
||||
|
||||
### 3.4 消息构建:buildForkedMessages
|
||||
|
||||
文件:`src/tools/AgentTool/forkSubagent.ts:107-169`
|
||||
文件:`packages/builtin-tools/src/tools/AgentTool/forkSubagent.ts:107-169`
|
||||
|
||||
构建的消息结构:
|
||||
|
||||
@@ -185,11 +185,11 @@ FEATURE_FORK_SUBAGENT=1 bun run dev
|
||||
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
| `src/tools/AgentTool/forkSubagent.ts` | ~210 | 核心定义 + 消息构建 + 递归防护 |
|
||||
| `src/tools/AgentTool/AgentTool.tsx` | — | Fork 路由 + 强制异步 |
|
||||
| `src/tools/AgentTool/prompt.ts` | — | "When to Fork" 提示词段落 |
|
||||
| `src/tools/AgentTool/runAgent.ts` | — | useExactTools 路径 |
|
||||
| `src/tools/AgentTool/resumeAgent.ts` | — | Fork agent 恢复 |
|
||||
| `packages/builtin-tools/src/tools/AgentTool/forkSubagent.ts` | ~210 | 核心定义 + 消息构建 + 递归防护 |
|
||||
| `packages/builtin-tools/src/tools/AgentTool/AgentTool.tsx` | — | Fork 路由 + 强制异步 |
|
||||
| `packages/builtin-tools/src/tools/AgentTool/prompt.ts` | — | "When to Fork" 提示词段落 |
|
||||
| `packages/builtin-tools/src/tools/AgentTool/runAgent.ts` | — | useExactTools 路径 |
|
||||
| `packages/builtin-tools/src/tools/AgentTool/resumeAgent.ts` | — | Fork agent 恢复 |
|
||||
| `src/constants/xml.ts` | — | XML 标签常量 |
|
||||
| `src/utils/forkedAgent.ts` | — | CacheSafeParams + ContentReplacementState 克隆 |
|
||||
| `src/commands/fork/index.ts` | — | /fork 命令(stub) |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# KAIROS — 常驻助手模式
|
||||
|
||||
> Feature Flag: `FEATURE_KAIROS=1`(及子 Feature)
|
||||
> 实现状态:核心框架完整,部分子模块为 stub
|
||||
> 实现状态:核心框架完整,部分子模块为 stub;proactive/sleep 节奏控制已可用
|
||||
> 引用数:154(全库最大)
|
||||
|
||||
## 一、功能概述
|
||||
@@ -34,13 +34,13 @@ KAIROS 在系统提示中注入两大段落:
|
||||
|
||||
### 2.1 Brief 段落 (`getBriefSection`)
|
||||
|
||||
文件:`src/constants/prompts.ts:843-858`
|
||||
文件:`src/constants/prompts.ts:847-858`
|
||||
|
||||
当 `feature('KAIROS') || feature('KAIROS_BRIEF')` 时注入。Brief 工具(`SendUserMessage`)的结构化消息输出指令。`/brief` toggle 和 `--brief` flag 只控制显示过滤,不影响模型行为。
|
||||
|
||||
### 2.2 Proactive/Autonomous Work 段落 (`getProactiveSection`)
|
||||
|
||||
文件:`src/constants/prompts.ts:860-914`
|
||||
文件:`src/constants/prompts.ts:864-918`
|
||||
|
||||
当 `feature('PROACTIVE') || feature('KAIROS')` 且 `isProactiveActive()` 时注入。核心行为指令:
|
||||
|
||||
@@ -74,8 +74,9 @@ KAIROS 在系统提示中注入两大段落:
|
||||
|
||||
SleepTool 是 KAIROS/Proactive 的节奏控制核心。工具描述让模型理解"休眠"概念:
|
||||
- 工具名:`Sleep`
|
||||
- 功能:等待指定时间后响应 tick prompt
|
||||
- 功能:等待指定时间后响应 tick prompt;若队列出现新工作或 proactive 被关闭,会提前唤醒
|
||||
- 与 `<tick_tag>` 配合实现心跳式自主工作
|
||||
- 远程控制 surfaces 可通过 `automation_state` 看到 `standby` / `sleeping` 两种状态
|
||||
|
||||
### 3.3 Bridge 集成
|
||||
|
||||
@@ -172,8 +173,10 @@ FEATURE_KAIROS=1 FEATURE_TOKEN_BUDGET=1 bun run dev
|
||||
| `src/assistant/AssistantSessionChooser.ts` | — | Session 选择 UI(stub) |
|
||||
| `src/tools/BriefTool/` | — | BriefTool 实现(stub) |
|
||||
| `src/tools/SleepTool/prompt.ts` | ~30 | SleepTool 工具提示 |
|
||||
| `src/tools/SleepTool/SleepTool.ts` | ~200 | 休眠/唤醒与 automation metadata |
|
||||
| `src/services/mcp/channelNotification.ts` | 5 | 频道消息接入(stub) |
|
||||
| `src/memdir/memdir.ts` | — | 记忆目录管理(stub) |
|
||||
| `src/constants/prompts.ts:552-554,843-914` | 72 | 系统提示注入 |
|
||||
| `src/constants/prompts.ts:557,847-918` | 72 | 系统提示注入 |
|
||||
| `src/components/tasks/src/tasks/DreamTask/` | 3 | Dream 任务(stub) |
|
||||
| `src/proactive/index.ts` | — | Proactive 核心(stub,KAIROS 共享) |
|
||||
| `src/proactive/index.ts` | — | Proactive 核心(KAIROS 共享) |
|
||||
| `src/utils/sessionState.ts` | — | 向 bridge/CCR 暴露 automation 状态 |
|
||||
|
||||
@@ -281,7 +281,7 @@ CLI-B (192.168.50.27) 心跳循环
|
||||
|
||||
## SendMessageTool TCP 支持
|
||||
|
||||
`src/tools/SendMessageTool/SendMessageTool.ts`
|
||||
`packages/builtin-tools/src/tools/SendMessageTool/SendMessageTool.ts`
|
||||
|
||||
- `to` 字段支持 `tcp:host:port` 格式
|
||||
- `checkPermissions`:`tcp:` scheme 返回 `behavior: 'ask'`,`classifierApprovable: false`
|
||||
|
||||
@@ -202,4 +202,4 @@ docker run -d \
|
||||
| `src/services/langfuse/__tests__/langfuse.test.ts` | 测试(568 行) |
|
||||
| `src/query.ts` | 主查询流程中的 Trace 集成 |
|
||||
| `src/services/tools/toolExecution.ts` | 工具执行中的观察记录 |
|
||||
| `src/tools/AgentTool/runAgent.ts` | 子 Agent Trace 创建 |
|
||||
| `packages/builtin-tools/src/tools/AgentTool/runAgent.ts` | 子 Agent Trace 创建 |
|
||||
|
||||
@@ -41,7 +41,7 @@ getMcpSkillCommands() 过滤 → SkillTool 调用
|
||||
|
||||
### 2.2 技能筛选
|
||||
|
||||
文件:`src/commands.ts:547-558`
|
||||
文件:`src/commands.ts:604-616`
|
||||
|
||||
`getMcpSkillCommands(mcpCommands)` 过滤条件:
|
||||
|
||||
@@ -54,7 +54,7 @@ feature('MCP_SKILLS') // feature flag 必须开启
|
||||
|
||||
### 2.3 条件加载
|
||||
|
||||
文件:`src/services/mcp/client.ts:117-121`
|
||||
文件:`src/services/mcp/client.ts:129-133`
|
||||
|
||||
`fetchMcpSkillsForClient` 通过 `require()` 条件加载,feature flag 关闭时不加载任何模块:
|
||||
|
||||
@@ -79,8 +79,8 @@ const fetchMcpSkillsForClient = feature('MCP_SKILLS')
|
||||
|
||||
| 文件 | 行 | 说明 |
|
||||
|------|------|------|
|
||||
| `src/commands.ts` | 547-558, 561-608 | 命令过滤和 SkillTool 命令收集 |
|
||||
| `src/services/mcp/client.ts` | 117-121, 1394, 1672, 2173-2181, 2346-2358 | 技能获取、缓存清除、连接时获取 |
|
||||
| `src/commands.ts` | 604-616, 620-633 | 命令过滤和 SkillTool 命令收集 |
|
||||
| `src/services/mcp/client.ts` | 129-133, 1394, 1672, 2176 | 技能获取、缓存清除、连接时获取 |
|
||||
| `src/services/mcp/useManageMCPConnections.ts` | 22-26, 682-740 | 实时刷新(prompts/resources 变化) |
|
||||
|
||||
## 三、关键设计决策
|
||||
|
||||
@@ -318,7 +318,7 @@ sub 角色:
|
||||
| `src/commands/pipes/pipes.ts` | /pipes 命令 |
|
||||
| `src/commands/attach/attach.ts` | /attach 命令 |
|
||||
| `src/commands/send/send.ts` | /send 命令 |
|
||||
| `src/tools/SendMessageTool/SendMessageTool.ts` | AI 发消息工具(含 tcp: 支持) |
|
||||
| `packages/builtin-tools/src/tools/SendMessageTool/SendMessageTool.ts` | AI 发消息工具(含 tcp: 支持) |
|
||||
|
||||
## 后续优化方向
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# PROACTIVE — 主动模式
|
||||
|
||||
> Feature Flag: `FEATURE_PROACTIVE=1`(与 `FEATURE_KAIROS=1` 共享功能)
|
||||
> 实现状态:核心模块全部 Stub,布线完整
|
||||
> 实现状态:核心循环与 SleepTool 已落地,部分外围文档仍在补齐
|
||||
> 引用数:37
|
||||
|
||||
## 一、功能概述
|
||||
@@ -21,13 +21,13 @@ PROACTIVE 实现 Tick 驱动的自主代理。CLI 在用户不输入时也能持
|
||||
|
||||
| 模块 | 文件 | 状态 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 核心逻辑 | `src/proactive/index.ts` | **Stub** | `activateProactive()`、`deactivateProactive()`、`isProactiveActive() => false` |
|
||||
| 核心逻辑 | `src/proactive/index.ts` | **已实现** | `activateProactive()`、`deactivateProactive()`、`pause/resume`、`nextTickAt` 调度状态 |
|
||||
| SleepTool 提示 | `src/tools/SleepTool/prompt.ts` | **完整** | 工具提示定义(工具名:`Sleep`) |
|
||||
| 命令注册 | `src/commands.ts:62-65` | **布线** | 动态加载 `./commands/proactive.js` |
|
||||
| 工具注册 | `src/tools.ts:26-28` | **布线** | SleepTool 动态加载 |
|
||||
| REPL 集成 | `src/screens/REPL.tsx` | **布线** | tick 驱动逻辑、占位符、页脚 UI |
|
||||
| 系统提示 | `src/constants/prompts.ts:860-914` | **完整** | 自主工作行为指令(~55 行详细 prompt) |
|
||||
| 会话存储 | `src/utils/sessionStorage.ts:4892-4912` | **布线** | tick 消息注入对话流 |
|
||||
| REPL 集成 | `src/screens/REPL.tsx` | **已实现** | tick 驱动、standby/sleeping 状态、页脚与 bridge automation metadata 上报 |
|
||||
| 系统提示 | `src/constants/prompts.ts:864-918` | **完整** | 自主工作行为指令(~55 行详细 prompt) |
|
||||
| 远控状态镜像 | `src/utils/sessionState.ts` | **已实现** | 向 remote-control/CCR 暴露 `automation_state` 元数据 |
|
||||
|
||||
### 2.2 系统提示内容
|
||||
|
||||
@@ -46,7 +46,7 @@ PROACTIVE 实现 Tick 驱动的自主代理。CLI 在用户不输入时也能持
|
||||
### 2.3 数据流
|
||||
|
||||
```
|
||||
activateProactive() [需要实现]
|
||||
activateProactive()
|
||||
│
|
||||
▼
|
||||
Tick 调度器启动
|
||||
@@ -62,20 +62,22 @@ Tick 调度器启动
|
||||
└── 无事可做 → 必须调用 SleepTool
|
||||
│
|
||||
▼
|
||||
SleepTool 等待 [需要实现]
|
||||
SleepTool 等待
|
||||
│
|
||||
├── 用户插入新工作 / 队列中有命令 → 立即唤醒
|
||||
├── proactive 被关闭 → 立即中断
|
||||
└── 进入休眠时向远端 surfaces 上报 `automation_state = sleeping`
|
||||
│
|
||||
▼
|
||||
下一个 tick 到达
|
||||
```
|
||||
|
||||
## 三、需要补全的内容
|
||||
## 三、当前行为补充
|
||||
|
||||
| 优先级 | 模块 | 工作量 | 说明 |
|
||||
|--------|------|--------|------|
|
||||
| 1 | `src/proactive/index.ts` | 中 | Tick 调度器、activate/deactivate 状态机、pause/resume |
|
||||
| 2 | `src/tools/SleepTool/SleepTool.ts` | 小 | 工具执行(等待指定时间后触发 tick) |
|
||||
| 3 | `src/commands/proactive.js` | 小 | `/proactive` 斜杠命令处理器 |
|
||||
| 4 | `src/hooks/useProactive.ts` | 中 | React hook(REPL 引用但不存在) |
|
||||
- `standby`:proactive 已开启,当前没有执行中的 turn,且已调度下一个 tick。
|
||||
- `sleeping`:模型显式调用 `SleepTool` 进入等待窗口。
|
||||
- remote-control/CCR 通过 `external_metadata.automation_state` 接收这两个状态,用于 Web UI 的 Autopilot 状态显示。
|
||||
- `SleepTool` 现在不是纯定时器;它会在共享命令队列出现新工作时提前醒来。
|
||||
|
||||
## 四、关键设计决策
|
||||
|
||||
@@ -101,9 +103,11 @@ FEATURE_PROACTIVE=1 FEATURE_KAIROS=1 FEATURE_KAIROS_BRIEF=1 bun run dev
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `src/proactive/index.ts` | 核心逻辑(stub) |
|
||||
| `src/proactive/index.ts` | 核心逻辑与 next-tick 状态 |
|
||||
| `src/tools/SleepTool/prompt.ts` | SleepTool 工具提示 |
|
||||
| `src/constants/prompts.ts:860-914` | 自主工作系统提示 |
|
||||
| `src/screens/REPL.tsx` | REPL tick 集成 |
|
||||
| `src/tools/SleepTool/SleepTool.ts` | 休眠/唤醒执行逻辑 |
|
||||
| `src/constants/prompts.ts:864-918` | 自主工作系统提示 |
|
||||
| `src/screens/REPL.tsx` | REPL tick 集成与 automation 状态上报 |
|
||||
| `src/utils/sessionStorage.ts:4892-4912` | Tick 消息注入 |
|
||||
| `src/utils/sessionState.ts` | bridge/CCR metadata 镜像 |
|
||||
| `src/components/PromptInput/PromptInputFooterLeftSide.tsx` | 页脚 UI 状态 |
|
||||
|
||||
@@ -13,17 +13,22 @@
|
||||
┌──────────────────┐ HTTP/SSE │ │ In-Memory │ │
|
||||
│ Web UI 控制面板 │ ◄─────────────── │ │ Store │ │
|
||||
│ (/code/*) │ │ └──────────────┘ │
|
||||
└──────────────────┘ │ ┌──────────────┐ │
|
||||
│ │ JWT Auth │ │
|
||||
│ (React + Vite) │ │ ┌──────────────┐ │
|
||||
└──────────────────┘ │ │ JWT Auth │ │
|
||||
│ └──────────────┘ │
|
||||
└──────────────────────┘
|
||||
┌──────────────────┐ │ ┌──────────────┐ │
|
||||
│ acp-link │ ◄── ACP Relay ─── │ │ ACP Handler │ │
|
||||
│ + ACP Agent │ WebSocket │ └──────────────┘ │
|
||||
└──────────────────┘ └──────────────────────┘
|
||||
```
|
||||
|
||||
**RCS 是一个纯内存的中间服务**,它的职责是:
|
||||
- 接收 Claude Code CLI 的环境注册和工作轮询
|
||||
- 接收 acp-link 的 ACP agent 注册,支持 WebSocket relay 桥接
|
||||
- 提供 Web UI 供操作者远程监控和审批
|
||||
- 通过 WebSocket/SSE 双向传输消息
|
||||
- 管理会话、环境、权限请求
|
||||
- 提供 ACP SSE event stream 供外部消费者订阅 channel group 事件
|
||||
|
||||
## 前置条件
|
||||
|
||||
@@ -99,6 +104,8 @@ docker compose up -d
|
||||
| `RCS_HEARTBEAT_INTERVAL` | 否 | `20` | 心跳间隔(秒) |
|
||||
| `RCS_JWT_EXPIRES_IN` | 否 | `3600` | JWT 令牌有效期(秒) |
|
||||
| `RCS_DISCONNECT_TIMEOUT` | 否 | `300` | 断线判定超时(秒) |
|
||||
| `RCS_WS_IDLE_TIMEOUT` | 否 | `30` | WebSocket 空闲超时(秒),Bun 发送协议级 ping |
|
||||
| `RCS_WS_KEEPALIVE_INTERVAL` | 否 | `20` | 服务端→客户端 keep_alive 帧间隔(秒),防止反向代理关闭空闲连接 |
|
||||
|
||||
### 客户端(Claude Code CLI)
|
||||
|
||||
@@ -138,13 +145,19 @@ bun run dist/cli.js
|
||||
/remote-control
|
||||
```
|
||||
|
||||
CLI 会向 RCS 注册环境,注册成功后在终端显示连接 URL:
|
||||
环境型 Remote Control(例如 `claude remote-control` 子命令)会向 RCS 注册环境,注册成功后在终端显示连接 URL:
|
||||
|
||||
```
|
||||
https://rcs.example.com/code?bridge=<environmentId>
|
||||
```
|
||||
|
||||
同时支持 QR 码扫码打开。该 URL 即 Web UI 控制面板入口,在浏览器中打开即可远程操控当前会话。
|
||||
交互式 REPL 方式(`--remote-control` 或 `/remote-control`)在某些桥接模式下也可能直接给出会话 URL:
|
||||
|
||||
```
|
||||
https://rcs.example.com/code/session_<id>
|
||||
```
|
||||
|
||||
两种 URL 都可以直接在浏览器打开并远程操控当前会话;只有 environment 模式才会出现在 Web UI 的环境列表中。
|
||||
|
||||
若已连接,再次执行 `/remote-control` 会显示对话框,包含以下选项:
|
||||
- **Disconnect this session** — 断开远程连接
|
||||
@@ -163,15 +176,69 @@ claude bridge
|
||||
|
||||
## Web UI 控制面板
|
||||
|
||||
通过 `/remote-control` 命令获取 URL 后,在浏览器打开即可使用。功能:
|
||||
通过 `/remote-control` 命令获取 URL 后,在浏览器打开即可使用。
|
||||
|
||||
- 查看已注册的运行环境
|
||||
### 技术栈(v2,2026-04-18 重构)
|
||||
|
||||
Web UI 已从原生 JS 重构为 **React + Vite + Radix UI**:
|
||||
|
||||
- **框架**: React 19 + Vite 构建,TypeScript
|
||||
- **UI 组件**: Radix UI primitives(Dialog、Tabs、Select、Popover 等)
|
||||
- **聊天组件**: 完整的 ACP 聊天界面,支持 Plan 可视化、工具调用展示、权限审批
|
||||
- **AI Elements**: 独立的 AI 交互组件库(message、reasoning、tool、code-block、prompt-input 等)
|
||||
- **ACP 直连**: 支持 QR 码扫描自动跳转 ACP 直连视图(`ACPDirectView`)
|
||||
- **主题系统**: 暗色/亮色主题切换,遵循 Impeccable 设计系统
|
||||
|
||||
### 功能
|
||||
|
||||
- 查看已注册的运行环境(environment 模式),区分 ACP Agent 和 Claude Code 类型
|
||||
- 创建和管理会话
|
||||
- 实时查看对话消息和工具调用
|
||||
- 查看 Autopilot 状态(`standby` / `sleeping`)和自动运行指示
|
||||
- 查看 authoritative task snapshots 驱动的 Tasks 面板
|
||||
- 审批 Claude Code 的工具权限请求
|
||||
- 权限模式选择器(6 种模式:默认/自动接受编辑/跳过权限/规划/不询问/自动判断)
|
||||
- 模型选择器(可选可用模型)
|
||||
- Plan 可视化(进度条、状态图标、优先级标签)
|
||||
- ACP QR 扫描自动跳转到 ACP 聊天界面
|
||||
|
||||
Web UI 使用 UUID 认证(无需用户账户),适合受信任网络环境。
|
||||
|
||||
## ACP 支持
|
||||
|
||||
RCS 支持 ACP (Agent Client Protocol) agent 通过 `acp-link` 包接入。
|
||||
|
||||
### 架构
|
||||
|
||||
```
|
||||
acp-link ──REST注册──► RCS POST /v1/environments/bridge
|
||||
acp-link ──WS identify──► RCS WebSocket (携带 agentId)
|
||||
acp-link ◄──ACP relay──► RCS ◄──Web UI WS──► 浏览器
|
||||
```
|
||||
|
||||
### 后端组件
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `src/routes/acp/index.ts` | ACP REST 路由:agents 列表、channel groups、relay |
|
||||
| `src/transport/acp-ws-handler.ts` | ACP WebSocket 处理:agent 注册、心跳、消息转发 |
|
||||
| `src/transport/acp-relay-handler.ts` | 前端 WS → acp-link 透传 + EventBus inbound 转发 |
|
||||
| `src/transport/acp-sse-writer.ts` | SSE event stream 供外部消费者订阅 |
|
||||
|
||||
### acp-link 连接
|
||||
|
||||
详见 [acp-link 文档](./acp-link.md)。
|
||||
|
||||
```bash
|
||||
# 在 RCS 环境中启动 acp-link
|
||||
# 注意:claude 本身不支持 ACP,需要用 ccb-bun --acp
|
||||
ACP_RCS_URL=http://localhost:3000 \
|
||||
ACP_RCS_TOKEN=sk-rcs-your-key \
|
||||
acp-link ccb-bun -- --acp
|
||||
```
|
||||
|
||||
ACP session 在 Web UI 中显示品牌色标签,与普通 Claude Code session 区分。
|
||||
|
||||
## 工作流程详解
|
||||
|
||||
```
|
||||
@@ -209,6 +276,7 @@ Web UI 使用 UUID 认证(无需用户账户),适合受信任网络环境
|
||||
9. 双向通信
|
||||
CLI ──消息/工具调用结果──► RCS ──► Browser
|
||||
CLI ◄──权限审批/指令───── RCS ◄──── Browser
|
||||
CLI ──automation_state / task_state──► RCS ──► Browser
|
||||
|
||||
10. 心跳保活(每 20 秒)
|
||||
CLI ──POST /v1/environments/:id/work/:workId/heartbeat──► RCS
|
||||
@@ -218,6 +286,13 @@ Web UI 使用 UUID 认证(无需用户账户),适合受信任网络环境
|
||||
|
||||
## 故障排查
|
||||
|
||||
### Web UI 看不到当前 Autopilot 状态
|
||||
|
||||
- `standby`:proactive 已开启,正在等待下一个 tick
|
||||
- `sleeping`:模型正在 `SleepTool` 等待窗口中
|
||||
|
||||
这两个状态通过 worker `external_metadata.automation_state` 上报。如果页面只显示普通 working spinner,优先检查 CLI 和 RCS 之间的 worker metadata PUT 是否成功。
|
||||
|
||||
### CLI 无法连接
|
||||
|
||||
```
|
||||
@@ -275,4 +350,3 @@ curl https://rcs.example.com/health
|
||||
| 依赖 | claude.ai 订阅 + OAuth | 仅需 API Key |
|
||||
|
||||
自托管模式的核心优势是:设置 `CLAUDE_BRIDGE_BASE_URL` 后,代码自动调用 `isSelfHostedBridge()` 返回 `true`,跳过所有 GrowthBook 和订阅检查,无需 claude.ai 账户即可使用。
|
||||
|
||||
|
||||
310
docs/features/stub-recovery-design-1-4.md
Normal file
310
docs/features/stub-recovery-design-1-4.md
Normal file
@@ -0,0 +1,310 @@
|
||||
# Stub 恢复设计 1-4
|
||||
|
||||
> 日期:2026-04-12
|
||||
> 目标:基于当前代码边界,为下一阶段 4 个 stub/半 stub 命令面给出可实施的设计方案。
|
||||
> 排序原则:按建议实施顺序排序,不按问题严重性排序。
|
||||
|
||||
## 设计原则
|
||||
|
||||
- 先做能独立闭环、收益明确、改动边界清晰的项。
|
||||
- 大项拆成 `MVP` 和 `Phase 2+`,避免一次性掉进大范围恢复。
|
||||
- 优先复用已有状态、传输层、日志与配置能力,不重造协议。
|
||||
- 设计以当前仓库实际代码为准,不以旧文档的理想状态为准。
|
||||
|
||||
## 1. `claude daemon status` / `claude daemon stop`
|
||||
|
||||
### 现状
|
||||
|
||||
- `start` 路径已有完整 supervisor + worker 生命周期:
|
||||
`src/daemon/main.ts`
|
||||
`src/daemon/workerRegistry.ts`
|
||||
- `status` / `stop` 目前只是占位输出:
|
||||
`src/daemon/main.ts`
|
||||
- `/remote-control-server` 有自己的命令内 UI 状态,但只维护当前进程内的 `daemonProcess`,并不适合作为跨进程 CLI 管理基础:
|
||||
`src/commands/remoteControlServer/remoteControlServer.tsx`
|
||||
|
||||
### 目标
|
||||
|
||||
- 让 `claude daemon status` 和 `claude daemon stop` 在另一个 CLI 进程中也能正确工作。
|
||||
- 不依赖 TUI 内存态,不要求当前命令进程就是启动 daemon 的那个进程。
|
||||
|
||||
### MVP 方案
|
||||
|
||||
- 新增 daemon 状态文件,例如:
|
||||
`~/.claude/daemon/remote-control.json`
|
||||
- `start` 时写入:
|
||||
- supervisor pid
|
||||
- cwd
|
||||
- startedAt
|
||||
- worker kinds
|
||||
- 最近状态
|
||||
- `status`:
|
||||
- 读取状态文件
|
||||
- 用现有进程探测能力验证 pid 是否存活
|
||||
- 输出 `running / stopped / stale`
|
||||
- stale 时自动清理状态文件
|
||||
- `stop`:
|
||||
- 读取 pid
|
||||
- 发送 `SIGTERM`
|
||||
- 等待退出
|
||||
- 超时后 `SIGKILL`
|
||||
- 清理状态文件
|
||||
|
||||
### 代码范围
|
||||
|
||||
- 新增 `src/daemon/state.ts`
|
||||
- 修改 `src/daemon/main.ts`
|
||||
- 轻量修改 `src/commands/remoteControlServer/remoteControlServer.tsx`,让 UI 尽量读取同一份状态文件
|
||||
|
||||
### 验证
|
||||
|
||||
1. `claude daemon start`
|
||||
2. 新开终端执行 `claude daemon status`
|
||||
3. 执行 `claude daemon stop`
|
||||
4. 再次执行 `claude daemon status`,确认返回 `stopped` 或清晰的 `stale cleaned`
|
||||
|
||||
### 风险
|
||||
|
||||
- Windows 信号模型和 Unix 不同,`stop` 需要超时兜底。
|
||||
- 当前设计默认单 supervisor,不处理多实例并发。
|
||||
|
||||
### 工作量判断
|
||||
|
||||
- 小
|
||||
- 适合作为下一步的首选实现项
|
||||
|
||||
## 2. `BG_SESSIONS`
|
||||
|
||||
### 现状
|
||||
|
||||
- fast-path 已接好:
|
||||
`src/entrypoints/cli.tsx`
|
||||
- session registry 已有真实实现:
|
||||
`src/utils/concurrentSessions.ts`
|
||||
- `exit` 在 bg session 内已会 `tmux detach-client`:
|
||||
`src/commands/exit/exit.tsx`
|
||||
- 但 CLI handler 仍全空:
|
||||
`src/cli/bg.ts`
|
||||
- task summary 仍然是 stub:
|
||||
`src/utils/taskSummary.ts`
|
||||
|
||||
### 目标
|
||||
|
||||
- 先把 `ps` / `logs` / `kill` 做成真正有用的 session 管理命令。
|
||||
- 不在第一阶段就强行补完 `attach` / `--bg`。
|
||||
|
||||
### Phase 2A:MVP
|
||||
|
||||
- 实现 `ps`
|
||||
- 从 registry 读取 live sessions
|
||||
- 展示 pid、kind、sessionId、cwd、name、startedAt、bridgeSessionId
|
||||
- 如果有 activity/status,则一并展示
|
||||
- 实现 `logs`
|
||||
- 支持按 `sessionId / pid / name` 查找
|
||||
- 优先复用本地 transcript/log 读取能力
|
||||
- 如果 registry 里存在 `logPath`,支持 tail 文件
|
||||
- 实现 `kill`
|
||||
- 解析目标 session
|
||||
- 发退出信号
|
||||
- 清理 stale registry
|
||||
|
||||
### Phase 2B:后续
|
||||
|
||||
- 实现 `attach`
|
||||
- 实现 `--bg`
|
||||
- 实现 `taskSummary` 的中途状态更新
|
||||
|
||||
### 为什么要拆
|
||||
|
||||
- 现有 registry 记录了 `pid / sessionId / name / logPath`
|
||||
- 但没有可靠的 tmux attach target
|
||||
- 所以 `attach` 和 `--bg` 不是简单补 handler,而是需要补启动/附着元数据设计
|
||||
|
||||
### 代码范围
|
||||
|
||||
- 修改 `src/cli/bg.ts`
|
||||
- 修改 `src/utils/concurrentSessions.ts` 以便后续 attach/--bg 扩展
|
||||
- 修改 `src/utils/taskSummary.ts`
|
||||
- 复用:
|
||||
`src/utils/sessionStorage.ts`
|
||||
`src/utils/udsClient.ts`
|
||||
|
||||
### 验证
|
||||
|
||||
1. `ps` 能列出 live sessions
|
||||
2. `logs <sessionId|pid|name>` 能输出对应日志
|
||||
3. `kill <sessionId|pid|name>` 能结束目标 session
|
||||
|
||||
### 风险
|
||||
|
||||
- `attach` / `--bg` 第二阶段需要 tmux 元数据设计
|
||||
- Windows 下 tmux 路径需要明确降级策略
|
||||
|
||||
### 工作量判断
|
||||
|
||||
- `ps/logs/kill` 中等
|
||||
- `attach/--bg` 明显更大,应分阶段
|
||||
|
||||
## 3. `TEMPLATES`
|
||||
|
||||
### 现状
|
||||
|
||||
- 命令入口只有 fast-path:
|
||||
`src/entrypoints/cli.tsx`
|
||||
- handler 是空的:
|
||||
`src/cli/handlers/templateJobs.ts`
|
||||
- `markdownConfigLoader` 已把 `templates` 纳入配置目录:
|
||||
`src/utils/markdownConfigLoader.ts`
|
||||
- `query / stopHooks` 已预留 job classifier 链路:
|
||||
`src/query/stopHooks.ts`
|
||||
- `jobs/classifier.ts` 仍是 stub:
|
||||
`src/jobs/classifier.ts`
|
||||
|
||||
### 目标
|
||||
|
||||
- 把 `new / list / reply` 做成可用的模板任务系统。
|
||||
- 第一阶段不碰复杂的自动分类与自动执行。
|
||||
|
||||
### MVP 方案
|
||||
|
||||
- 模板来源:
|
||||
`.claude/templates/*.md`
|
||||
- 模板格式:
|
||||
复用现有 markdown + frontmatter 解析,不另外设计 DSL
|
||||
- `list`
|
||||
- 列出所有模板
|
||||
- 显示模板名、description、路径
|
||||
- `new <template> [args...]`
|
||||
- 解析模板
|
||||
- 在 `~/.claude/jobs/<job-id>/` 下创建 job 目录
|
||||
- 写入 `template.md`、`input.txt`、`state.json`
|
||||
- 返回 job id 与目录
|
||||
- `reply <job-id> <text>`
|
||||
- 将回复写入 `replies.jsonl` 或 `input.txt`
|
||||
- 更新 `state.json`
|
||||
|
||||
### Phase 2
|
||||
|
||||
- 恢复 `src/jobs/classifier.ts`
|
||||
- 让带 `CLAUDE_JOB_DIR` 的 job session 在 turn 完成后自动更新 `state.json`
|
||||
- 再决定是否补自动 job runner
|
||||
|
||||
### 为什么要拆
|
||||
|
||||
- 当前证据表明这是“template job commands”,不是单纯模板列表
|
||||
- 但自动 job 运行链路没有足够现成实现,先做文件系统 job lifecycle 更稳
|
||||
|
||||
### 代码范围
|
||||
|
||||
- 修改 [src/cli/handlers/templateJobs.ts](</e:/Source_code/Claude-code-bast/src/cli/handlers/templateJobs.ts:1>)
|
||||
- 新增 `src/jobs/state.ts`
|
||||
- 新增 `src/jobs/templates.ts`
|
||||
- Phase 2 再改 [src/jobs/classifier.ts](</e:/Source_code/Claude-code-bast/src/jobs/classifier.ts:1>)
|
||||
|
||||
### 验证
|
||||
|
||||
1. `list` 能列出 `.claude/templates`
|
||||
2. `new` 能创建 job 目录和状态文件
|
||||
3. `reply` 能更新 job 内容和状态
|
||||
4. Phase 2 再验证 classifier 写状态
|
||||
|
||||
### 风险
|
||||
|
||||
- frontmatter schema 需要先定义最小字段集
|
||||
- 一旦扩展到“自动运行 job”,范围会明显膨胀
|
||||
|
||||
### 工作量判断
|
||||
|
||||
- MVP 中等
|
||||
- 完整 job 系统偏大
|
||||
|
||||
## 4. `assistant [sessionId]`
|
||||
|
||||
### 现状
|
||||
|
||||
- attach 主流程其实已经存在:
|
||||
[src/main.tsx](</e:/Source_code/Claude-code-bast/src/main.tsx:4708>)
|
||||
- 远端 viewer 所需基础模块已存在:
|
||||
[src/remote/RemoteSessionManager.ts](</e:/Source_code/Claude-code-bast/src/remote/RemoteSessionManager.ts:1>)
|
||||
[src/hooks/useAssistantHistory.ts](</e:/Source_code/Claude-code-bast/src/hooks/useAssistantHistory.ts:1>)
|
||||
[src/assistant/sessionHistory.ts](</e:/Source_code/Claude-code-bast/src/assistant/sessionHistory.ts:1>)
|
||||
- 真正 stub 的主要是:
|
||||
[src/assistant/sessionDiscovery.ts](</e:/Source_code/Claude-code-bast/src/assistant/sessionDiscovery.ts:1>)
|
||||
[src/assistant/AssistantSessionChooser.ts](</e:/Source_code/Claude-code-bast/src/assistant/AssistantSessionChooser.ts:1>)
|
||||
[src/commands/assistant/assistant.ts](</e:/Source_code/Claude-code-bast/src/commands/assistant/assistant.ts:7>)
|
||||
[src/assistant/index.ts](</e:/Source_code/Claude-code-bast/src/assistant/index.ts:1>)
|
||||
|
||||
### 目标
|
||||
|
||||
- 不一次性恢复整个 KAIROS 助手系统。
|
||||
- 先做“明确 sessionId 的 viewer attach 可用”,再逐步补 discovery / chooser / install。
|
||||
|
||||
### Phase 4A:MVP
|
||||
|
||||
- 只支持 `claude assistant <sessionId>`
|
||||
- 对 `claude assistant` 无参数模式,先返回明确提示:
|
||||
- 当前版本需要显式 `sessionId`
|
||||
- discovery 尚未启用
|
||||
- 这样可以直接复用现有 attach 分支,不必先恢复 chooser/install wizard
|
||||
|
||||
### Phase 4B
|
||||
|
||||
- 恢复 `discoverAssistantSessions()`
|
||||
- 数据来源优先复用现有 sessions / bridge / teleport API,而不是新协议
|
||||
- 让 `claude assistant` 无参数时能拿到候选 session 列表
|
||||
|
||||
### Phase 4C
|
||||
|
||||
- 恢复 `AssistantSessionChooser`
|
||||
- 多 session 时可交互选择
|
||||
|
||||
### Phase 4D
|
||||
|
||||
- 最后考虑 install wizard 辅助函数
|
||||
- 这部分属于“没有 session 时如何引导”,不是 attach 核心路径
|
||||
|
||||
### 为什么要拆
|
||||
|
||||
- attach 渲染层与远端消息通道大部分已经在
|
||||
- 真正缺的是“如何发现目标 session”和“如何交互选择”
|
||||
- 如果把 `src/assistant/index.ts` 的整套 KAIROS 正常模式也一起拉进来,范围会失控
|
||||
|
||||
### 代码范围
|
||||
|
||||
- Phase 4A:
|
||||
- [src/main.tsx](</e:/Source_code/Claude-code-bast/src/main.tsx:4708>)
|
||||
- [src/commands/assistant/index.ts](</e:/Source_code/Claude-code-bast/src/commands/assistant/index.ts:1>)
|
||||
- Phase 4B:
|
||||
- [src/assistant/sessionDiscovery.ts](</e:/Source_code/Claude-code-bast/src/assistant/sessionDiscovery.ts:1>)
|
||||
- Phase 4C:
|
||||
- [src/assistant/AssistantSessionChooser.ts](</e:/Source_code/Claude-code-bast/src/assistant/AssistantSessionChooser.ts:1>)
|
||||
- Phase 4D:
|
||||
- [src/commands/assistant/assistant.ts](</e:/Source_code/Claude-code-bast/src/commands/assistant/assistant.ts:7>)
|
||||
|
||||
### 验证
|
||||
|
||||
1. `claude assistant <sessionId>` 能进入 remote viewer
|
||||
2. 历史懒加载工作正常
|
||||
3. 无参数模式先给出明确提示
|
||||
4. 后续阶段再分别验证 discovery / chooser / install
|
||||
|
||||
### 风险
|
||||
|
||||
- 这是四项里范围最大的
|
||||
- 一旦把 KAIROS 正常模式整体拉入,会从“viewer attach”膨胀成“完整 assistant mode 恢复”
|
||||
|
||||
### 工作量判断
|
||||
|
||||
- Phase 4A 中等
|
||||
- 4A-4D 全做完很大
|
||||
|
||||
## 建议执行顺序
|
||||
|
||||
1. `claude daemon status` / `claude daemon stop`
|
||||
2. `BG_SESSIONS` 先做 `ps/logs/kill`
|
||||
3. `TEMPLATES` 先做 job 文件系统 MVP
|
||||
4. `assistant [sessionId]` 先做显式 sessionId attach,再补 discovery/chooser/install
|
||||
|
||||
## 简短结论
|
||||
|
||||
这四项里,最适合立刻实现的是 `daemon status/stop`。`BG_SESSIONS` 和 `TEMPLATES` 适合按 MVP 先补 handler 与文件系统闭环。`assistant [sessionId]` 不能整块硬上,应该按“attach → discovery → chooser → install”拆开恢复。
|
||||
@@ -7,50 +7,13 @@
|
||||
|
||||
| Feature | 引用 | 状态 | 类别 | 简要说明 |
|
||||
|---------|------|------|------|---------|
|
||||
| CHICAGO_MCP | 16 | N/A | 内部基础设施 | Anthropic 内部 MCP 基础设施,非外部可用 |
|
||||
| MONITOR_TOOL | 13 | Stub | 工具 | 文件/进程监控工具,检测变更并通知 |
|
||||
| BG_SESSIONS | 11 | Stub | 会话管理 | 后台会话管理,支持多会话并行 |
|
||||
| SHOT_STATS | 10 | 无实现 | 统计 | 逐 prompt 统计信息收集 |
|
||||
| EXTRACT_MEMORIES | 7 | 无实现 | 记忆 | 自动从对话中提取重要信息作为记忆 |
|
||||
| TEMPLATES | 6 | Stub | 项目管理 | 项目/提示模板系统 |
|
||||
| LODESTONE | 6 | N/A | 内部基础设施 | 内部基础设施模块 |
|
||||
| STREAMLINED_OUTPUT | 1 | — | 输出 | 精简输出模式,减少终端输出量 |
|
||||
| HOOK_PROMPTS | 1 | — | 钩子 | Hook 提示词,自定义钩子的提示注入 |
|
||||
| CCR_AUTO_CONNECT | 3 | — | 远程控制 | CCR 自动连接,自动建立远程控制会话 |
|
||||
| CCR_MIRROR | 4 | — | 远程控制 | CCR 镜像模式,会话状态同步 |
|
||||
| CCR_REMOTE_SETUP | 1 | — | 远程控制 | CCR 远程设置,初始化远程控制配置 |
|
||||
| NATIVE_CLIPBOARD_IMAGE | 2 | — | 系统集成 | 原生剪贴板图片,从剪贴板读取图片 |
|
||||
| CONNECTOR_TEXT | 7 | — | 连接器 | 连接器文本,外部系统文本适配 |
|
||||
| COMMIT_ATTRIBUTION | 12 | — | Git | Commit 归因,标记 commit 来源 |
|
||||
| CACHED_MICROCOMPACT | 12 | — | 压缩 | 缓存微压缩,优化 compaction 性能 |
|
||||
| PROMPT_CACHE_BREAK_DETECTION | 9 | — | 性能 | Prompt cache 中断检测,监控 cache miss |
|
||||
| MEMORY_SHAPE_TELEMETRY | 3 | — | 遥测 | 记忆形态遥测,记忆使用模式追踪 |
|
||||
| MCP_RICH_OUTPUT | 3 | — | MCP | MCP 富输出,增强 MCP 工具输出格式 |
|
||||
| FILE_PERSISTENCE | 3 | — | 持久化 | 文件持久化,跨会话保持状态 |
|
||||
| TREE_SITTER_BASH_SHADOW | 5 | Shadow | 安全 | Bash AST Shadow 模式(见 tree-sitter-bash.md) |
|
||||
| QUICK_SEARCH | 5 | — | 搜索 | 快速搜索,优化的文件/内容搜索 |
|
||||
| MESSAGE_ACTIONS | 5 | — | UI | 消息操作,对消息执行后处理动作 |
|
||||
| DOWNLOAD_USER_SETTINGS | 5 | — | 配置 | 下载用户设置,从服务端同步配置 |
|
||||
| DIRECT_CONNECT | 5 | — | 网络 | 直连模式,绕过代理直接连接 API |
|
||||
| VERIFICATION_AGENT | 4 | — | Agent | 验证 Agent,专门用于验证代码变更 |
|
||||
| TERMINAL_PANEL | 4 | — | UI | 终端面板,嵌入式终端输出显示 |
|
||||
| SSH_REMOTE | 4 | — | 远程 | SSH 远程,通过 SSH 连接远程 Claude |
|
||||
| REVIEW_ARTIFACT | 4 | — | 审查 | Review Artifact,代码审查产出物 |
|
||||
| REACTIVE_COMPACT | 4 | — | 压缩 | 响应式压缩,基于上下文变化触发 compaction |
|
||||
| HISTORY_PICKER | 4 | — | UI | 历史选择器,浏览和选择历史对话 |
|
||||
| UPLOAD_USER_SETTINGS | 2 | — | 配置 | 上传用户设置,同步配置到服务端 |
|
||||
| POWERSHELL_AUTO_MODE | 2 | — | 平台 | PowerShell 自动模式,Windows 权限自动化 |
|
||||
| OVERFLOW_TEST_TOOL | 2 | — | 测试 | 溢出测试工具,测试上下文溢出处理 |
|
||||
| NEW_INIT | 2 | — | 初始化 | 新版初始化流程 |
|
||||
| HARD_FAIL | 2 | — | 错误处理 | 硬失败模式,不可恢复错误直接终止 |
|
||||
| ENHANCED_TELEMETRY_BETA | 2 | — | 遥测 | 增强遥测 Beta,详细的性能指标收集 |
|
||||
| COWORKER_TYPE_TELEMETRY | 2 | — | 遥测 | 协作者类型遥测,追踪协作模式 |
|
||||
| BREAK_CACHE_COMMAND | 2 | — | 缓存 | 中断缓存命令,强制刷新 prompt cache |
|
||||
| AWAY_SUMMARY | 2 | — | 摘要 | 离开摘要,用户返回时总结期间工作 |
|
||||
| AUTO_THEME | 2 | — | UI | 自动主题,根据终端设置切换主题 |
|
||||
| ALLOW_TEST_VERSIONS | 2 | — | 版本 | 允许测试版本,跳过版本检查 |
|
||||
| AGENT_TRIGGERS_REMOTE | 2 | — | Agent | Agent 远程触发,从远程触发 Agent 任务 |
|
||||
| AGENT_MEMORY_SNAPSHOT | 2 | — | Agent | Agent 记忆快照,保存/恢复 Agent 状态 |
|
||||
| CHICAGO_MCP | 16 | 已实现 | 工具 | Computer Use + Chrome MCP 控制(build 默认启用) |
|
||||
| MONITOR_TOOL | 13 | 已实现 | 工具 | 后台监控工具,持续监视 shell 输出(build 默认启用) |
|
||||
| BG_SESSIONS | 11 | 部分实现 | 会话管理 | 后台会话注册/清理已实现,任务摘要是 stub(dev 默认启用) |
|
||||
| SHOT_STATS | 10 | 已实现 | 统计 | API 调用统计面板(build 默认启用) |
|
||||
| EXTRACT_MEMORIES | 7 | 已实现 | 记忆 | 自动记忆提取(build 默认启用,受 GrowthBook 门控) |
|
||||
| TEMPLATES | 6 | 部分实现 | 项目管理 | 项目/提示模板系统(dev 默认启用) |
|
||||
| LODESTONE | 6 | 已实现 | 深度链接 | URL 协议处理器(build 默认启用) |
|
||||
|
||||
## 单引用 Feature(40+ 个)
|
||||
|
||||
@@ -66,10 +29,9 @@ BUILDING_CLAUDE_APPS, ANTI_DISTILLATION_CC, AGENT_TRIGGERS, ABLATION_BASELINE
|
||||
|
||||
这些 feature 被列为 Tier 3 的原因:
|
||||
|
||||
1. **内部基础设施**(CHICAGO_MCP, LODESTONE):Anthropic 内部使用,外部无法运行
|
||||
2. **纯 Stub 且引用低**(MONITOR_TOOL, BG_SESSIONS):需要大量工作才能实现
|
||||
3. **实验性功能**(SHOT_STATS, EXTRACT_MEMORIES):尚在概念阶段
|
||||
4. **辅助功能**(STREAMLINED_OUTPUT, HOOK_PROMPTS):影响范围小
|
||||
5. **CCR 系列**:依赖远程控制基础设施,需要 BRIDGE_MODE 先完善
|
||||
1. **已实现但影响范围小**(CHICAGO_MCP, LODESTONE, SHOT_STATS, EXTRACT_MEMORIES, MONITOR_TOOL):已在 build/dev 默认启用,主要作为其他功能的基础设施
|
||||
2. **部分实现**(BG_SESSIONS, TEMPLATES):核心注册已实现,但部分功能如任务摘要仍是 stub
|
||||
3. **辅助功能**(STREAMLINED_OUTPUT, HOOK_PROMPTS):影响范围小
|
||||
4. **CCR 系列**:依赖远程控制基础设施,需要 BRIDGE_MODE 先完善
|
||||
|
||||
如需深入了解某个 Tier 3 feature,可以在代码库中搜索 `feature('FEATURE_NAME')` 查看具体使用场景。
|
||||
|
||||
@@ -191,7 +191,7 @@ FEATURE_TOKEN_BUDGET=1 bun run dev
|
||||
| `src/query/tokenBudget.ts` | 93 | 预算追踪器 + continue/stop 决策 |
|
||||
| `src/bootstrap/state.ts:724-743` | 20 | turn 级 token 快照状态 |
|
||||
| `src/constants/prompts.ts:538-551` | 14 | 系统提示注入 |
|
||||
| `src/utils/attachments.ts:3829-3845` | 17 | API attachment 附加 |
|
||||
| `src/utils/attachments.ts:3830-3844` | 17 | API attachment 附加 |
|
||||
| `src/query.ts:280,1311-1358` | 48 | 主循环集成 |
|
||||
| `src/screens/REPL.tsx:2897,2963,2138` | 20 | REPL 提交/完成/取消处理 |
|
||||
| `src/components/Spinner.tsx:319-338` | 20 | 进度条 UI |
|
||||
|
||||
@@ -158,4 +158,4 @@ FEATURE_TREE_SITTER_BASH_SHADOW=1 bun run dev
|
||||
| `src/utils/bash/bashParser.ts` | 4437 | 纯 TS bash 解析器 |
|
||||
| `src/utils/bash/ast.ts` | 2680 | 安全分析器(核心) |
|
||||
| `src/utils/bash/treeSitterAnalysis.ts` | 507 | AST 分析辅助 |
|
||||
| `src/tools/BashTool/bashPermissions.ts:1670-1810` | ~140 | 权限集成 + Shadow 遥测 |
|
||||
| `packages/builtin-tools/src/tools/BashTool/bashPermissions.ts` | ~140 | 权限集成 + Shadow 遥测 |
|
||||
|
||||
@@ -22,16 +22,16 @@ ULTRAPLAN 在用户输入中检测 "ultraplan" 关键字时,自动进入增强
|
||||
|
||||
| 模块 | 文件 | 行数 | 状态 |
|
||||
|------|------|------|------|
|
||||
| 命令处理器 | `src/commands/ultraplan.tsx` | 472 | **完整** |
|
||||
| CCR 会话 | `src/utils/ultraplan/ccrSession.ts` | 350 | **完整** |
|
||||
| 关键字检测 | `src/utils/ultraplan/keyword.ts` | 128 | **完整** |
|
||||
| 命令处理器 | `src/commands/ultraplan.tsx` | 525 | **完整** |
|
||||
| CCR 会话 | `src/utils/ultraplan/ccrSession.ts` | 349 | **完整** |
|
||||
| 关键字检测 | `src/utils/ultraplan/keyword.ts` | 127 | **完整** |
|
||||
| 嵌入式提示 | `src/utils/ultraplan/prompt.txt` | 1 | **完整** |
|
||||
| REPL 对话框 | `src/screens/REPL.tsx` | — | **布线** |
|
||||
| 关键字高亮 | `src/components/PromptInput/PromptInput.tsx` | — | **布线** |
|
||||
|
||||
### 2.2 关键字检测
|
||||
|
||||
文件:`src/utils/ultraplan/keyword.ts`(128 行)
|
||||
文件:`src/utils/ultraplan/keyword.ts`(127 行)
|
||||
|
||||
`findUltraplanTriggerPositions(text)` 智能过滤:
|
||||
- 排除引号内的 "ultraplan"
|
||||
@@ -41,7 +41,7 @@ ULTRAPLAN 在用户输入中检测 "ultraplan" 关键字时,自动进入增强
|
||||
|
||||
### 2.3 CCR 远程会话
|
||||
|
||||
文件:`src/utils/ultraplan/ccrSession.ts`(350 行)
|
||||
文件:`src/utils/ultraplan/ccrSession.ts`(349 行)
|
||||
|
||||
`ExitPlanModeScanner` 类实现完整的事件状态机:
|
||||
- `pollForApprovedExitPlanMode()` — 3 秒轮询间隔
|
||||
@@ -99,9 +99,9 @@ FEATURE_ULTRAPLAN=1 bun run dev
|
||||
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
| `src/commands/ultraplan.tsx` | 472 | 斜杠命令处理器 |
|
||||
| `src/utils/ultraplan/ccrSession.ts` | 350 | CCR 远程会话管理 |
|
||||
| `src/utils/ultraplan/keyword.ts` | 128 | 关键字检测和替换 |
|
||||
| `src/commands/ultraplan.tsx` | 525 | 斜杠命令处理器 |
|
||||
| `src/utils/ultraplan/ccrSession.ts` | 349 | CCR 远程会话管理 |
|
||||
| `src/utils/ultraplan/keyword.ts` | 127 | 关键字检测和替换 |
|
||||
| `src/utils/ultraplan/prompt.txt` | 1 | 嵌入式提示 |
|
||||
| `src/utils/processUserInput/processUserInput.ts:468` | — | 关键字重定向 |
|
||||
| `src/components/PromptInput/PromptInput.tsx` | — | 彩虹高亮 |
|
||||
|
||||
@@ -120,6 +120,6 @@ FEATURE_VOICE_MODE=1 bun run dev
|
||||
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
| `src/voice/voiceModeEnabled.ts` | 55 | 三层门控逻辑 |
|
||||
| `src/voice/voiceModeEnabled.ts` | 54 | 三层门控逻辑 |
|
||||
| `src/hooks/useVoice.ts` | — | React hook(录音状态 + WebSocket) |
|
||||
| `src/services/voiceStreamSTT.ts` | — | STT WebSocket 流式传输 |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# WEB_BROWSER_TOOL — 浏览器工具
|
||||
|
||||
> Feature Flag: `FEATURE_WEB_BROWSER_TOOL=1`
|
||||
> 实现状态:核心实现缺失,面板为 Stub,布线完整
|
||||
> 实现状态:核心工具已实现,面板为 Stub,布线完整
|
||||
> 引用数:4
|
||||
|
||||
## 一、功能概述
|
||||
@@ -14,8 +14,8 @@ WEB_BROWSER_TOOL 让模型可以启动浏览器实例、导航网页、与页面
|
||||
|
||||
| 模块 | 文件 | 状态 |
|
||||
|------|------|------|
|
||||
| 浏览器面板 | `src/tools/WebBrowserTool/WebBrowserPanel.ts` | **Stub** — 返回 null |
|
||||
| 浏览器工具 | `src/tools/WebBrowserTool/WebBrowserTool.ts` | **缺失** |
|
||||
| 浏览器面板 | `packages/builtin-tools/src/tools/WebBrowserTool/WebBrowserPanel.ts` | **Stub** — 返回 null |
|
||||
| 浏览器工具 | `packages/builtin-tools/src/tools/WebBrowserTool/WebBrowserTool.ts` | **已实现** |
|
||||
| REPL 集成 | `src/screens/REPL.tsx` | **布线** — 渲染 WebBrowserPanel |
|
||||
| 工具注册 | `src/tools.ts` | **布线** — 动态加载 |
|
||||
| WebView 检测 | `src/main.tsx` | **布线** — `'WebView' in Bun` 检测 |
|
||||
@@ -44,8 +44,8 @@ WebBrowserPanel 在 REPL 侧边显示浏览器状态
|
||||
|
||||
| 模块 | 工作量 | 说明 |
|
||||
|------|--------|------|
|
||||
| `WebBrowserTool.ts` | 大 | 工具 schema + Bun WebView API 执行 |
|
||||
| `WebBrowserPanel.tsx` | 中 | REPL 侧边栏浏览器状态面板 |
|
||||
| `WebBrowserTool.ts` | ✅ 已实现 | 工具 schema + Bun WebView API 执行 |
|
||||
| `WebBrowserPanel.tsx` | 中 | REPL 侧边栏浏览器状态面板(仍为 Stub) |
|
||||
|
||||
## 四、关键设计决策
|
||||
|
||||
@@ -63,7 +63,7 @@ FEATURE_WEB_BROWSER_TOOL=1 bun run dev
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `src/tools/WebBrowserTool/WebBrowserPanel.ts` | 面板组件(stub) |
|
||||
| `src/tools/WebBrowserTool/WebBrowserTool.ts` | 工具实现(缺失) |
|
||||
| `src/screens/REPL.tsx:273,4582` | 面板渲染 |
|
||||
| `packages/builtin-tools/src/tools/WebBrowserTool/WebBrowserPanel.ts` | 面板组件(stub) |
|
||||
| `packages/builtin-tools/src/tools/WebBrowserTool/WebBrowserTool.ts` | 工具实现(已实现) |
|
||||
| `src/screens/REPL.tsx:471,5676` | 面板渲染 |
|
||||
| `src/tools.ts:115-116` | 工具注册 |
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# WEB_SEARCH_TOOL — 网页搜索工具
|
||||
|
||||
> 实现状态:适配器架构完成,Bing 适配器为当前默认后端
|
||||
> 实现状态:适配器架构完成,支持 API / Bing / Brave 三种后端
|
||||
> 引用数:核心工具,无 feature flag 门控(始终启用)
|
||||
|
||||
## 一、功能概述
|
||||
|
||||
WebSearchTool 让模型可以搜索互联网获取最新信息。原始实现仅支持 Anthropic API 服务端搜索(`web_search_20250305` server tool),在第三方代理端点下不可用。现已重构为适配器架构,新增 Bing 搜索页面解析作为 fallback,确保任何 API 端点都能使用搜索功能。
|
||||
WebSearchTool 让模型可以搜索互联网获取最新信息。原始实现仅支持 Anthropic API 服务端搜索(`web_search_20250305` server tool),在第三方代理端点下不可用。现已重构为适配器架构,支持 API 服务端搜索,以及 Bing / Brave 两个 HTML 解析后端,确保任何 API 端点都能使用搜索功能。
|
||||
|
||||
## 二、实现架构
|
||||
|
||||
@@ -21,24 +21,29 @@ WebSearchTool.call()
|
||||
│ └── 使用 web_search_20250305 server tool
|
||||
│ 通过 queryModelWithStreaming 二次调用 API
|
||||
│
|
||||
└── BingSearchAdapter — Bing HTML 抓取 + 正则提取(当前默认)
|
||||
└── 直接抓取 Bing 搜索页 HTML
|
||||
正则提取 b_algo 块中的标题/URL/摘要
|
||||
├── BingSearchAdapter — Bing HTML 抓取 + 正则提取
|
||||
│ └── 直接抓取 Bing 搜索页 HTML
|
||||
│ 正则提取 b_algo 块中的标题/URL/摘要
|
||||
│
|
||||
└── BraveSearchAdapter — Brave LLM Context API
|
||||
└── 调用 Brave HTTPS GET 接口
|
||||
将 grounding payload 映射为标题/URL/摘要
|
||||
```
|
||||
|
||||
### 2.2 模块结构
|
||||
|
||||
| 模块 | 文件 | 说明 |
|
||||
|------|------|------|
|
||||
| 工具入口 | `src/tools/WebSearchTool/WebSearchTool.ts` | `buildTool()` 定义:schema、权限、执行、输出格式化 |
|
||||
| 工具 prompt | `src/tools/WebSearchTool/prompt.ts` | 搜索工具的系统提示词 |
|
||||
| UI 渲染 | `src/tools/WebSearchTool/UI.tsx` | 搜索结果的终端渲染组件 |
|
||||
| 适配器接口 | `src/tools/WebSearchTool/adapters/types.ts` | `WebSearchAdapter` 接口、`SearchResult`/`SearchOptions`/`SearchProgress` 类型 |
|
||||
| 适配器工厂 | `src/tools/WebSearchTool/adapters/index.ts` | `createAdapter()` 工厂函数,选择后端 |
|
||||
| API 适配器 | `src/tools/WebSearchTool/adapters/apiAdapter.ts` | 封装原有 `queryModelWithStreaming` 逻辑,使用 server tool |
|
||||
| Bing 适配器 | `src/tools/WebSearchTool/adapters/bingAdapter.ts` | Bing HTML 抓取 + 正则解析 |
|
||||
| 单元测试 | `src/tools/WebSearchTool/__tests__/bingAdapter.test.ts` | 32 个测试用例 |
|
||||
| 集成测试 | `src/tools/WebSearchTool/__tests__/bingAdapter.integration.ts` | 真实网络请求验证 |
|
||||
| 工具入口 | `packages/builtin-tools/src/tools/WebSearchTool/WebSearchTool.ts` | `buildTool()` 定义:schema、权限、执行、输出格式化 |
|
||||
| 工具 prompt | `packages/builtin-tools/src/tools/WebSearchTool/prompt.ts` | 搜索工具的系统提示词 |
|
||||
| UI 渲染 | `packages/builtin-tools/src/tools/WebSearchTool/UI.tsx` | 搜索结果的终端渲染组件 |
|
||||
| 适配器接口 | `packages/builtin-tools/src/tools/WebSearchTool/adapters/types.ts` | `WebSearchAdapter` 接口、`SearchResult`/`SearchOptions`/`SearchProgress` 类型 |
|
||||
| 适配器工厂 | `packages/builtin-tools/src/tools/WebSearchTool/adapters/index.ts` | `createAdapter()` 工厂函数,选择后端 |
|
||||
| API 适配器 | `packages/builtin-tools/src/tools/WebSearchTool/adapters/apiAdapter.ts` | 封装原有 `queryModelWithStreaming` 逻辑,使用 server tool |
|
||||
| Bing 适配器 | `packages/builtin-tools/src/tools/WebSearchTool/adapters/bingAdapter.ts` | Bing HTML 抓取 + 正则解析 |
|
||||
| Brave 适配器 | `packages/builtin-tools/src/tools/WebSearchTool/adapters/braveAdapter.ts` | Brave LLM Context API 适配与结果映射 |
|
||||
| 单元测试 | `packages/builtin-tools/src/tools/WebSearchTool/__tests__/bingAdapter.test.ts`, `packages/builtin-tools/src/tools/WebSearchTool/__tests__/braveAdapter*.test.ts`, `packages/builtin-tools/src/tools/WebSearchTool/__tests__/adapterFactory.test.ts` | Bing / Brave 解析与工厂逻辑测试 |
|
||||
| 集成测试 | `packages/builtin-tools/src/tools/WebSearchTool/__tests__/bingAdapter.integration.ts`, `packages/builtin-tools/src/tools/WebSearchTool/__tests__/braveAdapter.integration.ts` | 真实网络请求验证 |
|
||||
|
||||
### 2.3 数据流
|
||||
|
||||
@@ -49,20 +54,18 @@ WebSearchTool.call()
|
||||
validateInput() — 校验 query 非空、allowed/block 不共存
|
||||
│
|
||||
▼
|
||||
createAdapter() → BingSearchAdapter(当前硬编码)
|
||||
createAdapter() → ApiSearchAdapter | BingSearchAdapter | BraveSearchAdapter
|
||||
│
|
||||
▼
|
||||
adapter.search(query, { allowedDomains, blockedDomains, signal, onProgress })
|
||||
│
|
||||
├── onProgress({ type: 'query_update', query })
|
||||
│
|
||||
├── axios.get(bing.com/search?q=...&setmkt=en-US)
|
||||
│ └── 13 个 Edge 浏览器请求头
|
||||
├── axios.get(search-engine-url)
|
||||
│ └── API 鉴权请求头
|
||||
│
|
||||
├── extractBingResults(html) — 正则提取 <li class="b_algo"> 块
|
||||
│ ├── resolveBingUrl() — 解码 base64 重定向 URL
|
||||
│ ├── extractSnippet() — 三级降级摘要提取
|
||||
│ └── decodeHtmlEntities() — he.decode
|
||||
├── extractResults(payload) — 按后端提取结果
|
||||
│ └── grounding → SearchResult[] 映射
|
||||
│
|
||||
├── 客户端域名过滤 (allowedDomains / blockedDomains)
|
||||
│
|
||||
@@ -117,19 +120,18 @@ Bing 返回的重定向 URL 格式:`bing.com/ck/a?...&u=a1aHR0cHM6Ly9...`
|
||||
|
||||
## 四、适配器选择逻辑
|
||||
|
||||
当前 `createAdapter()` 硬编码返回 `BingSearchAdapter`,原逻辑已注释保留:
|
||||
`createAdapter()` 按以下优先级选择后端,并按选中的后端 key 缓存适配器实例:
|
||||
|
||||
```typescript
|
||||
export function createAdapter(): WebSearchAdapter {
|
||||
return new BingSearchAdapter()
|
||||
// 注释保留的选择逻辑:
|
||||
// 1. WEB_SEARCH_ADAPTER 环境变量强制指定 api|bing
|
||||
// 2. isFirstPartyAnthropicBaseUrl() → API 适配器
|
||||
// 3. 第三方端点 → Bing 适配器
|
||||
// 1. WEB_SEARCH_ADAPTER=api|bing|brave 显式指定
|
||||
// 2. Anthropic 官方 API Base URL → ApiSearchAdapter
|
||||
// 3. 第三方代理 / 非官方端点 → BingSearchAdapter
|
||||
}
|
||||
```
|
||||
|
||||
恢复自动选择:取消 `index.ts` 中的注释即可。
|
||||
显式指定 `WEB_SEARCH_ADAPTER=brave` 时,会改用 Brave LLM Context API 后端,并要求
|
||||
`BRAVE_SEARCH_API_KEY` 或 `BRAVE_API_KEY`。
|
||||
|
||||
## 五、接口定义
|
||||
|
||||
@@ -174,13 +176,13 @@ interface SearchProgress {
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `src/tools/WebSearchTool/WebSearchTool.ts` | 工具定义入口 |
|
||||
| `src/tools/WebSearchTool/prompt.ts` | 搜索工具 prompt |
|
||||
| `src/tools/WebSearchTool/UI.tsx` | 终端 UI 渲染 |
|
||||
| `src/tools/WebSearchTool/adapters/types.ts` | 适配器接口 |
|
||||
| `src/tools/WebSearchTool/adapters/index.ts` | 适配器工厂 |
|
||||
| `src/tools/WebSearchTool/adapters/apiAdapter.ts` | API 服务端搜索适配器 |
|
||||
| `src/tools/WebSearchTool/adapters/bingAdapter.ts` | Bing HTML 解析适配器 |
|
||||
| `src/tools/WebSearchTool/__tests__/bingAdapter.test.ts` | 单元测试 (32 cases) |
|
||||
| `src/tools/WebSearchTool/__tests__/bingAdapter.integration.ts` | 集成测试 |
|
||||
| `packages/builtin-tools/src/tools/WebSearchTool/WebSearchTool.ts` | 工具定义入口 |
|
||||
| `packages/builtin-tools/src/tools/WebSearchTool/prompt.ts` | 搜索工具 prompt |
|
||||
| `packages/builtin-tools/src/tools/WebSearchTool/UI.tsx` | 终端 UI 渲染 |
|
||||
| `packages/builtin-tools/src/tools/WebSearchTool/adapters/types.ts` | 适配器接口 |
|
||||
| `packages/builtin-tools/src/tools/WebSearchTool/adapters/index.ts` | 适配器工厂 |
|
||||
| `packages/builtin-tools/src/tools/WebSearchTool/adapters/apiAdapter.ts` | API 服务端搜索适配器 |
|
||||
| `packages/builtin-tools/src/tools/WebSearchTool/adapters/bingAdapter.ts` | Bing HTML 解析适配器 |
|
||||
| `packages/builtin-tools/src/tools/WebSearchTool/__tests__/bingAdapter.test.ts` | 单元测试 (32 cases) |
|
||||
| `packages/builtin-tools/src/tools/WebSearchTool/__tests__/bingAdapter.integration.ts` | 集成测试 |
|
||||
| `src/tools.ts` | 工具注册 |
|
||||
|
||||
@@ -14,17 +14,17 @@ WORKFLOW_SCRIPTS 实现基于文件的多步自动化工作流。用户可以定
|
||||
|
||||
| 模块 | 文件 | 状态 |
|
||||
|------|------|------|
|
||||
| WorkflowTool | `src/tools/WorkflowTool/WorkflowTool.ts` | **Stub** — 空对象 |
|
||||
| Workflow 权限 | `src/tools/WorkflowTool/WorkflowPermissionRequest.ts` | **Stub** — 返回 null |
|
||||
| 常量 | `src/tools/WorkflowTool/constants.ts` | **Stub** — 空工具名 |
|
||||
| 命令创建 | `src/tools/WorkflowTool/createWorkflowCommand.ts` | **Stub** — 空操作 |
|
||||
| 捆绑工作流 | `src/tools/WorkflowTool/bundled/` | **缺失** — 目录不存在 |
|
||||
| WorkflowTool | `packages/builtin-tools/src/tools/WorkflowTool/WorkflowTool.ts` | **部分实现** — tool schema + 渲染完整,call 返回运行时缺失提示 |
|
||||
| Workflow 权限 | `packages/builtin-tools/src/tools/WorkflowTool/WorkflowPermissionRequest.tsx` | **部分实现** — 权限请求组件 |
|
||||
| 常量 | `packages/builtin-tools/src/tools/WorkflowTool/constants.ts` | **实现** — 工具名 + 目录名 + 文件扩展名常量 |
|
||||
| 命令创建 | `packages/builtin-tools/src/tools/WorkflowTool/createWorkflowCommand.ts` | **实现** — 扫描 .claude/workflows/ 目录创建 Command 对象 |
|
||||
| 捆绑工作流 | `packages/builtin-tools/src/tools/WorkflowTool/bundled/index.ts` | **实现** — 内置工作流初始化 |
|
||||
| 本地工作流任务 | `src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts` | **Stub** — 类型 + 空操作 |
|
||||
| UI 任务组件 | `src/components/tasks/src/tasks/LocalWorkflowTask/` | **Stub** — 空导出 |
|
||||
| 详情对话框 | `src/components/tasks/WorkflowDetailDialog.ts` | **Stub** — 返回 null |
|
||||
| 任务注册 | `src/tasks.ts` | **布线** — 动态加载 |
|
||||
| 工具注册 | `src/tools.ts` | **布线** — 包含 bundled 工作流初始化 |
|
||||
| 命令注册 | `src/commands.ts` | **布线** — `/workflows` 命令 |
|
||||
| 工具注册 | `src/tools.ts` | **布线** — 动态加载 + bundled 工作流初始化 (行 131-134,235) |
|
||||
| 命令注册 | `src/commands.ts` | **布线** — `/workflows` 命令 (行 93-95,395,460) |
|
||||
|
||||
### 2.2 预期数据流
|
||||
|
||||
@@ -69,13 +69,9 @@ steps:
|
||||
|
||||
| 优先级 | 模块 | 工作量 | 说明 |
|
||||
|--------|------|--------|------|
|
||||
| 1 | `WorkflowTool.ts` | 大 | Schema 定义 + 多步执行引擎 |
|
||||
| 2 | `bundled/index.js` | 中 | 内置工作流定义(initBundledWorkflows) |
|
||||
| 3 | `createWorkflowCommand.ts` | 中 | 从文件解析创建命令对象 |
|
||||
| 4 | `LocalWorkflowTask.ts` | 大 | 步骤协调、kill/skip/retry |
|
||||
| 5 | `WorkflowDetailDialog.ts` | 中 | 进度详情 UI |
|
||||
| 6 | `WorkflowPermissionRequest.ts` | 小 | 权限对话框 |
|
||||
| 7 | `constants.ts` | 小 | 工具名常量 |
|
||||
| 1 | `WorkflowTool.ts` call 方法 | 中 | 实际工作流执行逻辑(当前返回运行时缺失提示) |
|
||||
| 2 | `LocalWorkflowTask.ts` | 大 | 步骤协调、kill/skip/retry |
|
||||
| 3 | `WorkflowDetailDialog.ts` | 中 | 进度详情 UI |
|
||||
|
||||
## 四、关键设计决策
|
||||
|
||||
@@ -95,11 +91,12 @@ FEATURE_WORKFLOW_SCRIPTS=1 bun run dev
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `src/tools/WorkflowTool/WorkflowTool.ts` | 工具定义(stub) |
|
||||
| `src/tools/WorkflowTool/WorkflowPermissionRequest.ts` | 权限对话框(stub) |
|
||||
| `src/tools/WorkflowTool/constants.ts` | 常量(stub) |
|
||||
| `src/tools/WorkflowTool/createWorkflowCommand.ts` | 命令创建(stub) |
|
||||
| `packages/builtin-tools/src/tools/WorkflowTool/WorkflowTool.ts` | 工具定义(部分实现) |
|
||||
| `packages/builtin-tools/src/tools/WorkflowTool/WorkflowPermissionRequest.tsx` | 权限请求组件 |
|
||||
| `packages/builtin-tools/src/tools/WorkflowTool/constants.ts` | 常量定义 |
|
||||
| `packages/builtin-tools/src/tools/WorkflowTool/createWorkflowCommand.ts` | 命令创建(已实现) |
|
||||
| `packages/builtin-tools/src/tools/WorkflowTool/bundled/index.ts` | 内置工作流初始化 |
|
||||
| `src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts` | 任务协调(stub) |
|
||||
| `src/components/tasks/WorkflowDetailDialog.ts` | 详情对话框(stub) |
|
||||
| `src/tools.ts:127-132` | 工具注册 |
|
||||
| `src/commands.ts:86-89` | 命令注册 |
|
||||
| `src/tools.ts:131-134,235` | 工具注册 |
|
||||
| `src/commands.ts:93-95,395,460` | 命令注册 |
|
||||
|
||||
@@ -17,7 +17,7 @@ keywords: ["Ant 特权", "USER_TYPE", "身份门控", "内部功能", "Anthropic
|
||||
|
||||
`BUILD_TARGET` 等构建时常量在反编译版本中已被移除。`USER_TYPE` 通过 Bun 的 `--define` 或环境变量注入,Bun 会进行**常量折叠**——所有 `process.env.USER_TYPE === 'ant'` 在外部构建中直接变为 `false`,后续代码被 DCE 移除。但在反编译版本中,这些代码保留完整。
|
||||
|
||||
`USER_TYPE === 'ant'` 在代码库中出现 **377+ 次**(含 `=== 'ant'` 291 次、`(process.env.USER_TYPE) === 'ant'` 86 次),另有 `!== 'ant'` 53 次、其他引用约 35 次,总计 **465 处引用**,控制着工具、命令、API、UI 等方方面面。
|
||||
`USER_TYPE === 'ant'` 在代码库中出现 **351+ 次**(跨 163 个文件),另有 `!== 'ant'` 59 次(跨 38 个文件),总计 **410+ 处引用**,控制着工具、命令、API、UI 等方方面面。
|
||||
|
||||
## Ant-Only 工具
|
||||
|
||||
@@ -25,10 +25,10 @@ keywords: ["Ant 特权", "USER_TYPE", "身份门控", "内部功能", "Anthropic
|
||||
|
||||
| 工具 | 代码位置 | 用途 |
|
||||
|------|---------|------|
|
||||
| **REPLTool** | `src/tools/REPLTool/` | 高级 REPL 模式——在 VM 中包装 Bash/Read/Edit/Glob/Grep/Agent 等工具 |
|
||||
| **SuggestBackgroundPRTool** | `src/tools/SuggestBackgroundPRTool/` | 建议在后台创建 PR |
|
||||
| **ConfigTool** | `src/tools/ConfigTool/` | 交互式配置编辑器,包含 Gates 标签页用于覆盖 GrowthBook flags |
|
||||
| **TungstenTool** | `src/tools/TungstenTool/` | 基于 tmux 的终端面板工具(反编译版中已 stub) |
|
||||
| **REPLTool** | `packages/builtin-tools/src/tools/REPLTool/` | 高级 REPL 模式——在 VM 中包装 Bash/Read/Edit/Glob/Grep/Agent 等工具 |
|
||||
| **SuggestBackgroundPRTool** | `packages/builtin-tools/src/tools/SuggestBackgroundPRTool/` | 建议在后台创建 PR |
|
||||
| **ConfigTool** | `packages/builtin-tools/src/tools/ConfigTool/` | 交互式配置编辑器,包含 Gates 标签页用于覆盖 GrowthBook flags |
|
||||
| **TungstenTool** | `packages/builtin-tools/src/tools/TungstenTool/` | 基于 tmux 的终端面板工具(反编译版中已 stub) |
|
||||
|
||||
```typescript
|
||||
// src/tools.ts 第 14-24 行——条件导入 + Dead Code Elimination 标记
|
||||
@@ -36,18 +36,18 @@ keywords: ["Ant 特权", "USER_TYPE", "身份门控", "内部功能", "Anthropic
|
||||
/* eslint-disable custom-rules/no-process-env-top-level, @typescript-eslint/no-require-imports */
|
||||
const REPLTool =
|
||||
process.env.USER_TYPE === 'ant'
|
||||
? require('./tools/REPLTool/REPLTool.js').REPLTool
|
||||
? require('@claude-code-best/builtin-tools/tools/REPLTool/REPLTool.js').REPLTool
|
||||
: null
|
||||
const SuggestBackgroundPRTool =
|
||||
process.env.USER_TYPE === 'ant'
|
||||
? require('./tools/SuggestBackgroundPRTool/SuggestBackgroundPRTool.js')
|
||||
? require('@claude-code-best/builtin-tools/tools/SuggestBackgroundPRTool/SuggestBackgroundPRTool.js')
|
||||
.SuggestBackgroundPRTool
|
||||
: null
|
||||
```
|
||||
|
||||
## Ant-Only 命令
|
||||
|
||||
`src/commands.ts` 注册了 **28** 个仅在内部构建中可用的斜杠命令(`INTERNAL_ONLY_COMMANDS`,lines 225-254),在 `USER_TYPE === 'ant' && !IS_DEMO` 时才加载(line 343-345):
|
||||
`src/commands.ts` 注册了 **24+** 个仅在内部构建中可用的斜杠命令(`INTERNAL_ONLY_COMMANDS`,lines 267-295),在 `USER_TYPE === 'ant' && !IS_DEMO` 时才加载(line 400-401):
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="调试类">
|
||||
@@ -74,7 +74,7 @@ const SuggestBackgroundPRTool =
|
||||
- `summary` — 生成摘要
|
||||
- `subscribePr` — 订阅 PR(需要 `KAIROS_GITHUB_WEBHOOKS` feature flag)
|
||||
- `forceSnip` — 强制截断历史(需要 `HISTORY_SNIP` feature flag)
|
||||
- `ultraplan` — 超级规划(需要 `ULTRAPLAN` feature flag)
|
||||
- `ultraplan` — 超级规划(需要 `ULTRAPLAN` feature flag,单独注册于 `commands.ts:396`)
|
||||
</Accordion>
|
||||
<Accordion title="基础设施类">
|
||||
- `backfillSessions` — 回填会话数据
|
||||
|
||||
@@ -15,17 +15,19 @@ Claude Code 使用 Bun 打包器的 `bun:bundle` 模块提供编译时特性门
|
||||
import { feature } from 'bun:bundle'
|
||||
|
||||
const SleepTool = feature('PROACTIVE') || feature('KAIROS')
|
||||
? require('./tools/SleepTool/SleepTool.js').SleepTool
|
||||
? require('@claude-code-best/builtin-tools/tools/SleepTool/SleepTool.js').SleepTool
|
||||
: null
|
||||
```
|
||||
|
||||
在 Anthropic 的内部构建中,`feature()` 在打包时被求值——返回 `true` 的代码会被保留,返回 `false` 的代码会被 **Dead Code Elimination (DCE)** 彻底移除。
|
||||
|
||||
在我们的反编译版本中,这个函数被兜底为:
|
||||
在我们的反编译版本中,`feature` 从 `bun:bundle` 导入(声明在 `src/types/internal-modules.d.ts`),在运行时始终返回 `false`:
|
||||
|
||||
```typescript
|
||||
// src/entrypoints/cli.tsx 第 3 行
|
||||
const feature = (_name: string) => false;
|
||||
// src/types/internal-modules.d.ts
|
||||
declare module 'bun:bundle' {
|
||||
export function feature(name: string): boolean;
|
||||
}
|
||||
```
|
||||
|
||||
这意味着所有 88+ 个 feature flag 后的代码**在运行时永远不会执行**,但代码本身完整保留,可以阅读和分析。
|
||||
@@ -79,7 +81,7 @@ Feature flags 在代码中主要有三种使用模式:
|
||||
```typescript
|
||||
// src/tools.ts — 最常见的模式
|
||||
const MonitorTool = feature('MONITOR_TOOL')
|
||||
? require('./tools/MonitorTool/MonitorTool.js').MonitorTool
|
||||
? require('@claude-code-best/builtin-tools/tools/MonitorTool/MonitorTool.js').MonitorTool
|
||||
: null
|
||||
```
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ keywords: ["GrowthBook", "A/B 测试", "运行时门控", "tengu", "渐进式发
|
||||
|
||||
## 集成架构
|
||||
|
||||
GrowthBook 的完整实现位于 `src/services/analytics/growthbook.ts`(1156 行),工作流程如下:
|
||||
GrowthBook 的完整实现位于 `src/services/analytics/growthbook.ts`(1258 行),工作流程如下:
|
||||
|
||||
<Steps>
|
||||
<Step title="启动时获取远程配置">
|
||||
|
||||
@@ -84,7 +84,7 @@ keywords: ["隐藏功能", "未公开功能", "秘密功能", "Claude Code 彩
|
||||
<Accordion title="VOICE_MODE:语音交互">
|
||||
**门控**: `feature('VOICE_MODE')`
|
||||
|
||||
代码中存在语音输入模式的注册点,但核心实现依赖于 `audio-napi` 包(在反编译版本中已 stub):
|
||||
代码中存在语音输入模式的注册点,核心实现依赖 `audio-capture-napi` 包(已恢复):
|
||||
|
||||
- 通过 `/voice` 命令激活
|
||||
- "按住说话"(hold-to-talk)交互模式
|
||||
|
||||
@@ -64,24 +64,27 @@ Claude Code 从上到下分为五个层次,每一层职责清晰、边界分
|
||||
needsFollowUp ? continue : return { reason }
|
||||
```
|
||||
|
||||
完整的状态机通过 `State` 类型(`src/query.ts:204`)在迭代间传递,包含 10 个字段(messages、autoCompactTracking、maxOutputTokensRecoveryCount 等)。
|
||||
完整的状态机通过 `State` 类型(`src/query.ts:207`)在迭代间传递,包含 10 个字段(messages、autoCompactTracking、maxOutputTokensRecoveryCount 等)。
|
||||
|
||||
### 4. 工具层(`src/tools.ts` → `src/Tool.ts`)
|
||||
|
||||
`getAllBaseTools()`(`src/tools.ts:191`)组装 50+ 工具列表,经过 `filterToolsByDenyRules()` 权限过滤后传给 API。
|
||||
`getAllBaseTools()`(`src/tools.ts:195`)组装 50+ 工具列表,经过 `filterToolsByDenyRules()` 权限过滤后传给 API。
|
||||
|
||||
每个工具实现 `Tool<Input, Output, Progress>` 接口(`src/Tool.ts:362`),核心方法链:
|
||||
每个工具实现 `Tool<Input, Output, Progress>` 接口(`src/Tool.ts:368`),核心方法链:
|
||||
```
|
||||
validateInput() → canUseTool()(UI 层)→ checkPermissions() → call() → ToolResult
|
||||
```
|
||||
|
||||
### 5. 通信层(`src/services/api/claude.ts`)
|
||||
|
||||
API 客户端支持 4 种 Provider:
|
||||
- **Anthropic Direct**:默认
|
||||
API 客户端支持 7 种 Provider:
|
||||
- **Anthropic Direct (firstParty)**:默认
|
||||
- **AWS Bedrock**:`ANTHROPIC_BEDROCK_BASE_URL`
|
||||
- **Google Vertex**:`ANTHROPIC_VERTEX_PROJECT_ID`
|
||||
- **Azure**:通过自定义 base URL
|
||||
- **Foundry**:`ANTHROPIC_CODE_USE_FOUNDRY`
|
||||
- **OpenAI**:兼容层
|
||||
- **Gemini**:兼容层
|
||||
- **Grok (xAI)**:兼容层
|
||||
|
||||
`deps.callModel()` 发起流式请求,返回 `BetaRawMessageStreamEvent` 事件流。支持 Prompt Cache(`cache_control`)、thinking blocks、multi-turn tool use。
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ Claude Code 是一个**运行在本地终端中的 agentic coding system**。它
|
||||
│ 实际执行: 读文件、运行命令、搜索代码... │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 6. 通信层 (claude.ts → Anthropic API) │
|
||||
│ 流式 HTTP, 支持 Bedrock/Vertex/Azure 多 provider │
|
||||
│ 流式 HTTP, 支持 Bedrock/Vertex/Foundry 等 7 种 provider │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ AI 没有真正的"记忆",Claude Code 通过精心分层营造了这个幻觉
|
||||
|
||||
### 3. 工具系统的权限双轨制
|
||||
|
||||
`src/tools/BashTool/shouldUseSandbox.ts` 展示了一个精巧的双重安全模型:
|
||||
`packages/builtin-tools/src/tools/BashTool/shouldUseSandbox.ts` 展示了一个精巧的双重安全模型:
|
||||
|
||||
- **应用层**:权限规则决定"能不能执行"(白名单/黑名单/用户确认)
|
||||
- **OS 层**:沙箱决定"执行时能做什么"(文件系统/网络/进程隔离)
|
||||
|
||||
@@ -65,7 +65,7 @@ ENABLE_LSP_TOOL=1 bun run dev
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ LSP Tool │
|
||||
│ src/tools/LSPTool/LSPTool.ts │
|
||||
│ packages/builtin-tools/src/tools/LSPTool/LSPTool.ts│
|
||||
│ (Claude 可调用的工具,9 种操作) │
|
||||
└──────────────────────┬──────────────────────────────┘
|
||||
│
|
||||
@@ -128,10 +128,10 @@ LSP 服务器会异步推送 `textDocument/publishDiagnostics` 通知,经去
|
||||
| `src/services/lsp/config.ts` | 从插件加载 LSP 服务器配置 |
|
||||
| `src/services/lsp/LSPDiagnosticRegistry.ts` | 诊断信息注册、去重、容量限制 |
|
||||
| `src/services/lsp/passiveFeedback.ts` | 注册 `publishDiagnostics` 通知处理器 |
|
||||
| `src/tools/LSPTool/LSPTool.ts` | LSP Tool 实现(暴露给 Claude) |
|
||||
| `src/tools/LSPTool/schemas.ts` | 输入 schema(9 种操作的 discriminated union) |
|
||||
| `src/tools/LSPTool/formatters.ts` | 各操作结果的格式化 |
|
||||
| `src/tools/LSPTool/prompt.ts` | Tool 描述文本 |
|
||||
| `packages/builtin-tools/src/tools/LSPTool/LSPTool.ts` | LSP Tool 实现(暴露给 Claude) |
|
||||
| `packages/builtin-tools/src/tools/LSPTool/schemas.ts` | 输入 schema(9 种操作的 discriminated union) |
|
||||
| `packages/builtin-tools/src/tools/LSPTool/formatters.ts` | 各操作结果的格式化 |
|
||||
| `packages/builtin-tools/src/tools/LSPTool/prompt.ts` | Tool 描述文本 |
|
||||
| `src/utils/plugins/lspPluginIntegration.ts` | 从插件加载、验证、环境变量解析、作用域管理 |
|
||||
|
||||
## LSP Tool 支持的操作
|
||||
|
||||
@@ -137,7 +137,7 @@ Auto mode 可通过以下方式激活:
|
||||
|
||||
### 进入时(Full Instructions)
|
||||
|
||||
注入到对话中的指令(`messages.ts:3464`):
|
||||
注入到对话中的指令(`messages.ts:3481`):
|
||||
|
||||
> Auto mode is active. The user chose continuous, autonomous execution. You should:
|
||||
>
|
||||
|
||||
@@ -18,17 +18,19 @@ keywords: ["权限模型", "Allow Ask Deny", "PermissionRule", "checkPermissions
|
||||
|
||||
这些行为由 `PermissionResult` 类型定义(`src/utils/permissions/PermissionResult.ts`)。
|
||||
|
||||
## 权限规则的五层来源
|
||||
## 权限规则的来源
|
||||
|
||||
规则从 5 个来源汇聚(`PERMISSION_RULE_SOURCES`,`permissions.ts:109`),优先级从高到低:
|
||||
规则从 8 个来源汇聚(`PERMISSION_RULE_SOURCES`,`permissions.ts:109`),优先级从低到高(后者覆盖前者):
|
||||
|
||||
```
|
||||
1. session — 用户在当前对话中手动授权("Always allow")
|
||||
2. cliArg — 命令行 --allow/--deny 参数
|
||||
3. command — Skill 工具的 allowedTools 白名单
|
||||
4. projectSettings — .claude/settings.json(团队共享)
|
||||
5. userSettings — ~/.claude/settings.json(跨项目)
|
||||
6. policySettings — 企业管理员下发的策略(用户不可覆盖)
|
||||
1. userSettings — ~/.claude/settings.json(跨项目)
|
||||
2. projectSettings — .claude/settings.json(团队共享)
|
||||
3. localSettings — .claude/settings.local.json(gitignored,个人覆盖)
|
||||
4. flagSettings — --settings 命令行参数
|
||||
5. policySettings — 企业管理员下发的策略(用户不可覆盖)
|
||||
6. cliArg — 命令行 --allow/--deny 参数
|
||||
7. command — Skill 工具的 allowedTools 白名单
|
||||
8. session — 用户在当前对话中手动授权("Always allow")
|
||||
```
|
||||
|
||||
每个来源维护三个数组:`alwaysAllowRules[source]`、`alwaysAskRules[source]`、`alwaysDenyRules[source]`。
|
||||
@@ -65,7 +67,7 @@ MCP 工具使用 `getToolNameForPermissionCheck()` 获取匹配名称,支持
|
||||
|
||||
**2. 命令模式匹配**(BashTool 的 `checkPermissions()`)
|
||||
|
||||
BashTool 通过 `preparePermissionMatcher()`(`Tool.ts:514`)解析命令模式:
|
||||
BashTool 通过 `preparePermissionMatcher()`(`Tool.ts:520`)解析命令模式:
|
||||
```json
|
||||
{"tool": "Bash", "ruleContent": "git *"} → 匹配 "git commit -m 'fix'"
|
||||
```
|
||||
@@ -120,7 +122,9 @@ Read/Edit/Write 工具通过 `getPath()` 提取文件路径,与 `ruleContent`
|
||||
|------|---------------------|---------|------|
|
||||
| **Default** | `'default'` | 日常使用 | 敏感操作逐一确认 |
|
||||
| **Plan Mode** | `'plan'` | 探索阶段 | 只能读不能写(`isReadOnly()` 检查) |
|
||||
| **Auto** | `'auto'` | 信任 AI | 通过 transcript classifier 自动决策 |
|
||||
| **Accept Edits** | `'acceptEdits'` | 快速迭代 | 工作区内文件编辑自动放行,其他操作仍需确认 |
|
||||
| **Don't Ask** | `'dontAsk'` | 减少打断 | 尽量自动决策,减少确认弹窗 |
|
||||
| **Auto** | `'auto'` | 信任 AI | 通过 transcript classifier 自动决策(需 `TRANSCRIPT_CLASSIFIER` feature flag) |
|
||||
| **Bypass** | `'bypassPermissions'` | 完全信任 | 所有操作自动放行(需显式 `--dangerously-skip-permissions`) |
|
||||
|
||||
Plan Mode 切换由 `EnterPlanModeTool.call()` 触发:
|
||||
@@ -143,8 +147,8 @@ context.setAppState(prev => ({
|
||||
|
||||
```typescript
|
||||
const DENIAL_LIMITS = {
|
||||
maxDenialsPerTool: 3, // 同一工具连续拒绝上限
|
||||
cooldownPeriodMs: 30_000, // 冷却期 30 秒
|
||||
maxConsecutive: 3, // 同一工具连续拒绝上限
|
||||
maxTotal: 20, // 总拒绝上限
|
||||
}
|
||||
```
|
||||
|
||||
@@ -162,9 +166,12 @@ const DENIAL_LIMITS = {
|
||||
|
||||
```typescript
|
||||
type PermissionUpdate =
|
||||
| { type: 'addRule', behavior, rule, destination }
|
||||
| { type: 'removeRule', behavior, rule, destination }
|
||||
| { type: 'setMode', mode, destination }
|
||||
| { type: 'addRules', destination, rules, behavior }
|
||||
| { type: 'replaceRules', destination, rules, behavior }
|
||||
| { type: 'removeRules', destination, rules, behavior }
|
||||
| { type: 'setMode', destination, mode }
|
||||
| { type: 'addDirectories', destination, directories }
|
||||
| { type: 'removeDirectories', destination, directories }
|
||||
```
|
||||
|
||||
当用户在 Ask 对话框中选择 "Always allow",系统调用 `persistPermissionUpdates()` 将规则写入对应层级的 settings 文件(project/user/managed),同时更新内存中的 `toolPermissionContext`。
|
||||
|
||||
@@ -16,13 +16,13 @@ keywords: ["Plan Mode", "计划模式", "EnterPlanMode", "ExitPlanMode", "prepar
|
||||
|
||||
<Steps>
|
||||
<Step title="EnterPlanMode — 进入计划模式">
|
||||
AI 自主判断(或用户触发)任务需要规划,调用 `EnterPlanModeTool`(`src/tools/EnterPlanModeTool/EnterPlanModeTool.ts:36`)。该工具需要**用户审批**(`checkPermissions` 返回 `ask`)。
|
||||
AI 自主判断(或用户触发)任务需要规划,调用 `EnterPlanModeTool`(`packages/builtin-tools/src/tools/EnterPlanModeTool/EnterPlanModeTool.ts:36`)。该工具需要**用户审批**(`checkPermissions` 返回 `ask`)。
|
||||
</Step>
|
||||
<Step title="探索阶段 — 只读工具集">
|
||||
权限模式切换为 `'plan'`,AI 只能使用 `isReadOnly()` 为 true 的工具(Read、Grep、Glob、Agent 等)。写操作被自动拒绝。
|
||||
</Step>
|
||||
<Step title="ExitPlanMode — 提交方案审批">
|
||||
AI 完成探索后,调用 `ExitPlanModeV2Tool`(`src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts:147`),将计划文件提交给用户审阅。这是第二个**需要用户审批**的节点。
|
||||
AI 完成探索后,调用 `ExitPlanModeV2Tool`(`packages/builtin-tools/src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts:147`),将计划文件提交给用户审阅。这是第二个**需要用户审批**的节点。
|
||||
</Step>
|
||||
<Step title="恢复执行 — 全部工具权限">
|
||||
用户批准后,权限模式恢复为进入前的状态,AI 按计划执行。
|
||||
@@ -107,7 +107,7 @@ if (isTeammate()) {
|
||||
|
||||
## 什么时候该用计划模式
|
||||
|
||||
`EnterPlanModeTool` 的 Prompt(`src/tools/EnterPlanModeTool/prompt.ts`)定义了两套触发标准——外部版本更积极(鼓励规划),内部版本更克制(仅在真正模糊时使用):
|
||||
`EnterPlanModeTool` 的 Prompt(`packages/builtin-tools/src/tools/EnterPlanModeTool/prompt.ts`)定义了两套触发标准——外部版本更积极(鼓励规划),内部版本更克制(仅在真正模糊时使用):
|
||||
|
||||
| 场景 | 外部版本 | 内部版本 |
|
||||
|------|---------|---------|
|
||||
|
||||
@@ -166,7 +166,7 @@ keywords: ["沙箱", "sandbox", "权限", "Bash", "PowerShell", "bubblewrap", "s
|
||||
5. 这条命令没有被显式排除
|
||||
6. 这次调用没有被允许以 `dangerouslyDisableSandbox` 绕过
|
||||
|
||||
对应入口在 `src/tools/BashTool/shouldUseSandbox.ts` 和 `src/utils/sandbox/sandbox-adapter.ts`。
|
||||
对应入口在 `packages/builtin-tools/src/tools/BashTool/shouldUseSandbox.ts` 和 `src/utils/sandbox/sandbox-adapter.ts`。
|
||||
|
||||
### 3. PowerShell 只在支持平台上走
|
||||
|
||||
@@ -518,11 +518,11 @@ REPL / CLI 启动
|
||||
|
||||
如果你想继续顺着源码深入,推荐按下面顺序看:
|
||||
|
||||
1. `src/tools/BashTool/shouldUseSandbox.ts`
|
||||
1. `packages/builtin-tools/src/tools/BashTool/shouldUseSandbox.ts`
|
||||
2. `src/utils/Shell.ts`
|
||||
3. `src/utils/sandbox/sandbox-adapter.ts`
|
||||
4. `src/utils/permissions/permissions.ts`
|
||||
5. `src/tools/BashTool/bashPermissions.ts`
|
||||
5. `packages/builtin-tools/src/tools/BashTool/bashPermissions.ts`
|
||||
6. `src/utils/permissions/pathValidation.ts`
|
||||
7. `src/utils/permissions/filesystem.ts`
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ Claude 的 System Prompt 中包含安全指令——这是"软性"约束,依
|
||||
| `deny` | 直接拒绝 | 匹配 deny 规则 |
|
||||
| `ask` | 弹窗确认 | 未匹配任何规则 或 匹配 ask 规则 |
|
||||
|
||||
以 BashTool 为例(`src/tools/BashTool/bashPermissions.ts`),`bashToolHasPermission()` 执行了极其细致的检查链:
|
||||
以 BashTool 为例(`packages/builtin-tools/src/tools/BashTool/bashPermissions.ts`),`bashToolHasPermission()` 执行了极其细致的检查链:
|
||||
|
||||
1. **AST 安全解析**:用 tree-sitter 解析 bash AST,检测命令注入(`$()`、反引号等)
|
||||
2. **语义检查**:识别危险命令(`eval`、`exec`、`source` 等)
|
||||
@@ -169,7 +169,7 @@ Bash("rm -rf node_modules") → ⚠️ 需确认(不可逆)
|
||||
攻击:cd /malicious/dir && git status
|
||||
/malicious/dir 包含 bare repo + 恶意钩子
|
||||
防御:bashToolHasPermission() 检测 cd + git 组合
|
||||
强制 require approval(src/tools/BashTool/bashPermissions.ts:2209)
|
||||
强制 require approval(packages/builtin-tools/src/tools/BashTool/bashPermissions.ts:2209)
|
||||
```
|
||||
|
||||
### 场景3:管道注入
|
||||
@@ -178,5 +178,5 @@ Bash("rm -rf node_modules") → ⚠️ 需确认(不可逆)
|
||||
攻击:echo 'x' | xargs printf '%s' >> /etc/passwd
|
||||
splitCommand 会剥离重定向,导致路径检查遗漏
|
||||
防御:即使管道段独立检查通过,仍对原始命令重新验证路径约束
|
||||
检查重定向目标中的危险模式(反引号、$())(bashPermissions.ts:1992-2056)
|
||||
检查重定向目标中的危险模式(反引号、$())(packages/builtin-tools/src/tools/BashTool/bashPermissions.ts:1992-2056)
|
||||
```
|
||||
|
||||
77
docs/task/task-001-daemon-status-stop.md
Normal file
77
docs/task/task-001-daemon-status-stop.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Task 001: daemon status / stop
|
||||
|
||||
> 来源: [stub-recovery-design-1-4.md](../features/stub-recovery-design-1-4.md) 第 1 项
|
||||
> 优先级: P0 (首选实现项)
|
||||
> 工作量: 小
|
||||
> 状态: DONE
|
||||
|
||||
## 目标
|
||||
|
||||
让 `claude daemon status` 和 `claude daemon stop` 在任意 CLI 进程中都能正确工作,不依赖 TUI 内存态。
|
||||
|
||||
## 背景
|
||||
|
||||
- `start` 路径已有完整 supervisor + worker 生命周期 (`src/daemon/main.ts`, `src/daemon/workerRegistry.ts`)
|
||||
- `status` / `stop` 目前只是占位输出 (`src/daemon/main.ts:49`)
|
||||
- `/remote-control-server` 有自己的命令内 UI 状态,但只维护当前进程内的 `daemonProcess`,不适合跨进程管理
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 新增文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `src/daemon/state.ts` | daemon 状态文件读写模块 |
|
||||
|
||||
### 修改文件
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `src/daemon/main.ts` | `start` 写入状态文件;`status`/`stop` 调用 state 模块 |
|
||||
| `src/commands/remoteControlServer/remoteControlServer.tsx` | 读取同一份状态文件(轻量改动) |
|
||||
|
||||
### 状态文件
|
||||
|
||||
路径: `~/.claude/daemon/remote-control.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"pid": 12345,
|
||||
"cwd": "/path/to/project",
|
||||
"startedAt": "2026-04-12T10:00:00Z",
|
||||
"workerKinds": ["bridge", "rcs"],
|
||||
"lastStatus": "running"
|
||||
}
|
||||
```
|
||||
|
||||
### status 逻辑
|
||||
|
||||
1. 读取状态文件
|
||||
2. 用进程探测验证 pid 是否存活
|
||||
3. 输出 `running` / `stopped` / `stale`
|
||||
4. stale 时自动清理状态文件
|
||||
|
||||
### stop 逻辑
|
||||
|
||||
1. 读取 pid
|
||||
2. 发送 `SIGTERM`
|
||||
3. 等待退出(超时兜底)
|
||||
4. 超时后 `SIGKILL`
|
||||
5. 清理状态文件
|
||||
|
||||
## 验证步骤
|
||||
|
||||
- [ ] `claude daemon start` 正常启动并写入状态文件
|
||||
- [ ] 新开终端执行 `claude daemon status`,显示 `running`
|
||||
- [ ] 执行 `claude daemon stop`,daemon 正常退出
|
||||
- [ ] 再次执行 `claude daemon status`,返回 `stopped` 或 `stale cleaned`
|
||||
- [ ] Windows 下 stop 超时兜底正常工作
|
||||
|
||||
## 风险
|
||||
|
||||
- Windows 信号模型和 Unix 不同,`stop` 需要超时兜底
|
||||
- 当前设计默认单 supervisor,不处理多实例并发
|
||||
|
||||
## 依赖
|
||||
|
||||
无外部依赖,可独立实施。
|
||||
80
docs/task/task-002-bg-sessions-ps-logs-kill.md
Normal file
80
docs/task/task-002-bg-sessions-ps-logs-kill.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Task 002: BG_SESSIONS — ps / logs / kill
|
||||
|
||||
> 来源: [stub-recovery-design-1-4.md](../features/stub-recovery-design-1-4.md) 第 2 项
|
||||
> 优先级: P1
|
||||
> 工作量: 中等
|
||||
> 状态: DONE
|
||||
> 阶段: Phase 2A (MVP)
|
||||
|
||||
## 目标
|
||||
|
||||
把 `ps` / `logs` / `kill` 做成真正有用的 session 管理命令。不在第一阶段补完 `attach` / `--bg`。
|
||||
|
||||
## 背景
|
||||
|
||||
- fast-path 已接好 (`src/entrypoints/cli.tsx:218`)
|
||||
- session registry 已有真实实现 (`src/utils/concurrentSessions.ts`)
|
||||
- `exit` 在 bg session 内已会 `tmux detach-client` (`src/commands/exit/exit.tsx:20`)
|
||||
- CLI handler 仍全空 (`src/cli/bg.ts`)
|
||||
- task summary 仍然是 stub (`src/utils/taskSummary.ts`)
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 修改文件
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `src/cli/bg.ts` | 实现 `ps` / `logs` / `kill` handler |
|
||||
| `src/utils/concurrentSessions.ts` | 扩展以便后续 attach/--bg 使用 |
|
||||
| `src/utils/taskSummary.ts` | 补充基础实现 |
|
||||
|
||||
### 复用模块
|
||||
|
||||
- `src/utils/sessionStorage.ts` — session 存储
|
||||
- `src/utils/udsClient.ts` — UDS 通信
|
||||
|
||||
### ps 命令
|
||||
|
||||
- 从 registry 读取 live sessions
|
||||
- 展示: pid, kind, sessionId, cwd, name, startedAt, bridgeSessionId
|
||||
- 如果有 activity/status,一并展示
|
||||
|
||||
### logs 命令
|
||||
|
||||
- 支持按 `sessionId` / `pid` / `name` 查找
|
||||
- 优先复用本地 transcript/log 读取能力
|
||||
- 如果 registry 里存在 `logPath`,支持 tail 文件
|
||||
|
||||
### kill 命令
|
||||
|
||||
- 解析目标 session
|
||||
- 发退出信号
|
||||
- 清理 stale registry
|
||||
|
||||
## 验证步骤
|
||||
|
||||
- [ ] `ps` 能列出当前 live sessions
|
||||
- [ ] `logs <sessionId|pid|name>` 能输出对应日志
|
||||
- [ ] `kill <sessionId|pid|name>` 能结束目标 session 并清理 registry
|
||||
- [ ] 无 live session 时各命令有明确提示
|
||||
|
||||
## Phase 2B (后续)
|
||||
|
||||
- [ ] 实现 `attach`
|
||||
- [ ] 实现 `--bg`
|
||||
- [ ] 实现 `taskSummary` 的中途状态更新
|
||||
|
||||
### 为什么拆分
|
||||
|
||||
- 现有 registry 记录了 `pid / sessionId / name / logPath`
|
||||
- 但没有可靠的 tmux attach target
|
||||
- `attach` 和 `--bg` 需要补启动/附着元数据设计,不是简单补 handler
|
||||
|
||||
## 风险
|
||||
|
||||
- `attach` / `--bg` 第二阶段需要 tmux 元数据设计
|
||||
- Windows 下 tmux 路径需要明确降级策略
|
||||
|
||||
## 依赖
|
||||
|
||||
- Task 001 (daemon 状态管理可复用模式,但非硬性依赖)
|
||||
87
docs/task/task-003-templates-job-mvp.md
Normal file
87
docs/task/task-003-templates-job-mvp.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Task 003: TEMPLATES — job 文件系统 MVP
|
||||
|
||||
> 来源: [stub-recovery-design-1-4.md](../features/stub-recovery-design-1-4.md) 第 3 项
|
||||
> 优先级: P2
|
||||
> 工作量: 中等
|
||||
> 状态: DONE
|
||||
> 阶段: MVP
|
||||
|
||||
## 目标
|
||||
|
||||
把 `new` / `list` / `reply` 做成可用的模板任务系统。第一阶段不碰复杂的自动分类与自动执行。
|
||||
|
||||
## 背景
|
||||
|
||||
- 命令入口只有 fast-path (`src/entrypoints/cli.tsx:272`)
|
||||
- handler 是空的 (`src/cli/handlers/templateJobs.ts`)
|
||||
- `markdownConfigLoader` 已把 `templates` 纳入配置目录 (`src/utils/markdownConfigLoader.ts:35`)
|
||||
- `query/stopHooks` 已预留 job classifier 链路 (`src/query/stopHooks.ts:103`)
|
||||
- `jobs/classifier.ts` 仍是 stub (`src/jobs/classifier.ts`)
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 新增文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `src/jobs/state.ts` | job 状态管理 |
|
||||
| `src/jobs/templates.ts` | 模板解析与列表 |
|
||||
|
||||
### 修改文件
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `src/cli/handlers/templateJobs.ts` | 实现 `new` / `list` / `reply` handler |
|
||||
|
||||
### 模板来源
|
||||
|
||||
`.claude/templates/*.md`
|
||||
|
||||
### 模板格式
|
||||
|
||||
复用现有 markdown + frontmatter 解析,不另外设计 DSL。
|
||||
|
||||
### list 命令
|
||||
|
||||
- 列出所有模板
|
||||
- 显示: 模板名, description, 路径
|
||||
|
||||
### new 命令
|
||||
|
||||
- 解析模板
|
||||
- 在 `~/.claude/jobs/<job-id>/` 下创建 job 目录
|
||||
- 写入 `template.md`, `input.txt`, `state.json`
|
||||
- 返回 job id 与目录路径
|
||||
|
||||
### reply 命令
|
||||
|
||||
- 将回复写入 `replies.jsonl` 或 `input.txt`
|
||||
- 更新 `state.json`
|
||||
|
||||
## 验证步骤
|
||||
|
||||
- [ ] `list` 能列出 `.claude/templates` 下的所有模板
|
||||
- [ ] `new <template> [args...]` 能创建 job 目录和状态文件
|
||||
- [ ] `reply <job-id> <text>` 能更新 job 内容和状态
|
||||
- [ ] frontmatter schema 最小字段集已定义
|
||||
|
||||
## Phase 2 (后续)
|
||||
|
||||
- [ ] 恢复 `src/jobs/classifier.ts`
|
||||
- [ ] 让带 `CLAUDE_JOB_DIR` 的 job session 在 turn 完成后自动更新 `state.json`
|
||||
- [ ] 再决定是否补自动 job runner
|
||||
|
||||
### 为什么拆分
|
||||
|
||||
- 当前是 "template job commands",不是单纯模板列表
|
||||
- 自动 job 运行链路没有足够现成实现
|
||||
- 先做文件系统 job lifecycle 更稳
|
||||
|
||||
## 风险
|
||||
|
||||
- frontmatter schema 需要先定义最小字段集
|
||||
- 一旦扩展到"自动运行 job",范围会明显膨胀
|
||||
|
||||
## 依赖
|
||||
|
||||
无硬性依赖,可独立实施。
|
||||
103
docs/task/task-004-assistant-session-attach.md
Normal file
103
docs/task/task-004-assistant-session-attach.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# Task 004: assistant [sessionId] — 分阶段恢复
|
||||
|
||||
> 来源: [stub-recovery-design-1-4.md](../features/stub-recovery-design-1-4.md) 第 4 项
|
||||
> 优先级: P3
|
||||
> 工作量: Phase 4A 中等,4A-4D 全做完很大
|
||||
> 状态: Phase 4A DONE, 4B-4D TODO
|
||||
|
||||
## 目标
|
||||
|
||||
不一次性恢复整个 KAIROS 助手系统。先做"明确 sessionId 的 viewer attach 可用",再逐步补 discovery / chooser / install。
|
||||
|
||||
## 背景
|
||||
|
||||
- attach 主流程已存在 (`src/main.tsx:4708`)
|
||||
- 远端 viewer 所需基础模块已存在:
|
||||
- `src/remote/RemoteSessionManager.ts`
|
||||
- `src/hooks/useAssistantHistory.ts`
|
||||
- `src/assistant/sessionHistory.ts`
|
||||
- 真正 stub 的主要是:
|
||||
- `src/assistant/sessionDiscovery.ts`
|
||||
- `src/assistant/AssistantSessionChooser.tsx`
|
||||
- `src/commands/assistant/assistant.tsx:7`
|
||||
- `src/assistant/index.ts`
|
||||
|
||||
## 分阶段实现
|
||||
|
||||
### Phase 4A: MVP — 显式 sessionId attach
|
||||
|
||||
**修改文件:**
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `src/main.tsx` | 确保 attach 分支可用 |
|
||||
| `src/commands/assistant/index.ts` | 实现显式 sessionId 参数入口 |
|
||||
|
||||
**行为:**
|
||||
- `claude assistant <sessionId>` — 进入 remote viewer
|
||||
- `claude assistant` (无参数) — 返回明确提示: 当前版本需要显式 sessionId,discovery 尚未启用
|
||||
|
||||
**验证:**
|
||||
- [ ] `claude assistant <sessionId>` 能进入 remote viewer
|
||||
- [ ] 历史懒加载工作正常
|
||||
- [ ] 无参数模式给出明确提示
|
||||
|
||||
### Phase 4B: session discovery
|
||||
|
||||
**修改文件:**
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `src/assistant/sessionDiscovery.ts` | 恢复 `discoverAssistantSessions()` |
|
||||
|
||||
**行为:**
|
||||
- 数据来源优先复用现有 sessions / bridge / teleport API,不新增协议
|
||||
- `claude assistant` 无参数时能拿到候选 session 列表
|
||||
|
||||
**验证:**
|
||||
- [ ] 无参数调用能列出可用 sessions
|
||||
- [ ] 数据来源复用现有通道
|
||||
|
||||
### Phase 4C: session chooser
|
||||
|
||||
**修改文件:**
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `src/assistant/AssistantSessionChooser.ts` | 恢复交互式选择器 |
|
||||
|
||||
**行为:**
|
||||
- 多 session 时可交互选择
|
||||
|
||||
**验证:**
|
||||
- [ ] 多个 session 时弹出选择器
|
||||
- [ ] 选择后正确 attach
|
||||
|
||||
### Phase 4D: install wizard
|
||||
|
||||
**修改文件:**
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `src/commands/assistant/assistant.ts` | 恢复 install wizard 辅助函数 |
|
||||
|
||||
**行为:**
|
||||
- 没有 session 时如何引导用户
|
||||
|
||||
**验证:**
|
||||
- [ ] 无可用 session 时引导用户创建/连接
|
||||
|
||||
## 为什么拆分
|
||||
|
||||
- attach 渲染层与远端消息通道大部分已在
|
||||
- 真正缺的是"如何发现目标 session"和"如何交互选择"
|
||||
- 如果把 `src/assistant/index.ts` 的整套 KAIROS 正常模式也一起拉进来,范围会失控
|
||||
|
||||
## 风险
|
||||
|
||||
- 这是四项里范围最大的
|
||||
- 一旦把 KAIROS 正常模式整体拉入,会从"viewer attach"膨胀成"完整 assistant mode 恢复"
|
||||
|
||||
## 依赖
|
||||
|
||||
- Task 002 的 session registry 模式可复用
|
||||
196
docs/task/task-013-bg-engine-abstraction.md
Normal file
196
docs/task/task-013-bg-engine-abstraction.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# Task 013: BgEngine 跨平台后台引擎抽象
|
||||
|
||||
> 设计文档: [daemon-restructure-design.md](../features/daemon-restructure-design.md) § 四
|
||||
> 依赖: 无
|
||||
> 分支: `feat/integrate-5-branches`
|
||||
|
||||
## 目标
|
||||
|
||||
将 `src/cli/bg.ts` 中硬编码的 tmux 逻辑提取为引擎抽象层,实现 TmuxEngine + DetachedEngine,使后台会话功能在 Windows / macOS / Linux 上都能工作。
|
||||
|
||||
## 背景
|
||||
|
||||
当前 `bg.ts` 中 `handleBgFlag()` 和 `attachHandler()` 直接调用 tmux 命令。Windows 上 `--bg` 直接报错退出。需要一个引擎抽象层,根据平台和可用工具自动选择最佳方案。
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 新增
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `src/cli/bg/engine.ts` | BgEngine 接口 + BgStartOptions/BgStartResult 类型 |
|
||||
| `src/cli/bg/engines/tmux.ts` | TmuxEngine: 从 `bg.ts` 提取 tmux 相关逻辑 |
|
||||
| `src/cli/bg/engines/detached.ts` | DetachedEngine: spawn({ detached }) + logFile 重定向 |
|
||||
| `src/cli/bg/engines/index.ts` | selectEngine() 自动选择 + re-export |
|
||||
| `src/cli/bg/tail.ts` | 跨平台日志 tail: fs.watch + 轮询 fallback |
|
||||
|
||||
### 修改
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `src/cli/bg.ts` | `handleBgFlag()` 改为调用 `selectEngine().start()`;`attachHandler()` 改为调用 `engine.attach()` |
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 1. BgEngine 接口 (`src/cli/bg/engine.ts`)
|
||||
|
||||
```typescript
|
||||
export interface BgEngine {
|
||||
readonly name: string
|
||||
available(): Promise<boolean>
|
||||
start(opts: BgStartOptions): Promise<BgStartResult>
|
||||
attach(session: SessionEntry): Promise<void>
|
||||
}
|
||||
|
||||
export interface BgStartOptions {
|
||||
sessionName: string
|
||||
args: string[] // CLI args (去除 --bg)
|
||||
env: Record<string, string | undefined>
|
||||
logPath: string
|
||||
cwd: string
|
||||
}
|
||||
|
||||
export interface BgStartResult {
|
||||
pid: number
|
||||
sessionName: string
|
||||
logPath: string
|
||||
engineUsed: 'tmux' | 'detached'
|
||||
}
|
||||
```
|
||||
|
||||
### 2. TmuxEngine (`src/cli/bg/engines/tmux.ts`)
|
||||
|
||||
从 `bg.ts:handleBgFlag()` 和 `bg.ts:attachHandler()` 提取:
|
||||
- `available()`: `execFileNoThrow('tmux', ['-V'])` 返回 code === 0
|
||||
- `start()`: `tmux new-session -d -s <name> <cmd>`
|
||||
- `attach()`: `tmux attach-session -t <session.tmuxSessionName>`
|
||||
|
||||
### 3. DetachedEngine (`src/cli/bg/engines/detached.ts`)
|
||||
|
||||
```typescript
|
||||
export class DetachedEngine implements BgEngine {
|
||||
readonly name = 'detached'
|
||||
|
||||
async available(): Promise<boolean> {
|
||||
return true // 总是可用
|
||||
}
|
||||
|
||||
async start(opts: BgStartOptions): Promise<BgStartResult> {
|
||||
const logFd = openSync(opts.logPath, 'a')
|
||||
const child = spawn(process.execPath, [process.argv[1]!, ...opts.args], {
|
||||
detached: true,
|
||||
stdio: ['ignore', logFd, logFd],
|
||||
env: opts.env,
|
||||
cwd: opts.cwd,
|
||||
})
|
||||
child.unref()
|
||||
closeSync(logFd)
|
||||
|
||||
return {
|
||||
pid: child.pid!,
|
||||
sessionName: opts.sessionName,
|
||||
logPath: opts.logPath,
|
||||
engineUsed: 'detached',
|
||||
}
|
||||
}
|
||||
|
||||
async attach(session: SessionEntry): Promise<void> {
|
||||
// 委托给 tail.ts
|
||||
await tailLog(session.logPath!)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 日志 Tail (`src/cli/bg/tail.ts`)
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 跨平台实时日志输出。Ctrl+C 退出,不杀后台进程。
|
||||
*
|
||||
* 策略:
|
||||
* 1. 读取已有内容输出
|
||||
* 2. fs.watch() 监听文件变化 (主方案)
|
||||
* 3. 如果 fs.watch 不可靠 (某些 Windows 网络驱动器),fallback 到 500ms 轮询
|
||||
*/
|
||||
export async function tailLog(logPath: string): Promise<void>
|
||||
```
|
||||
|
||||
### 5. 引擎选择 (`src/cli/bg/engines/index.ts`)
|
||||
|
||||
```typescript
|
||||
export async function selectEngine(): Promise<BgEngine> {
|
||||
if (process.platform === 'win32') {
|
||||
return new DetachedEngine()
|
||||
}
|
||||
const tmux = new TmuxEngine()
|
||||
if (await tmux.available()) {
|
||||
return tmux
|
||||
}
|
||||
return new DetachedEngine()
|
||||
}
|
||||
```
|
||||
|
||||
### 6. bg.ts 重构
|
||||
|
||||
`handleBgFlag()` 改名为 `handleBgStart()`,内部逻辑:
|
||||
```typescript
|
||||
export async function handleBgStart(args: string[]): Promise<void> {
|
||||
const engine = await selectEngine()
|
||||
const sessionName = `claude-bg-${randomUUID().slice(0, 8)}`
|
||||
const logPath = join(getClaudeConfigHomeDir(), 'sessions', 'logs', `${sessionName}.log`)
|
||||
|
||||
const result = await engine.start({
|
||||
sessionName,
|
||||
args: filteredArgs,
|
||||
env: { ...process.env, CLAUDE_CODE_SESSION_KIND: 'bg', ... },
|
||||
logPath,
|
||||
cwd: process.cwd(),
|
||||
})
|
||||
|
||||
console.log(`Background session started: ${result.sessionName}`)
|
||||
console.log(` Engine: ${result.engineUsed}`)
|
||||
console.log(` Log: ${result.logPath}`)
|
||||
console.log(` Use \`claude daemon attach ${result.sessionName}\` to reconnect.`)
|
||||
}
|
||||
```
|
||||
|
||||
`attachHandler()` 根据 `session.engine` 字段选择引擎:
|
||||
```typescript
|
||||
export async function attachHandler(target: string | undefined): Promise<void> {
|
||||
// ... 找到 session
|
||||
if (session.engine === 'tmux' && session.tmuxSessionName) {
|
||||
const tmux = new TmuxEngine()
|
||||
await tmux.attach(session)
|
||||
} else {
|
||||
const detached = new DetachedEngine()
|
||||
await detached.attach(session)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## SessionEntry 扩展
|
||||
|
||||
`sessions/<PID>.json` 新增 `engine` 字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"pid": 12345,
|
||||
"engine": "detached",
|
||||
"logPath": "~/.claude/sessions/logs/claude-bg-a1b2c3d4.log",
|
||||
"sessionId": "...",
|
||||
"cwd": "..."
|
||||
}
|
||||
```
|
||||
|
||||
兼容旧格式: 如果 `engine` 字段缺失,检查 `tmuxSessionName` 存在则为 `tmux`,否则为 `detached`。
|
||||
|
||||
## 验证清单
|
||||
|
||||
- [ ] Windows: `claude daemon bg` 启动后台会话,无 tmux 依赖
|
||||
- [ ] Windows: `claude daemon attach <name>` 以 tail 模式附着,Ctrl+C 退出不杀进程
|
||||
- [ ] macOS/Linux (有 tmux): 行为与当前一致
|
||||
- [ ] macOS/Linux (无 tmux): 自动 fallback 到 detached 引擎
|
||||
- [ ] `claude daemon status` 正确显示 engine 类型
|
||||
- [ ] 旧格式 session JSON (无 engine 字段) 兼容
|
||||
- [ ] tsc --noEmit 零错误
|
||||
- [ ] bun test 通过
|
||||
275
docs/task/task-014-daemon-command-hierarchy.md
Normal file
275
docs/task/task-014-daemon-command-hierarchy.md
Normal file
@@ -0,0 +1,275 @@
|
||||
# Task 014: /daemon 命令层级化
|
||||
|
||||
> 设计文档: [daemon-restructure-design.md](../features/daemon-restructure-design.md) § 三.1
|
||||
> 依赖: Task 013 (BgEngine 抽象)
|
||||
> 分支: `feat/integrate-5-branches`
|
||||
|
||||
## 目标
|
||||
|
||||
将散落的 `daemon start/stop/status` + `ps/logs/attach/kill` + `--bg` 统一收归 `/daemon` 命名空间,实现 CLI + REPL 双注册。
|
||||
|
||||
## 背景
|
||||
|
||||
当前这些命令注册在两个互不关联的位置:
|
||||
- `cli.tsx:203-212`: `daemon [start|status|stop]` → `daemon/main.ts`
|
||||
- `cli.tsx:217-246`: `ps|logs|attach|kill|--bg` → `cli/bg.ts`
|
||||
|
||||
需要合并为统一的 `claude daemon <subcommand>` 入口,并新增 REPL `/daemon` 斜杠命令。
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 新增
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `src/commands/daemon/index.ts` | `/daemon` REPL 斜杠命令注册 (type: local-jsx) |
|
||||
| `src/commands/daemon/daemon.tsx` | `/daemon` 子命令路由 + status UI 组件 |
|
||||
|
||||
### 修改
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `src/entrypoints/cli.tsx` | 统一 daemon 快速路径: `daemon <sub>` 路由到对应 handler。旧命令 `ps/logs/attach/kill` 保留但输出 deprecation 警告后代理 |
|
||||
| `src/commands.ts` | 注册 `/daemon` 斜杠命令 (feature-gated: DAEMON \|\| BG_SESSIONS) |
|
||||
| `src/daemon/main.ts` | `daemonMain()` 扩展: 支持 `bg/attach/logs/kill/ps` 子命令 (委托给 bg.ts handlers) |
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 1. CLI 快速路径统一 (`cli.tsx`)
|
||||
|
||||
**改前** (两段独立路由):
|
||||
```typescript
|
||||
// 段 1: daemon
|
||||
if (feature('DAEMON') && args[0] === 'daemon') {
|
||||
await daemonMain(args.slice(1))
|
||||
}
|
||||
// 段 2: bg sessions
|
||||
if (feature('BG_SESSIONS') && ['ps','logs','attach','kill'].includes(args[0])) {
|
||||
// ...switch/case
|
||||
}
|
||||
```
|
||||
|
||||
**改后** (统一入口):
|
||||
```typescript
|
||||
// 统一 daemon 入口 — 合并 daemon supervisor + bg sessions
|
||||
if (
|
||||
(feature('DAEMON') || feature('BG_SESSIONS')) &&
|
||||
args[0] === 'daemon'
|
||||
) {
|
||||
profileCheckpoint('cli_daemon_path')
|
||||
const { enableConfigs } = await import('../utils/config.js')
|
||||
enableConfigs()
|
||||
const { initSinks } = await import('../utils/sinks.js')
|
||||
initSinks()
|
||||
const { daemonMain } = await import('../daemon/main.js')
|
||||
await daemonMain(args.slice(1))
|
||||
return
|
||||
}
|
||||
|
||||
// --bg 快捷方式 → daemon bg
|
||||
if (
|
||||
feature('BG_SESSIONS') &&
|
||||
(args.includes('--bg') || args.includes('--background'))
|
||||
) {
|
||||
profileCheckpoint('cli_daemon_path')
|
||||
const { enableConfigs } = await import('../utils/config.js')
|
||||
enableConfigs()
|
||||
const bg = await import('../cli/bg.js')
|
||||
await bg.handleBgStart(args.filter(a => a !== '--bg' && a !== '--background'))
|
||||
return
|
||||
}
|
||||
|
||||
// 向后兼容: ps/logs/attach/kill → daemon <sub> (deprecated)
|
||||
if (
|
||||
feature('BG_SESSIONS') &&
|
||||
['ps', 'logs', 'attach', 'kill'].includes(args[0] ?? '')
|
||||
) {
|
||||
const mapped = args[0] === 'ps' ? 'status' : args[0]
|
||||
console.error(`[deprecated] Use: claude daemon ${mapped} ${args.slice(1).join(' ')}`.trim())
|
||||
const { enableConfigs } = await import('../utils/config.js')
|
||||
enableConfigs()
|
||||
const { daemonMain } = await import('../daemon/main.js')
|
||||
await daemonMain([args[0]!, ...args.slice(1)])
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
### 2. daemonMain 扩展 (`daemon/main.ts`)
|
||||
|
||||
```typescript
|
||||
export async function daemonMain(args: string[]): Promise<void> {
|
||||
const subcommand = args[0] || 'status'
|
||||
|
||||
switch (subcommand) {
|
||||
// --- Supervisor 管理 ---
|
||||
case 'start':
|
||||
await runSupervisor(args.slice(1))
|
||||
break
|
||||
case 'stop':
|
||||
await handleDaemonStop()
|
||||
break
|
||||
|
||||
// --- 会话管理 (委托给 bg.ts) ---
|
||||
case 'status':
|
||||
case 'ps':
|
||||
await showUnifiedStatus() // 新: daemon 状态 + 会话列表
|
||||
break
|
||||
case 'bg':
|
||||
const bg = await import('../cli/bg.js')
|
||||
await bg.handleBgStart(args.slice(1))
|
||||
break
|
||||
case 'attach':
|
||||
const bg2 = await import('../cli/bg.js')
|
||||
await bg2.attachHandler(args[1])
|
||||
break
|
||||
case 'logs':
|
||||
const bg3 = await import('../cli/bg.js')
|
||||
await bg3.logsHandler(args[1])
|
||||
break
|
||||
case 'kill':
|
||||
const bg4 = await import('../cli/bg.js')
|
||||
await bg4.killHandler(args[1])
|
||||
break
|
||||
|
||||
case '--help': case '-h': case 'help':
|
||||
printHelp()
|
||||
break
|
||||
default:
|
||||
console.error(`Unknown daemon subcommand: ${subcommand}`)
|
||||
printHelp()
|
||||
process.exitCode = 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 统一状态面板 (`showUnifiedStatus`)
|
||||
|
||||
```typescript
|
||||
async function showUnifiedStatus(): Promise<void> {
|
||||
// 1. Daemon supervisor 状态
|
||||
const daemonResult = queryDaemonStatus()
|
||||
console.log('=== Daemon Supervisor ===')
|
||||
switch (daemonResult.status) {
|
||||
case 'running':
|
||||
console.log(` Status: running (PID: ${daemonResult.state!.pid})`)
|
||||
console.log(` Workers: ${daemonResult.state!.workerKinds.join(', ')}`)
|
||||
break
|
||||
case 'stopped':
|
||||
console.log(' Status: stopped')
|
||||
break
|
||||
case 'stale':
|
||||
console.log(' Status: stale (cleaned up)')
|
||||
break
|
||||
}
|
||||
|
||||
// 2. 后台会话列表
|
||||
console.log('\n=== Background Sessions ===')
|
||||
const bg = await import('../cli/bg.js')
|
||||
await bg.psHandler([])
|
||||
}
|
||||
```
|
||||
|
||||
### 4. REPL 斜杠命令注册
|
||||
|
||||
**`src/commands/daemon/index.ts`**:
|
||||
```typescript
|
||||
import type { Command } from '../../commands.js'
|
||||
import { feature } from 'bun:bundle'
|
||||
|
||||
const daemon = {
|
||||
type: 'local-jsx',
|
||||
name: 'daemon',
|
||||
description: 'Manage background sessions and daemon',
|
||||
argumentHint: '[status|start|stop|bg|attach|logs|kill]',
|
||||
isEnabled: () => {
|
||||
if (feature('DAEMON')) return true
|
||||
if (feature('BG_SESSIONS')) return true
|
||||
return false
|
||||
},
|
||||
load: () => import('./daemon.js'),
|
||||
} satisfies Command
|
||||
|
||||
export default daemon
|
||||
```
|
||||
|
||||
**`src/commands/daemon/daemon.tsx`**:
|
||||
```typescript
|
||||
export async function call(
|
||||
onDone: LocalJSXCommandOnDone,
|
||||
context: LocalJSXCommandContext,
|
||||
args: string,
|
||||
): Promise<React.ReactNode> {
|
||||
const parts = args.trim().split(/\s+/)
|
||||
const sub = parts[0] || 'status'
|
||||
|
||||
switch (sub) {
|
||||
case 'status':
|
||||
case 'ps':
|
||||
// 调用 showUnifiedStatus,捕获输出
|
||||
// 返回文本结果
|
||||
break
|
||||
case 'bg':
|
||||
// REPL 中启动后台会话
|
||||
break
|
||||
case 'start':
|
||||
case 'stop':
|
||||
case 'attach':
|
||||
case 'logs':
|
||||
case 'kill':
|
||||
// 委托给对应 handler
|
||||
break
|
||||
default:
|
||||
onDone(`Unknown: ${sub}. Use: status|start|stop|bg|attach|logs|kill`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**`src/commands.ts`** 添加:
|
||||
```typescript
|
||||
// 条件导入
|
||||
const daemonCmd =
|
||||
feature('DAEMON') || feature('BG_SESSIONS')
|
||||
? require('./commands/daemon/index.js').default
|
||||
: null
|
||||
|
||||
// COMMANDS 数组中添加
|
||||
...(daemonCmd ? [daemonCmd] : []),
|
||||
```
|
||||
|
||||
### 5. 更新 help 文本 (`daemon/main.ts`)
|
||||
|
||||
```
|
||||
Claude Code Daemon — background process management
|
||||
|
||||
USAGE
|
||||
claude daemon [subcommand]
|
||||
|
||||
SUBCOMMANDS
|
||||
status Show daemon and session status (default)
|
||||
start Start the daemon supervisor
|
||||
stop Stop the daemon
|
||||
bg Start a background session
|
||||
attach Attach to a background session
|
||||
logs Show session logs
|
||||
kill Kill a session
|
||||
help Show this help
|
||||
|
||||
REPL
|
||||
/daemon [subcommand] Same commands available in interactive mode
|
||||
```
|
||||
|
||||
## 验证清单
|
||||
|
||||
- [ ] `claude daemon` (无参数) 显示统一状态面板
|
||||
- [ ] `claude daemon status` 显示 supervisor + 会话列表
|
||||
- [ ] `claude daemon start/stop` 与当前行为一致
|
||||
- [ ] `claude daemon bg` 启动后台会话 (调用 BgEngine)
|
||||
- [ ] `claude daemon attach/logs/kill <target>` 功能正常
|
||||
- [ ] `claude ps` 输出 deprecation 警告 + 正常工作
|
||||
- [ ] `claude logs/attach/kill` 同上
|
||||
- [ ] `claude --bg` 快捷方式正常
|
||||
- [ ] REPL 中 `/daemon` 可用,tab 补全显示
|
||||
- [ ] REPL 中 `/daemon status` 显示状态信息
|
||||
- [ ] tsc --noEmit 零错误
|
||||
- [ ] bun test 通过
|
||||
177
docs/task/task-015-job-command-hierarchy.md
Normal file
177
docs/task/task-015-job-command-hierarchy.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# Task 015: /job 命令层级化
|
||||
|
||||
> 设计文档: [daemon-restructure-design.md](../features/daemon-restructure-design.md) § 三.2
|
||||
> 依赖: 无 (可与 Task 013 并行)
|
||||
> 分支: `feat/integrate-5-branches`
|
||||
|
||||
## 目标
|
||||
|
||||
将 `claude new/list/reply` 收归 `/job` 命名空间,实现 CLI + REPL 双注册。
|
||||
|
||||
## 背景
|
||||
|
||||
当前 `new`, `list`, `reply` 是顶级 CLI 命令 (`cli.tsx:250-261`),容易与其他命令冲突(特别是 `list` 这种通用词)。需要收归 `claude job <subcommand>` 并新增 REPL `/job` 入口。
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 新增
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `src/commands/job/index.ts` | `/job` REPL 斜杠命令注册 |
|
||||
| `src/commands/job/job.tsx` | `/job` 子命令路由 |
|
||||
|
||||
### 修改
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `src/entrypoints/cli.tsx` | 新增 `job` 快速路径 + 旧 `new/list/reply` deprecation 代理 |
|
||||
| `src/commands.ts` | 注册 `/job` 斜杠命令 |
|
||||
|
||||
### 不动
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `src/cli/handlers/templateJobs.ts` | 内部 handler 不变,只是被调用方式变了 |
|
||||
| `src/jobs/state.ts` | job 状态管理不变 |
|
||||
| `src/jobs/templates.ts` | 模板发现不变 |
|
||||
| `src/jobs/classifier.ts` | 任务分类器不变 |
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 1. CLI 快速路径 (`cli.tsx`)
|
||||
|
||||
**改后**:
|
||||
```typescript
|
||||
// 新: claude job <subcommand>
|
||||
if (
|
||||
feature('TEMPLATES') &&
|
||||
args[0] === 'job'
|
||||
) {
|
||||
profileCheckpoint('cli_templates_path')
|
||||
const { templatesMain } = await import('../cli/handlers/templateJobs.js')
|
||||
await templatesMain(args.slice(1))
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
// 向后兼容 (deprecated)
|
||||
if (
|
||||
feature('TEMPLATES') &&
|
||||
(args[0] === 'new' || args[0] === 'list' || args[0] === 'reply')
|
||||
) {
|
||||
console.error(`[deprecated] Use: claude job ${args[0]} ${args.slice(1).join(' ')}`.trim())
|
||||
profileCheckpoint('cli_templates_path')
|
||||
const { templatesMain } = await import('../cli/handlers/templateJobs.js')
|
||||
await templatesMain(args)
|
||||
process.exit(0)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. templateJobs.ts 新增 status 子命令
|
||||
|
||||
在现有 `switch` 中增加:
|
||||
```typescript
|
||||
case 'status':
|
||||
handleStatus(args.slice(1))
|
||||
break
|
||||
```
|
||||
|
||||
```typescript
|
||||
function handleStatus(args: string[]): void {
|
||||
const jobId = args[0]
|
||||
if (!jobId) {
|
||||
console.error('Usage: claude job status <job-id>')
|
||||
process.exitCode = 1
|
||||
return
|
||||
}
|
||||
const state = readJobState(jobId)
|
||||
if (!state) {
|
||||
console.error(`Job not found: ${jobId}`)
|
||||
process.exitCode = 1
|
||||
return
|
||||
}
|
||||
console.log(`Job: ${state.jobId}`)
|
||||
console.log(` Template: ${state.templateName}`)
|
||||
console.log(` Status: ${state.status}`)
|
||||
console.log(` Created: ${state.createdAt}`)
|
||||
console.log(` Updated: ${state.updatedAt}`)
|
||||
}
|
||||
```
|
||||
|
||||
### 3. REPL 斜杠命令
|
||||
|
||||
**`src/commands/job/index.ts`**:
|
||||
```typescript
|
||||
import type { Command } from '../../commands.js'
|
||||
import { feature } from 'bun:bundle'
|
||||
|
||||
const job = {
|
||||
type: 'local-jsx',
|
||||
name: 'job',
|
||||
description: 'Manage template jobs',
|
||||
argumentHint: '[list|new|reply|status]',
|
||||
isEnabled: () => {
|
||||
if (feature('TEMPLATES')) return true
|
||||
return false
|
||||
},
|
||||
load: () => import('./job.js'),
|
||||
} satisfies Command
|
||||
|
||||
export default job
|
||||
```
|
||||
|
||||
**`src/commands/job/job.tsx`**:
|
||||
```typescript
|
||||
export async function call(
|
||||
onDone: LocalJSXCommandOnDone,
|
||||
_context: LocalJSXCommandContext,
|
||||
args: string,
|
||||
): Promise<React.ReactNode> {
|
||||
const parts = args.trim().split(/\s+/)
|
||||
const sub = parts[0] || 'list'
|
||||
|
||||
// 委托给 templatesMain
|
||||
const { templatesMain } = await import('../../cli/handlers/templateJobs.js')
|
||||
|
||||
// 捕获 console.log 输出作为结果返回给 REPL
|
||||
const lines: string[] = []
|
||||
const origLog = console.log
|
||||
const origError = console.error
|
||||
console.log = (...a: unknown[]) => lines.push(a.join(' '))
|
||||
console.error = (...a: unknown[]) => lines.push(a.join(' '))
|
||||
|
||||
try {
|
||||
await templatesMain([sub, ...parts.slice(1)])
|
||||
} finally {
|
||||
console.log = origLog
|
||||
console.error = origError
|
||||
}
|
||||
|
||||
onDone(lines.join('\n') || 'Done.', { display: 'system' })
|
||||
return null
|
||||
}
|
||||
```
|
||||
|
||||
### 4. commands.ts 注册
|
||||
|
||||
```typescript
|
||||
const jobCmd = feature('TEMPLATES')
|
||||
? require('./commands/job/index.js').default
|
||||
: null
|
||||
|
||||
// COMMANDS 数组:
|
||||
...(jobCmd ? [jobCmd] : []),
|
||||
```
|
||||
|
||||
## 验证清单
|
||||
|
||||
- [ ] `claude job list` 列出模板
|
||||
- [ ] `claude job new <template>` 创建任务
|
||||
- [ ] `claude job reply <id> <text>` 回复任务
|
||||
- [ ] `claude job status <id>` 显示任务状态
|
||||
- [ ] `claude job` (无参数) 等同于 `claude job list`
|
||||
- [ ] `claude new/list/reply` 输出 deprecation 警告 + 正常工作
|
||||
- [ ] REPL 中 `/job` 可用
|
||||
- [ ] REPL 中 `/job list` 显示模板列表
|
||||
- [ ] tsc --noEmit 零错误
|
||||
- [ ] bun test 通过
|
||||
123
docs/task/task-016-backward-compat-tests.md
Normal file
123
docs/task/task-016-backward-compat-tests.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Task 016: 向后兼容 + 测试
|
||||
|
||||
> 设计文档: [daemon-restructure-design.md](../features/daemon-restructure-design.md) § 五
|
||||
> 依赖: Task 014, Task 015
|
||||
> 分支: `feat/integrate-5-branches`
|
||||
|
||||
## 目标
|
||||
|
||||
确保旧命令向后兼容 (deprecation 警告 + 正常代理),并为重构后的命令结构编写测试。
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 新增
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `src/daemon/__tests__/daemonMain.test.ts` | daemonMain 子命令路由测试 |
|
||||
| `src/cli/bg/__tests__/engine.test.ts` | BgEngine 选择逻辑测试 |
|
||||
| `src/cli/bg/__tests__/detached.test.ts` | DetachedEngine 启动/停止测试 |
|
||||
| `src/cli/bg/__tests__/tail.test.ts` | 日志 tail 功能测试 |
|
||||
|
||||
### 修改
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `src/entrypoints/cli.tsx` | 确认 deprecation 路径正确代理 |
|
||||
|
||||
## 实现方案
|
||||
|
||||
### 1. 向后兼容矩阵
|
||||
|
||||
| 旧命令 | 新命令 | 处理方式 |
|
||||
|--------|--------|---------|
|
||||
| `claude ps` | `claude daemon status` | stderr 输出 `[deprecated] Use: claude daemon status`,然后执行 |
|
||||
| `claude logs <x>` | `claude daemon logs <x>` | 同上 |
|
||||
| `claude attach <x>` | `claude daemon attach <x>` | 同上 |
|
||||
| `claude kill <x>` | `claude daemon kill <x>` | 同上 |
|
||||
| `claude --bg` | `claude daemon bg` | 保留为快捷方式,**不** deprecate (太常用) |
|
||||
| `claude new <t>` | `claude job new <t>` | stderr deprecation + 执行 |
|
||||
| `claude list` | `claude job list` | stderr deprecation + 执行 |
|
||||
| `claude reply <id>` | `claude job reply <id>` | stderr deprecation + 执行 |
|
||||
|
||||
**关键**: deprecation 输出到 stderr 而非 stdout,不影响脚本管道。
|
||||
|
||||
### 2. 测试计划
|
||||
|
||||
#### 2.1 daemonMain 路由测试
|
||||
|
||||
```typescript
|
||||
describe('daemonMain', () => {
|
||||
test('无参数默认 status', async () => { ... })
|
||||
test('start 调用 runSupervisor', async () => { ... })
|
||||
test('stop 调用 handleDaemonStop', async () => { ... })
|
||||
test('bg 委托给 bg.handleBgStart', async () => { ... })
|
||||
test('attach 委托给 bg.attachHandler', async () => { ... })
|
||||
test('logs 委托给 bg.logsHandler', async () => { ... })
|
||||
test('kill 委托给 bg.killHandler', async () => { ... })
|
||||
test('未知子命令设置 exitCode=1', async () => { ... })
|
||||
})
|
||||
```
|
||||
|
||||
#### 2.2 引擎选择测试
|
||||
|
||||
```typescript
|
||||
describe('selectEngine', () => {
|
||||
test('win32 返回 DetachedEngine', async () => { ... })
|
||||
test('darwin + tmux 可用返回 TmuxEngine', async () => { ... })
|
||||
test('darwin + tmux 不可用返回 DetachedEngine', async () => { ... })
|
||||
test('linux + tmux 可用返回 TmuxEngine', async () => { ... })
|
||||
})
|
||||
```
|
||||
|
||||
#### 2.3 DetachedEngine 测试
|
||||
|
||||
```typescript
|
||||
describe('DetachedEngine', () => {
|
||||
test('available 始终返回 true', async () => { ... })
|
||||
test('start 创建 detached 子进程并写入日志', async () => { ... })
|
||||
test('start 返回的 PID 文件存在', async () => { ... })
|
||||
})
|
||||
```
|
||||
|
||||
#### 2.4 Tail 测试
|
||||
|
||||
```typescript
|
||||
describe('tailLog', () => {
|
||||
test('输出已有日志内容', async () => { ... })
|
||||
test('追加内容时实时输出', async () => { ... })
|
||||
test('SIGINT 退出 tail', async () => { ... })
|
||||
})
|
||||
```
|
||||
|
||||
### 3. 集成验证脚本
|
||||
|
||||
可选: 在 `scripts/` 下添加一个手动验证脚本:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/verify-daemon-restructure.sh
|
||||
echo "=== 1. claude daemon status ==="
|
||||
bun run dev -- daemon status
|
||||
|
||||
echo "=== 2. claude daemon bg (should start) ==="
|
||||
bun run dev -- daemon bg --help
|
||||
|
||||
echo "=== 3. claude ps (deprecated) ==="
|
||||
bun run dev -- ps 2>&1 | head -1
|
||||
|
||||
echo "=== 4. claude job list ==="
|
||||
bun run dev -- job list
|
||||
|
||||
echo "=== 5. claude list (deprecated) ==="
|
||||
bun run dev -- list 2>&1 | head -1
|
||||
```
|
||||
|
||||
## 验证清单
|
||||
|
||||
- [ ] 旧命令全部正常工作 (仅多一行 stderr 警告)
|
||||
- [ ] `--bg` 保持无警告
|
||||
- [ ] 所有新增测试通过
|
||||
- [ ] 现有 2695 个测试无回归
|
||||
- [ ] tsc --noEmit 零错误
|
||||
- [ ] 手动在 Windows + macOS/Linux 上验证关键路径
|
||||
88
docs/test-plans/openclaw-autonomy-baseline.md
Normal file
88
docs/test-plans/openclaw-autonomy-baseline.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# OpenClaw Autonomy Baseline Test Spec
|
||||
|
||||
## Purpose
|
||||
|
||||
This test spec locks the current behavior of the existing trigger and context layers before any formal autonomy-subsystem implementation begins.
|
||||
|
||||
At this stage, production code is read-only. Only test files, fixtures, and planning documents may change.
|
||||
|
||||
## Goal
|
||||
|
||||
Establish a stable baseline around the parts of `Claude-code-bast` that later autonomy work is most likely to touch:
|
||||
|
||||
- proactive state handling
|
||||
- cron task storage semantics
|
||||
- cron scheduler helper semantics
|
||||
- user-context cache and `CLAUDE.md` injection behavior
|
||||
|
||||
## Out of Scope for This Baseline Round
|
||||
|
||||
- New authority behavior (`AGENTS.md` / `HEARTBEAT.md`)
|
||||
- New detached-run ledger behavior
|
||||
- New flow behavior
|
||||
- UI redesign
|
||||
|
||||
## Files Under Baseline Protection
|
||||
|
||||
- `src/proactive/index.ts`
|
||||
- `src/utils/cronTasks.ts`
|
||||
- `src/utils/cronScheduler.ts`
|
||||
- `src/context.ts`
|
||||
|
||||
## Test Files Added In This Round
|
||||
|
||||
- `src/proactive/__tests__/state.baseline.test.ts`
|
||||
- `src/commands/__tests__/proactive.baseline.test.ts`
|
||||
- `src/utils/__tests__/cronTasks.baseline.test.ts`
|
||||
- `src/utils/__tests__/cronScheduler.baseline.test.ts`
|
||||
- `src/__tests__/context.baseline.test.ts`
|
||||
|
||||
## Baseline Assertions
|
||||
|
||||
### Proactive state
|
||||
|
||||
1. Activating proactive mode sets active state and activation source.
|
||||
2. Pausing proactive mode suppresses `shouldTick()` and clears `nextTickAt`.
|
||||
3. Blocking context suppresses `shouldTick()` and clears `nextTickAt`.
|
||||
4. Subscribers are notified on state transitions.
|
||||
5. The `/proactive` command enables proactive mode and emits the expected hidden reminder.
|
||||
6. The `/proactive` command disables proactive mode on the second invocation.
|
||||
|
||||
### Cron task storage
|
||||
|
||||
1. Session-only cron tasks remain in memory only.
|
||||
2. Durable cron tasks are persisted to `.claude/scheduled_tasks.json`.
|
||||
3. Daemon-style `dir`-scoped reads exclude session-only cron tasks.
|
||||
4. `removeCronTasks()` without `dir` can remove session-only tasks.
|
||||
5. `removeCronTasks()` with `dir` does not mutate session-only task storage.
|
||||
|
||||
### Cron scheduler helpers
|
||||
|
||||
1. `isRecurringTaskAged()` preserves current aging semantics.
|
||||
2. `buildMissedTaskNotification()` preserves the current AskUserQuestion safety wording.
|
||||
3. `buildMissedTaskNotification()` preserves code-fence hardening for prompt bodies that contain backticks.
|
||||
|
||||
### User context caching
|
||||
|
||||
1. `getUserContext()` includes `currentDate`.
|
||||
2. `getUserContext()` includes mocked `claudeMd` content when memory loading is enabled.
|
||||
3. `CLAUDE_CODE_DISABLE_CLAUDE_MDS` suppresses `claudeMd`.
|
||||
4. `setSystemPromptInjection()` clears the memoized user-context cache.
|
||||
5. `getSystemContext()` reflects the injection after cache invalidation.
|
||||
|
||||
## Remaining Baseline Gaps
|
||||
|
||||
The following areas are intentionally deferred because they require higher-cost harnessing and should still avoid production-code changes:
|
||||
|
||||
1. `useScheduledTasks.ts` hook-level runtime behavior
|
||||
2. `src/cli/print.ts` full headless scheduler loop behavior
|
||||
3. `useProactive.ts` hook timer behavior
|
||||
4. end-to-end queue interaction between proactive ticks and `SleepTool`
|
||||
|
||||
## Acceptance
|
||||
|
||||
This baseline round is complete when:
|
||||
|
||||
1. The four new test files pass.
|
||||
2. No production source files are modified.
|
||||
3. The tests are stable enough to serve as a pre-implementation guardrail.
|
||||
@@ -22,14 +22,14 @@ Read 的 `maxResultSizeChars` 是 `Infinity`,但这并不意味着无限制输
|
||||
|
||||
## FileRead:多模态文件读取引擎
|
||||
|
||||
源码路径:`src/tools/FileReadTool/FileReadTool.ts`
|
||||
源码路径:`packages/builtin-tools/src/tools/FileReadTool/FileReadTool.ts`
|
||||
|
||||
### 读取去重机制
|
||||
|
||||
Read 工具有一个常被忽视但至关重要的**去重层**。当 AI 重复读取同一个文件的同一范围时,系统不会浪费 token 发送两份完整内容:
|
||||
|
||||
```typescript
|
||||
// FileReadTool.ts:530-573 — 去重逻辑
|
||||
// FileReadTool.ts — 去重逻辑
|
||||
const existingState = readFileState.get(fullFilePath)
|
||||
if (existingState && !existingState.isPartialView && existingState.offset !== undefined) {
|
||||
const rangeMatch = existingState.offset === offset && existingState.limit === limit
|
||||
@@ -83,7 +83,7 @@ Read 工具在 `validateInput()` 中设置了多层安全门:
|
||||
当文件不存在时,Read 不会只报一个 "file not found":
|
||||
|
||||
```typescript
|
||||
// FileReadTool.ts:639-647
|
||||
// FileReadTool.ts
|
||||
const similarFilename = findSimilarFile(fullFilePath) // 相似扩展名
|
||||
const cwdSuggestion = await suggestPathUnderCwd(fullFilePath) // cwd 相对路径建议
|
||||
// macOS 截图特殊处理:薄空格(U+202F) vs 普通空格
|
||||
@@ -94,7 +94,7 @@ const altPath = getAlternateScreenshotPath(fullFilePath)
|
||||
|
||||
## FileEdit:精确字符串替换引擎
|
||||
|
||||
源码路径:`src/tools/FileEditTool/FileEditTool.ts` + `utils.ts`
|
||||
源码路径:`packages/builtin-tools/src/tools/FileEditTool/FileEditTool.ts` + `utils.ts`
|
||||
|
||||
### 引号标准化:AI 无法输出的字符怎么办
|
||||
|
||||
@@ -138,7 +138,7 @@ Edit 工具在 `validateInput()` 中检查两个条件:
|
||||
2. **文件未被外部修改**(`mtime` 未变,或全量读取时内容完全一致)
|
||||
|
||||
```typescript
|
||||
// FileEditTool.ts:290-311 — Windows 特殊处理
|
||||
// FileEditTool.ts — Windows 特殊处理
|
||||
const isFullRead = readTimestamp.offset === undefined && readTimestamp.limit === undefined
|
||||
if (isFullRead && fileContent === readTimestamp.content) {
|
||||
// 内容不变,安全继续(Windows 云同步/杀毒可能改 mtime)
|
||||
@@ -157,7 +157,7 @@ const MAX_EDIT_FILE_SIZE = 1024 * 1024 * 1024 // 1 GiB
|
||||
|
||||
## FileWrite:全量写入与创建
|
||||
|
||||
源码路径:`src/tools/FileWriteTool/FileWriteTool.ts`
|
||||
源码路径:`packages/builtin-tools/src/tools/FileWriteTool/FileWriteTool.ts`
|
||||
|
||||
Write 工具与 Edit 共享大部分基础设施(权限检查、mtime 校验、fileHistory 备份),但有两个关键差异:
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ Glob 默认把**最近修改的文件排在前面**。这不是默认的文件
|
||||
实际效果:AI 优先看到"活"的代码,而不是沉寂的历史文件
|
||||
```
|
||||
|
||||
在 `src/tools/GlobTool/` 中,ripgrep 的输出在返回给 AI 前按 mtime 排序。
|
||||
在 `packages/builtin-tools/src/tools/GlobTool/` 中,ripgrep 的输出在返回给 AI 前按 mtime 排序。
|
||||
|
||||
### ripgrep 的错误处理
|
||||
|
||||
@@ -92,7 +92,7 @@ ripgrep 执行有专门的错误恢复链(`src/utils/ripgrep.ts`):
|
||||
|
||||
## ToolSearch:在 50+ 工具中发现目标
|
||||
|
||||
当可用工具超过 50 个时(含 MCP 提供的外部工具),AI 可能不知道该用哪个。**ToolSearch**(`src/tools/ToolSearchTool/`)提供了工具发现机制。
|
||||
当可用工具超过 50 个时(含 MCP 提供的外部工具),AI 可能不知道该用哪个。**ToolSearch**(`packages/builtin-tools/src/tools/ToolSearchTool/`)提供了工具发现机制。
|
||||
|
||||
### 搜索算法
|
||||
|
||||
@@ -139,21 +139,22 @@ function getDeferredToolsCacheKey(deferredTools: Tools): string {
|
||||
|
||||
AI 的信息获取不局限于本地代码:
|
||||
|
||||
- **WebSearch**(`src/tools/WebSearchTool/`):调用 Anthropic API 的 `web_search_20250305` server tool 搜索互联网
|
||||
- **WebFetch**(`src/tools/WebFetchTool/`):抓取特定 URL 内容,转换为 Markdown 供 AI 阅读
|
||||
- **WebSearch**(`packages/builtin-tools/src/tools/WebSearchTool/`):调用 Anthropic API 的 `web_search_20250305` server tool 搜索互联网
|
||||
- **WebFetch**(`packages/builtin-tools/src/tools/WebFetchTool/`):抓取特定 URL 内容,转换为 Markdown 供 AI 阅读
|
||||
|
||||
这让 AI 可以查阅文档、搜索 Stack Overflow、阅读 GitHub issue——和人类开发者的工作方式一致。
|
||||
|
||||
### WebSearch 实现机制
|
||||
|
||||
WebSearch 通过适配器模式支持两种搜索后端,由 `src/tools/WebSearchTool/adapters/` 中的工厂函数 `createAdapter()` 选择:
|
||||
WebSearch 通过适配器模式支持三种搜索后端,由 `packages/builtin-tools/src/tools/WebSearchTool/adapters/` 中的工厂函数 `createAdapter()` 选择:
|
||||
|
||||
```
|
||||
适配器架构:
|
||||
WebSearchTool.call()
|
||||
→ createAdapter() 选择后端
|
||||
├─ ApiSearchAdapter — Anthropic API 服务端搜索(需官方 API 密钥)
|
||||
└─ BingSearchAdapter — 直接抓取 Bing 搜索页面解析(无需 API 密钥)
|
||||
├─ BingSearchAdapter — 直接抓取 Bing 搜索页面解析(无需 API 密钥)
|
||||
└─ BraveSearchAdapter — 调用 Brave LLM Context API 解析(需 Brave API 密钥)
|
||||
→ adapter.search(query, options)
|
||||
→ 转换为统一 SearchResult[] 格式返回
|
||||
```
|
||||
@@ -166,8 +167,9 @@ WebSearch 通过适配器模式支持两种搜索后端,由 `src/tools/WebSear
|
||||
|--------|------|--------|
|
||||
| 1 | 环境变量 `WEB_SEARCH_ADAPTER=api` | `ApiSearchAdapter` |
|
||||
| 2 | 环境变量 `WEB_SEARCH_ADAPTER=bing` | `BingSearchAdapter` |
|
||||
| 3 | API Base URL 指向 Anthropic 官方 | `ApiSearchAdapter` |
|
||||
| 4 | 第三方代理 / 非官方端点 | `BingSearchAdapter` |
|
||||
| 3 | 环境变量 `WEB_SEARCH_ADAPTER=brave` | `BraveSearchAdapter` |
|
||||
| 4 | API Base URL 指向 Anthropic 官方 | `ApiSearchAdapter` |
|
||||
| 5 | 第三方代理 / 非官方端点 | `BingSearchAdapter` |
|
||||
|
||||
适配器是无状态的,同一会话内缓存复用。
|
||||
|
||||
@@ -227,7 +229,7 @@ WebSearch 通过适配器模式支持两种搜索后端,由 `src/tools/WebSear
|
||||
|
||||
### WebSearchTool 统一接口
|
||||
|
||||
`WebSearchTool`(`src/tools/WebSearchTool/WebSearchTool.ts`)是面向主循环的工具定义,所有 provider 均可使用(`isEnabled()` 始终返回 true)。它将适配器返回的 `SearchResult[]` 转换为内部 `Output` 格式,`mapToolResultToToolResultBlockParam` 将搜索结果格式化为带 markdown 超链接的文本,并附加 "REMINDER" 要求主模型在回复中包含 Sources。
|
||||
`WebSearchTool`(`packages/builtin-tools/src/tools/WebSearchTool/WebSearchTool.ts`)是面向主循环的工具定义,所有 provider 均可使用(`isEnabled()` 始终返回 true)。它将适配器返回的 `SearchResult[]` 转换为内部 `Output` 格式,`mapToolResultToToolResultBlockParam` 将搜索结果格式化为带 markdown 超链接的文本,并附加 "REMINDER" 要求主模型在回复中包含 Sources。
|
||||
|
||||
### WebFetch 实现机制
|
||||
|
||||
@@ -262,7 +264,7 @@ WebFetch 是一个完整的 HTTP 客户端 + 内容处理管线:
|
||||
| **URL 验证** | `validateURL()` | 长度、协议、用户名密码、公网域名检查 |
|
||||
| **egress 检测** | `X-Proxy-Error: blocked-by-allowlist` | 检测企业代理拦截 |
|
||||
|
||||
预批准域名(`src/tools/WebFetchTool/preapproved.ts`):
|
||||
预批准域名(`packages/builtin-tools/src/tools/WebFetchTool/preapproved.ts`):
|
||||
|
||||
用户无需手动授权即可抓取的域名列表,包含 ~90 个主流技术文档站点(MDN、Python docs、React docs、AWS docs 等)。列表分为 hostname-only 和 path-prefix 两类,查找复杂度 O(1)。
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ spawn(wrapped_command) ← 实际进程创建
|
||||
|
||||
## 只读命令的判定:为什么 Read 免审批而 Bash 不一定
|
||||
|
||||
BashTool 的 `isReadOnly()` 方法(`BashTool.tsx:437`)决定一条命令是否被视为"只读":
|
||||
BashTool 的 `isReadOnly()` 方法(`packages/builtin-tools/src/tools/BashTool/BashTool.tsx:655`)决定一条命令是否被视为"只读":
|
||||
|
||||
```typescript
|
||||
isReadOnly(input) {
|
||||
@@ -41,7 +41,7 @@ isReadOnly(input) {
|
||||
}
|
||||
```
|
||||
|
||||
判定逻辑基于 4 个命令集合(`BashTool.tsx:60-78`):
|
||||
判定逻辑基于 4 个命令集合(`BashTool.tsx:120-166`):
|
||||
|
||||
| 集合 | 命令 | 性质 |
|
||||
|------|------|------|
|
||||
@@ -53,7 +53,7 @@ isReadOnly(input) {
|
||||
对于复合命令(`ls dir && echo "---" && ls dir2`),系统拆分后逐段检查——**所有非中性段都必须属于上述集合**,整条命令才被视为只读。
|
||||
|
||||
```typescript
|
||||
// BashTool.tsx:95 — 简化的判定逻辑
|
||||
// BashTool.tsx — 简化的判定逻辑
|
||||
for (const part of partsWithOperators) {
|
||||
if (BASH_SEMANTIC_NEUTRAL_COMMANDS.has(baseCommand)) continue // 跳过中性段
|
||||
if (!isPartSearch && !isPartRead && !isPartList) {
|
||||
@@ -64,7 +64,7 @@ for (const part of partsWithOperators) {
|
||||
|
||||
## AST 安全解析:tree-sitter bash 解析
|
||||
|
||||
`preparePermissionMatcher()`(`BashTool.tsx:445`)在权限检查前用 `parseForSecurity()` 解析命令结构:
|
||||
`preparePermissionMatcher()`(`BashTool.tsx:663`)在权限检查前用 `parseForSecurity()` 解析命令结构:
|
||||
|
||||
```typescript
|
||||
async preparePermissionMatcher({ command }) {
|
||||
@@ -92,14 +92,14 @@ getDefaultTimeoutMs()
|
||||
└── 最大上限:600,000ms(10 分钟,用户显式设置时)
|
||||
```
|
||||
|
||||
超时后系统不会直接杀进程——`ShellCommand`(`src/utils/ShellCommand.ts:129`)通过 `onTimeout` 回调通知调用方,由调用方决定是终止还是后台化。
|
||||
超时后系统不会直接杀进程——`ShellCommand`(`src/utils/ShellCommand.ts:144`)通过 `onTimeout` 回调通知调用方,由调用方决定是终止还是后台化。
|
||||
|
||||
## 自动后台化
|
||||
|
||||
长时间运行的命令可以自动转为后台任务,不阻塞 AI 的 agentic loop:
|
||||
|
||||
```typescript
|
||||
// BashTool.tsx:880
|
||||
// BashTool.tsx:1158
|
||||
const shouldAutoBackground = !isBackgroundTasksDisabled
|
||||
&& isAutobackgroundingAllowed(command)
|
||||
```
|
||||
@@ -148,7 +148,7 @@ Claude Code 为文件读写、代码搜索等操作提供了专用工具(Read
|
||||
| **并发安全** | `isConcurrencySafe()` 返回 `true` → 可并行执行 | Bash 命令可能有副作用,串行执行 |
|
||||
| **安全审计** | 工具名精确匹配权限规则 | 需 AST 解析命令结构后匹配 |
|
||||
|
||||
`isConcurrencySafe()`(`BashTool.tsx:434`)是一个常被忽视但重要的设计——只有只读命令可以在 agentic loop 中并行执行,有副作用的命令必须串行,防止竞态条件。
|
||||
`isConcurrencySafe()`(`BashTool.tsx:652`)是一个常被忽视但重要的设计——只有只读命令可以在 agentic loop 中并行执行,有副作用的命令必须串行,防止竞态条件。
|
||||
|
||||
## 进度反馈的流式设计
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Claude Code 的任务管理并非单一系统,而是两个并存、按运行
|
||||
TodoWrite 本质是一个**全量替换**操作——每次调用传入完整的 `todos[]` 数组,完全覆盖之前的状态:
|
||||
|
||||
```typescript
|
||||
// src/tools/TodoWriteTool/TodoWriteTool.ts — call() 核心逻辑
|
||||
// packages/builtin-tools/src/tools/TodoWriteTool/TodoWriteTool.ts — call() 核心逻辑
|
||||
async call({ todos }, context) {
|
||||
const todoKey = context.agentId ?? getSessionId()
|
||||
const oldTodos = appState.todos[todoKey] ?? []
|
||||
|
||||
@@ -16,7 +16,7 @@ keywords: ["工具系统", "Tool 抽象", "AI 工具", "function calling", "buil
|
||||
|
||||
## Tool 类型:35 个字段的统一接口
|
||||
|
||||
所有工具都实现 `src/Tool.ts:362` 的 `Tool<Input, Output, Progress>` 类型。这不是一个 class,而是一个包含 35+ 字段的**结构化类型**(structural typing),任何满足该接口的对象就是一个工具:
|
||||
所有工具都实现 `src/Tool.ts:368` 的 `Tool<Input, Output, Progress>` 类型。这不是一个 class,而是一个包含 35+ 字段的**结构化类型**(structural typing),任何满足该接口的对象就是一个工具:
|
||||
|
||||
### 核心四要素
|
||||
|
||||
@@ -69,7 +69,7 @@ keywords: ["工具系统", "Tool 抽象", "AI 工具", "function calling", "buil
|
||||
|
||||
## 工具注册:`getTools()` 的分层组装
|
||||
|
||||
`src/tools.ts` 的 `getAllBaseTools()`(第 191 行)是工具注册的核心:
|
||||
`src/tools.ts` 的 `getAllBaseTools()`(第 195 行)是工具注册的核心:
|
||||
|
||||
```
|
||||
固定工具(始终可用):
|
||||
@@ -96,7 +96,7 @@ Ant-only 工具:
|
||||
← process.env.USER_TYPE === 'ant' ? [REPLTool, ConfigTool, TungstenTool]
|
||||
```
|
||||
|
||||
`getTools()`(第 269 行)在 `getAllBaseTools()` 基础上应用权限过滤:
|
||||
`getTools()`(第 274 行)在 `getAllBaseTools()` 基础上应用权限过滤:
|
||||
|
||||
```typescript
|
||||
export const getTools = (permissionContext): Tools => {
|
||||
@@ -110,7 +110,7 @@ export const getTools = (permissionContext): Tools => {
|
||||
|
||||
## `buildTool()` 工厂函数
|
||||
|
||||
大多数工具通过 `buildTool()` 创建(`src/Tool.ts:721`),它是一个类型安全的构造器:
|
||||
大多数工具通过 `buildTool()` 创建(`src/Tool.ts:789`),它是一个类型安全的构造器:
|
||||
|
||||
```typescript
|
||||
export const BashTool: Tool<...> = buildTool({
|
||||
|
||||
19
mint.json
19
mint.json
@@ -86,6 +86,7 @@
|
||||
"group": "可扩展性",
|
||||
"pages": [
|
||||
"docs/extensibility/mcp-protocol",
|
||||
"docs/extensibility/mcp-configuration",
|
||||
"docs/extensibility/hooks",
|
||||
"docs/extensibility/skills",
|
||||
"docs/extensibility/custom-agents"
|
||||
@@ -134,9 +135,11 @@
|
||||
"group": "运行模式",
|
||||
"pages": [
|
||||
"docs/features/kairos",
|
||||
"docs/features/channels",
|
||||
"docs/features/voice-mode",
|
||||
"docs/features/bridge-mode",
|
||||
"docs/features/remote-control-self-hosting",
|
||||
"docs/features/acp-link",
|
||||
"docs/features/proactive",
|
||||
"docs/features/ultraplan"
|
||||
]
|
||||
@@ -177,21 +180,7 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"excludes": [
|
||||
"docs/test-plans/**",
|
||||
"docs/testing-spec.md",
|
||||
"docs/REVISION-PLAN.md",
|
||||
"docs/feature-exploration-plan.md",
|
||||
"docs/ultraplan-implementation.md",
|
||||
"docs/features/feature-flags-audit-complete.md",
|
||||
"docs/features/feature-flags-codex-review.md",
|
||||
"docs/features/growthbook-enablement-plan.md",
|
||||
"docs/features/computer-use-architecture-v2.md",
|
||||
"docs/features/computer-use-mcp-test-report.md",
|
||||
"docs/features/computer-use-tools-reference.md",
|
||||
"docs/features/computer-use-windows-enhancement.md",
|
||||
"docs/features/lan-pipes-implementation.md"
|
||||
],
|
||||
"excludes": [],
|
||||
"footerSocials": {
|
||||
"github": "https://github.com/anthropics/claude-code"
|
||||
}
|
||||
|
||||
96
package.json
96
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-code-best",
|
||||
"version": "1.2.1",
|
||||
"version": "1.4.4",
|
||||
"description": "Reverse-engineered Anthropic Claude Code CLI — interactive AI coding assistant in the terminal",
|
||||
"type": "module",
|
||||
"author": "claude-code-best <claude-code-best@proton.me>",
|
||||
@@ -25,20 +25,26 @@
|
||||
"bun": ">=1.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"ccb": "dist/cli.js",
|
||||
"claude-code-best": "dist/cli.js"
|
||||
"ccb": "dist/cli-node.js",
|
||||
"ccb-bun": "dist/cli-bun.js",
|
||||
"claude-code-best": "dist/cli-node.js"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"packages/@ant/*"
|
||||
"packages/@ant/*",
|
||||
"packages/@anthropic-ai/*"
|
||||
],
|
||||
"files": [
|
||||
"dist",
|
||||
"scripts/download-ripgrep.ts",
|
||||
"scripts/postinstall.cjs"
|
||||
"scripts/postinstall.cjs",
|
||||
"scripts/run-parallel.mjs",
|
||||
"scripts/setup-chrome-mcp.mjs"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "bun run build.ts",
|
||||
"build:vite": "vite build && bun run scripts/post-build.ts",
|
||||
"build:vite:only": "vite build",
|
||||
"build:bun": "bun run build.ts",
|
||||
"dev": "bun run scripts/dev.ts",
|
||||
"dev:inspect": "bun run scripts/dev-debug.ts",
|
||||
"prepublishOnly": "bun run build",
|
||||
@@ -49,44 +55,50 @@
|
||||
"test": "bun test",
|
||||
"check:unused": "knip-bun",
|
||||
"health": "bun run scripts/health-check.ts",
|
||||
"postinstall": "node scripts/postinstall.cjs && node scripts/setup-chrome-mcp.mjs",
|
||||
"postinstall": "node scripts/run-parallel.mjs scripts/postinstall.cjs scripts/setup-chrome-mcp.mjs",
|
||||
"docs:dev": "npx mintlify dev",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"rcs": "bun run scripts/rcs.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"mcp-chrome-bridge": "^1.0.31"
|
||||
"@agentclientprotocol/sdk": "^0.19.0",
|
||||
"@claude-code-best/mcp-chrome-bridge": "^2.0.8",
|
||||
"ws": "^8.20.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/he": "^1.2.3",
|
||||
"@langfuse/otel": "^5.1.0",
|
||||
"@langfuse/tracing": "^5.1.0",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@alcalzone/ansi-tokenize": "^0.3.0",
|
||||
"@ant/model-provider": "workspace:*",
|
||||
"@ant/claude-for-chrome-mcp": "workspace:*",
|
||||
"@ant/computer-use-input": "workspace:*",
|
||||
"@ant/computer-use-mcp": "workspace:*",
|
||||
"@ant/computer-use-swift": "workspace:*",
|
||||
"@anthropic-ai/bedrock-sdk": "^0.26.4",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.87",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.114",
|
||||
"@anthropic-ai/foundry-sdk": "^0.2.3",
|
||||
"@anthropic-ai/mcpb": "^2.1.2",
|
||||
"@anthropic-ai/sandbox-runtime": "^0.0.44",
|
||||
"@anthropic-ai/sdk": "^0.80.0",
|
||||
"@anthropic-ai/vertex-sdk": "^0.14.4",
|
||||
"@anthropic/ink": "workspace:*",
|
||||
"@aws-sdk/client-bedrock": "^3.1020.0",
|
||||
"@aws-sdk/client-bedrock-runtime": "^3.1020.0",
|
||||
"@aws-sdk/client-sts": "^3.1020.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.28",
|
||||
"@aws-sdk/credential-providers": "^3.1020.0",
|
||||
"@aws-sdk/client-bedrock": "^3.1032.0",
|
||||
"@aws-sdk/client-bedrock-runtime": "^3.1032.0",
|
||||
"@aws-sdk/client-sts": "^3.1032.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.972.32",
|
||||
"@aws-sdk/credential-providers": "^3.1032.0",
|
||||
"@azure/identity": "^4.13.1",
|
||||
"@biomejs/biome": "^2.4.10",
|
||||
"@biomejs/biome": "^2.4.12",
|
||||
"@claude-code-best/agent-tools": "workspace:*",
|
||||
"@claude-code-best/builtin-tools": "workspace:*",
|
||||
"@claude-code-best/mcp-client": "workspace:*",
|
||||
"@claude-code-best/weixin": "workspace:*",
|
||||
"@commander-js/extra-typings": "^14.0.0",
|
||||
"@growthbook/growthbook": "^1.6.5",
|
||||
"@langfuse/otel": "^5.1.0",
|
||||
"@langfuse/tracing": "^5.1.0",
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"@opentelemetry/api": "^1.9.1",
|
||||
"@opentelemetry/api-logs": "^0.214.0",
|
||||
"@opentelemetry/core": "^2.6.1",
|
||||
"@opentelemetry/core": "^2.7.0",
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": "^0.214.0",
|
||||
"@opentelemetry/exporter-logs-otlp-http": "^0.214.0",
|
||||
"@opentelemetry/exporter-logs-otlp-proto": "^0.214.0",
|
||||
@@ -97,16 +109,19 @@
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": "^0.214.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.214.0",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.214.0",
|
||||
"@opentelemetry/resources": "^2.6.1",
|
||||
"@opentelemetry/resources": "^2.7.0",
|
||||
"@opentelemetry/sdk-logs": "^0.214.0",
|
||||
"@opentelemetry/sdk-metrics": "^2.6.1",
|
||||
"@opentelemetry/sdk-trace-base": "^2.6.1",
|
||||
"@opentelemetry/sdk-metrics": "^2.7.0",
|
||||
"@opentelemetry/sdk-trace-base": "^2.7.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.40.0",
|
||||
"@sentry/node": "^10.47.0",
|
||||
"@smithy/core": "^3.23.13",
|
||||
"@smithy/node-http-handler": "^4.5.1",
|
||||
"@types/bun": "^1.3.11",
|
||||
"@sentry/node": "^10.49.0",
|
||||
"@smithy/core": "^3.23.15",
|
||||
"@smithy/node-http-handler": "^4.5.3",
|
||||
"@types/bun": "^1.3.12",
|
||||
"@types/cacache": "^20.0.1",
|
||||
"@types/he": "^1.2.3",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^25.6.0",
|
||||
"@types/picomatch": "^4.0.3",
|
||||
"@types/plist": "^3.0.5",
|
||||
"@types/proper-lockfile": "^4.1.4",
|
||||
@@ -123,7 +138,7 @@
|
||||
"asciichart": "^1.5.25",
|
||||
"audio-capture-napi": "workspace:*",
|
||||
"auto-bind": "^5.0.1",
|
||||
"axios": "^1.14.0",
|
||||
"axios": "^1.15.0",
|
||||
"bidi-js": "^1.0.3",
|
||||
"cacache": "^20.0.4",
|
||||
"chalk": "^5.6.2",
|
||||
@@ -138,7 +153,7 @@
|
||||
"execa": "^9.6.1",
|
||||
"fflate": "^0.8.2",
|
||||
"figures": "^6.1.0",
|
||||
"fuse.js": "^7.1.0",
|
||||
"fuse.js": "^7.3.0",
|
||||
"get-east-asian-width": "^1.5.0",
|
||||
"google-auth-library": "^10.6.2",
|
||||
"he": "^1.2.0",
|
||||
@@ -148,20 +163,21 @@
|
||||
"image-processor-napi": "workspace:*",
|
||||
"indent-string": "^5.0.0",
|
||||
"jsonc-parser": "^3.3.1",
|
||||
"knip": "^6.1.1",
|
||||
"lodash-es": "^4.17.23",
|
||||
"lru-cache": "^11.2.7",
|
||||
"marked": "^17.0.5",
|
||||
"knip": "^6.4.1",
|
||||
"lodash-es": "^4.18.1",
|
||||
"lru-cache": "^11.3.5",
|
||||
"marked": "^17.0.6",
|
||||
"modifiers-napi": "workspace:*",
|
||||
"openai": "^6.33.0",
|
||||
"openai": "^6.34.0",
|
||||
"p-map": "^7.0.4",
|
||||
"picomatch": "^4.0.4",
|
||||
"plist": "^3.1.0",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^19.2.4",
|
||||
"react": "^19.2.5",
|
||||
"react-compiler-runtime": "^1.0.0",
|
||||
"react-reconciler": "^0.33.0",
|
||||
"rollup": "^4.60.2",
|
||||
"semver": "^7.7.4",
|
||||
"sharp": "^0.34.5",
|
||||
"shell-quote": "^1.8.3",
|
||||
@@ -170,17 +186,17 @@
|
||||
"strip-ansi": "^7.2.0",
|
||||
"supports-hyperlinks": "^4.4.0",
|
||||
"tree-kill": "^1.2.2",
|
||||
"turndown": "^7.2.2",
|
||||
"type-fest": "^5.5.0",
|
||||
"typescript": "^6.0.2",
|
||||
"undici": "^7.24.6",
|
||||
"turndown": "^7.2.4",
|
||||
"type-fest": "^5.6.0",
|
||||
"typescript": "^6.0.3",
|
||||
"undici": "^7.25.0",
|
||||
"url-handler-napi": "workspace:*",
|
||||
"usehooks-ts": "^3.1.1",
|
||||
"vite": "^8.0.8",
|
||||
"vscode-jsonrpc": "^8.2.1",
|
||||
"vscode-languageserver-protocol": "^3.17.5",
|
||||
"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"
|
||||
|
||||
5
packages/@ant/claude-for-chrome-mcp/tsconfig.json
Normal file
5
packages/@ant/claude-for-chrome-mcp/tsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -5,9 +5,12 @@
|
||||
* mouse and keyboard via CoreGraphics events and System Events.
|
||||
*/
|
||||
|
||||
import { $ } from 'bun'
|
||||
import { execFile, execFileSync } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
import type { FrontmostAppInfo, InputBackend } from '../types.js'
|
||||
|
||||
const execFileAsync = promisify(execFile)
|
||||
|
||||
const KEY_MAP: Record<string, number> = {
|
||||
return: 36, enter: 36, tab: 48, space: 49, delete: 51, backspace: 51,
|
||||
escape: 53, esc: 53,
|
||||
@@ -25,13 +28,17 @@ const MODIFIER_MAP: Record<string, string> = {
|
||||
}
|
||||
|
||||
async function osascript(script: string): Promise<string> {
|
||||
const result = await $`osascript -e ${script}`.quiet().nothrow().text()
|
||||
return result.trim()
|
||||
const { stdout } = await execFileAsync('osascript', ['-e', script], {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
return stdout.trim()
|
||||
}
|
||||
|
||||
async function jxa(script: string): Promise<string> {
|
||||
const result = await $`osascript -l JavaScript -e ${script}`.quiet().nothrow().text()
|
||||
return result.trim()
|
||||
const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', script], {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
return stdout.trim()
|
||||
}
|
||||
|
||||
function buildMouseJxa(eventType: string, x: number, y: number, btn: number, clickState?: number): string {
|
||||
@@ -115,19 +122,14 @@ export const typeText: InputBackend['typeText'] = async (text) => {
|
||||
|
||||
export const getFrontmostAppInfo: InputBackend['getFrontmostAppInfo'] = () => {
|
||||
try {
|
||||
const result = Bun.spawnSync({
|
||||
cmd: ['osascript', '-e', `
|
||||
tell application "System Events"
|
||||
set frontApp to first application process whose frontmost is true
|
||||
set appName to name of frontApp
|
||||
set bundleId to bundle identifier of frontApp
|
||||
return bundleId & "|" & appName
|
||||
end tell
|
||||
`],
|
||||
stdout: 'pipe',
|
||||
stderr: 'pipe',
|
||||
})
|
||||
const output = new TextDecoder().decode(result.stdout).trim()
|
||||
const output = execFileSync('osascript', ['-e', `
|
||||
tell application "System Events"
|
||||
set frontApp to first application process whose frontmost is true
|
||||
set appName to name of frontApp
|
||||
set bundleId to bundle identifier of frontApp
|
||||
return bundleId & "|" & appName
|
||||
end tell
|
||||
`], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim()
|
||||
if (!output || !output.includes('|')) return null
|
||||
const [bundleId, appName] = output.split('|', 2)
|
||||
return { bundleId: bundleId!, appName: appName! }
|
||||
|
||||
5
packages/@ant/computer-use-input/tsconfig.json
Normal file
5
packages/@ant/computer-use-input/tsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -37,16 +37,21 @@
|
||||
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
||||
import { randomUUID } from "node:crypto";
|
||||
|
||||
/** Detect actual image MIME type from base64 data using magic bytes. */
|
||||
/** Detect actual image MIME type from base64 data by decoding the magic bytes. */
|
||||
function detectMimeFromBase64(b64: string): string {
|
||||
// First byte is enough to distinguish PNG (0x89) from JPEG (0xFF)
|
||||
const c = b64.charCodeAt(0);
|
||||
if (c === 0x89) return "image/png";
|
||||
if (c === 0xFF) return "image/jpeg";
|
||||
// RIFF = WebP
|
||||
if (c === 0x52) return "image/webp";
|
||||
// GIF
|
||||
if (c === 0x47) return "image/gif";
|
||||
// Decode first 12 raw bytes (16 base64 chars is enough) and check standard magic bytes.
|
||||
// PNG: 89 50 4E 47
|
||||
// JPEG: FF D8 FF
|
||||
// RIFF+WEBP: "RIFF" at 0..3 + "WEBP" at 8..11
|
||||
// GIF: "GIF" at 0..2
|
||||
const raw = Buffer.from(b64.slice(0, 16), "base64");
|
||||
if (raw[0] === 0x89 && raw[1] === 0x50 && raw[2] === 0x4e && raw[3] === 0x47) return "image/png";
|
||||
if (raw[0] === 0xff && raw[1] === 0xd8 && raw[2] === 0xff) return "image/jpeg";
|
||||
if (
|
||||
raw[0] === 0x52 && raw[1] === 0x49 && raw[2] === 0x46 && raw[3] === 0x46 && // RIFF
|
||||
raw[8] === 0x57 && raw[9] === 0x45 && raw[10] === 0x42 && raw[11] === 0x50 // WEBP
|
||||
) return "image/webp";
|
||||
if (raw[0] === 0x47 && raw[1] === 0x49 && raw[2] === 0x46) return "image/gif";
|
||||
return "image/png";
|
||||
}
|
||||
|
||||
|
||||
5
packages/@ant/computer-use-mcp/tsconfig.json
Normal file
5
packages/@ant/computer-use-mcp/tsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -274,4 +274,9 @@ export const screenshot: ScreenshotAPI = {
|
||||
if (displayId !== undefined) args.push('-D', String(displayId))
|
||||
return captureScreenToBase64(args)
|
||||
},
|
||||
|
||||
captureWindowTarget(_titleOrHwnd: string | number): ScreenshotResult | null {
|
||||
// Window capture not supported on macOS via this backend
|
||||
return null
|
||||
},
|
||||
}
|
||||
|
||||
@@ -275,4 +275,9 @@ export const screenshot: ScreenshotAPI = {
|
||||
return { base64: '', width: 0, height: 0 }
|
||||
}
|
||||
},
|
||||
|
||||
captureWindowTarget(_titleOrHwnd: string | number): ScreenshotResult | null {
|
||||
// Window capture not supported on Linux via this backend
|
||||
return null
|
||||
},
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ export interface ScreenshotAPI {
|
||||
x: number, y: number, w: number, h: number,
|
||||
outW: number, outH: number, quality: number, displayId?: number,
|
||||
): Promise<ScreenshotResult>
|
||||
captureWindowTarget(titleOrHwnd: string | number): ScreenshotResult | null
|
||||
}
|
||||
|
||||
export interface SwiftBackend {
|
||||
|
||||
5
packages/@ant/computer-use-swift/tsconfig.json
Normal file
5
packages/@ant/computer-use-swift/tsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
176
packages/@ant/ink/docs/01-getting-started.md
Normal file
176
packages/@ant/ink/docs/01-getting-started.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# Chapter 1: Getting Started
|
||||
|
||||
## Installation
|
||||
|
||||
`@anthropic/ink` is a workspace package. It is consumed internally and not published to npm.
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"@anthropic/ink": "workspace:*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Peer Dependencies
|
||||
|
||||
- `react` ^19.2.4
|
||||
- `react-reconciler` ^0.33.0
|
||||
|
||||
### Key Dependencies
|
||||
|
||||
| Package | Purpose |
|
||||
|---------|---------|
|
||||
| `chalk` | ANSI color generation |
|
||||
| `cli-boxes` | Border style definitions |
|
||||
| `get-east-asian-width` | CJK character width measurement |
|
||||
| `wrap-ansi` | ANSI-aware word wrapping |
|
||||
| `bidi-js` | Bidirectional text support |
|
||||
| `lodash-es` | Utility functions (throttle, noop) |
|
||||
| `signal-exit` | Process exit handler cleanup |
|
||||
| `emoji-regex` | Emoji width handling |
|
||||
|
||||
## Basic Rendering
|
||||
|
||||
### `render(node, options?)`
|
||||
|
||||
The primary entry point. Renders a React element tree to the terminal.
|
||||
|
||||
```tsx
|
||||
import { render } from '@anthropic/ink'
|
||||
import { Box, Text } from '@anthropic/ink'
|
||||
|
||||
const { unmount, rerender, waitUntilExit } = await render(
|
||||
<Box>
|
||||
<Text>Hello, World!</Text>
|
||||
</Box>
|
||||
)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `node` -- `ReactNode` to render
|
||||
- `options` -- `RenderOptions | NodeJS.WriteStream` (optional)
|
||||
|
||||
**Returns:** `Promise<Instance>` with:
|
||||
- `rerender(node)` -- Replace the root node
|
||||
- `unmount()` -- Unmount and clean up
|
||||
- `waitUntilExit()` -- `Promise<void>` that resolves on unmount
|
||||
- `cleanup()` -- Remove from instance registry
|
||||
|
||||
### `renderSync(node, options?)`
|
||||
|
||||
Synchronous version of render. Same API, returns `Instance` directly (no Promise).
|
||||
|
||||
```tsx
|
||||
import { renderSync } from '@anthropic/ink'
|
||||
|
||||
const instance = renderSync(<App />)
|
||||
// instance.rerender, instance.unmount, etc.
|
||||
```
|
||||
|
||||
### `createRoot(options?)`
|
||||
|
||||
Creates a managed Ink root without immediately rendering. Similar to `react-dom`'s `createRoot`.
|
||||
|
||||
```tsx
|
||||
import { createRoot } from '@anthropic/ink'
|
||||
|
||||
const root = await createRoot({ exitOnCtrlC: false })
|
||||
|
||||
// Later, render into it
|
||||
root.render(<App />)
|
||||
|
||||
// You can re-render into the same root
|
||||
root.render(<DifferentApp />)
|
||||
|
||||
// Clean up
|
||||
root.unmount()
|
||||
```
|
||||
|
||||
**Returns:** `Promise<Root>` with:
|
||||
- `render(node)` -- Mount or update the tree
|
||||
- `unmount()` -- Unmount
|
||||
- `waitUntilExit()` -- `Promise<void>`
|
||||
|
||||
## RenderOptions
|
||||
|
||||
```ts
|
||||
type RenderOptions = {
|
||||
/** Output stream. Default: process.stdout */
|
||||
stdout?: NodeJS.WriteStream
|
||||
|
||||
/** Input stream. Default: process.stdin */
|
||||
stdin?: NodeJS.ReadStream
|
||||
|
||||
/** Error stream. Default: process.stderr */
|
||||
stderr?: NodeJS.WriteStream
|
||||
|
||||
/** Handle Ctrl+C to exit. Default: true */
|
||||
exitOnCtrlC?: boolean
|
||||
|
||||
/** Patch console methods to prevent Ink output mixing. Default: true */
|
||||
patchConsole?: boolean
|
||||
|
||||
/** Called after each frame render with timing info. */
|
||||
onFrame?: (event: FrameEvent) => void
|
||||
}
|
||||
```
|
||||
|
||||
## Basic Concepts
|
||||
|
||||
### Component Tree
|
||||
|
||||
Ink renders React components to a terminal using a custom reconciler. The tree structure maps to terminal output:
|
||||
|
||||
```tsx
|
||||
<Box flexDirection="column">
|
||||
<Text bold color="green">Header</Text>
|
||||
<Box flexDirection="row" gap={1}>
|
||||
<Text>Left</Text>
|
||||
<Text>Right</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
```
|
||||
|
||||
This produces terminal output with Flexbox layout (via Yoga).
|
||||
|
||||
### Rendering Pipeline
|
||||
|
||||
1. **React Reconciler** -- Standard React reconciliation; diffs virtual tree
|
||||
2. **Yoga Layout** -- Computes Flexbox positions/ sizes for every node
|
||||
3. **Render to Output** -- Walks the DOM tree, emits styled text into an `Output` buffer
|
||||
4. **Screen Diff** -- Compares new frame against previous frame in a screen buffer
|
||||
5. **Terminal Write** -- Emits minimal ANSI escape sequences to update only changed cells
|
||||
|
||||
### Module System
|
||||
|
||||
Import everything from the package root:
|
||||
|
||||
```tsx
|
||||
// Core rendering
|
||||
import { render, createRoot, renderSync } from '@anthropic/ink'
|
||||
|
||||
// Components (base, no theme)
|
||||
import { BaseBox, BaseText, ScrollBox, Button, Link, Newline, Spacer } from '@anthropic/ink'
|
||||
|
||||
// Theme-aware components (recommended)
|
||||
import { Box, Text } from '@anthropic/ink'
|
||||
|
||||
// Hooks
|
||||
import { useApp, useInput, useTerminalSize, useInterval } from '@anthropic/ink'
|
||||
|
||||
// Theme
|
||||
import { ThemeProvider, useTheme, color } from '@anthropic/ink'
|
||||
|
||||
// Keybindings
|
||||
import { useKeybinding, KeybindingProvider } from '@anthropic/ink'
|
||||
```
|
||||
|
||||
### Naming Convention: Base vs Theme-aware
|
||||
|
||||
The package exports both raw and theme-aware versions of core components:
|
||||
|
||||
- **`BaseBox`** / **`BaseText`** -- Raw components that only accept raw color values (`rgb(...)`, `#hex`, `ansi:...`, `ansi256(...)`)
|
||||
- **`Box`** / **`Text`** -- Theme-aware wrappers that accept both theme keys (`'claude'`, `'success'`, `'error'`) and raw color values
|
||||
|
||||
Always prefer the theme-aware versions unless you have a specific reason to use raw components.
|
||||
348
packages/@ant/ink/docs/02-layout.md
Normal file
348
packages/@ant/ink/docs/02-layout.md
Normal file
@@ -0,0 +1,348 @@
|
||||
# Chapter 2: Layout System
|
||||
|
||||
Ink uses [Yoga](https://yogalayout.com/) (Facebook's cross-platform layout engine) to implement CSS Flexbox in the terminal. Every layout is flexbox-based -- there is no CSS Grid or flow layout.
|
||||
|
||||
## Box Component
|
||||
|
||||
`Box` is the fundamental layout primitive. It is the terminal equivalent of `<div style="display: flex">`.
|
||||
|
||||
```tsx
|
||||
import { Box, Text } from '@anthropic/ink'
|
||||
|
||||
<Box flexDirection="row" gap={1}>
|
||||
<Text>Left</Text>
|
||||
<Text>Right</Text>
|
||||
</Box>
|
||||
```
|
||||
|
||||
### Box Props (Styles)
|
||||
|
||||
All layout props are passed directly as JSX props (no `style={}` wrapper needed):
|
||||
|
||||
#### Flex Direction
|
||||
|
||||
Controls the main axis direction.
|
||||
|
||||
```tsx
|
||||
<Box flexDirection="row">...</Box> // Left to right (default)
|
||||
<Box flexDirection="column">...</Box> // Top to bottom
|
||||
<Box flexDirection="row-reverse">...</Box> // Right to left
|
||||
<Box flexDirection="column-reverse">...</Box> // Bottom to top
|
||||
```
|
||||
|
||||
#### Flex Grow / Shrink / Basis
|
||||
|
||||
```tsx
|
||||
<Box flexGrow={1}>...</Box> // Grow to fill available space
|
||||
<Box flexShrink={0}>...</Box> // Don't shrink below intrinsic size
|
||||
<Box flexBasis={20}>...</Box> // Initial size before flex distribution
|
||||
<Box flexBasis="50%">...</Box> // Percentage basis
|
||||
```
|
||||
|
||||
Default values: `flexGrow={0}`, `flexShrink={1}`, `flexBasis=auto`.
|
||||
|
||||
#### Flex Wrap
|
||||
|
||||
```tsx
|
||||
<Box flexWrap="nowrap">...</Box> // Single line (default)
|
||||
<Box flexWrap="wrap">...</Box> // Multiple lines
|
||||
<Box flexWrap="wrap-reverse">...</Box> // Reverse cross-axis stacking
|
||||
```
|
||||
|
||||
#### Alignment
|
||||
|
||||
```tsx
|
||||
<Box alignItems="flex-start">...</Box> // Cross-axis start
|
||||
<Box alignItems="center">...</Box> // Cross-axis center
|
||||
<Box alignItems="flex-end">...</Box> // Cross-axis end
|
||||
<Box alignItems="stretch">...</Box> // Stretch to fill (default)
|
||||
|
||||
<Box alignSelf="flex-start">...</Box> // Override parent's alignItems
|
||||
<Box alignSelf="center">...</Box>
|
||||
<Box alignSelf="flex-end">...</Box>
|
||||
<Box alignSelf="auto">...</Box> // Inherit from parent
|
||||
```
|
||||
|
||||
#### Justify Content
|
||||
|
||||
```tsx
|
||||
<Box justifyContent="flex-start">...</Box> // Main-axis start (default)
|
||||
<Box justifyContent="flex-end">...</Box> // Main-axis end
|
||||
<Box justifyContent="center">...</Box> // Center
|
||||
<Box justifyContent="space-between">...</Box> // Equal gaps, no edges
|
||||
<Box justifyContent="space-around">...</Box> // Equal gaps with edges
|
||||
<Box justifyContent="space-evenly">...</Box> // Evenly distributed
|
||||
```
|
||||
|
||||
#### Gap
|
||||
|
||||
Spacing between children (only accepts integers):
|
||||
|
||||
```tsx
|
||||
<Box gap={1}>...</Box> // Both row and column gap
|
||||
<Box columnGap={2}>...</Box> // Gap between columns only
|
||||
<Box rowGap={1}>...</Box> // Gap between rows only
|
||||
```
|
||||
|
||||
#### Padding
|
||||
|
||||
Inner spacing (only accepts integers):
|
||||
|
||||
```tsx
|
||||
<Box padding={1}>...</Box> // All sides
|
||||
<Box paddingX={2}>...</Box> // Left and right
|
||||
<Box paddingY={1}>...</Box> // Top and bottom
|
||||
<Box paddingLeft={2}>...</Box> // Left only
|
||||
<Box paddingRight={2}>...</Box> // Right only
|
||||
<Box paddingTop={1}>...</Box> // Top only
|
||||
<Box paddingBottom={1}>...</Box> // Bottom only
|
||||
```
|
||||
|
||||
#### Margin
|
||||
|
||||
Outer spacing (only accepts integers):
|
||||
|
||||
```tsx
|
||||
<Box margin={1}>...</Box> // All sides
|
||||
<Box marginX={2}>...</Box> // Left and right
|
||||
<Box marginY={1}>...</Box> // Top and bottom
|
||||
<Box marginLeft={2}>...</Box> // Left only
|
||||
<Box marginRight={2}>...</Box> // Right only
|
||||
<Box marginTop={1}>...</Box> // Top only
|
||||
<Box marginBottom={1}>...</Box> // Bottom only
|
||||
```
|
||||
|
||||
> **Note:** Fractional values for padding, margin, and gap are not supported. Ink will emit warnings if non-integer values are used.
|
||||
|
||||
#### Width & Height
|
||||
|
||||
```tsx
|
||||
<Box width={40}>...</Box> // Fixed 40 characters wide
|
||||
<Box height={10}>...</Box> // Fixed 10 rows tall
|
||||
<Box width="50%">...</Box> // 50% of parent's width
|
||||
<Box width="100%">...</Box> // Full parent width
|
||||
```
|
||||
|
||||
#### Min/Max Dimensions
|
||||
|
||||
```tsx
|
||||
<Box minWidth={20}>...</Box>
|
||||
<Box maxWidth={80}>...</Box>
|
||||
<Box minHeight={5}>...</Box>
|
||||
<Box maxHeight={20}>...</Box>
|
||||
```
|
||||
|
||||
Percentage values are supported: `minWidth="30%"`.
|
||||
|
||||
#### Position
|
||||
|
||||
```tsx
|
||||
<Box position="absolute" top={0} right={0}>...</Box>
|
||||
<Box position="absolute" top="10%" left="20%">...</Box>
|
||||
<Box position="relative">...</Box> // Default
|
||||
```
|
||||
|
||||
Position `absolute` removes the element from normal flow and positions it relative to its nearest positioned ancestor. Useful for overlays.
|
||||
|
||||
#### Display
|
||||
|
||||
```tsx
|
||||
<Box display="flex">...</Box> // Visible (default)
|
||||
<Box display="none">...</Box> // Hidden (removed from layout)
|
||||
```
|
||||
|
||||
#### Border
|
||||
|
||||
```tsx
|
||||
<Box borderStyle="single">...</Box> // Thin border
|
||||
<Box borderStyle="double">...</Box> // Double-line border
|
||||
<Box borderStyle="round">...</Box> // Rounded corners
|
||||
<Box borderStyle="bold">...</Box> // Bold border
|
||||
<Box borderStyle="singleDouble">...</Box> // Mixed
|
||||
<Box borderStyle="doubleSingle">...</Box> // Mixed
|
||||
<Box borderStyle="classic">...</Box> // ASCII art border
|
||||
```
|
||||
|
||||
Control individual sides and colors:
|
||||
|
||||
```tsx
|
||||
<Box
|
||||
borderStyle="single"
|
||||
borderTop={false} // Hide top border
|
||||
borderBottom={true} // Show bottom border
|
||||
borderColor="rgb(255,0,0)" // Red border
|
||||
borderDimColor={true} // Dim the border
|
||||
>
|
||||
...
|
||||
</Box>
|
||||
```
|
||||
|
||||
Per-side colors:
|
||||
|
||||
```tsx
|
||||
<Box
|
||||
borderStyle="single"
|
||||
borderTopColor="rgb(255,0,0)"
|
||||
borderBottomColor="ansi:green"
|
||||
borderLeftColor="#0000FF"
|
||||
borderRightColor="ansi256(200)"
|
||||
/>
|
||||
```
|
||||
|
||||
Border text (labels in the border):
|
||||
|
||||
```tsx
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderText={{ title: "My Panel", align: "left" }}
|
||||
/>
|
||||
```
|
||||
|
||||
#### Background
|
||||
|
||||
```tsx
|
||||
<Box backgroundColor="rgb(40,40,40)">...</Box>
|
||||
```
|
||||
|
||||
#### Overflow
|
||||
|
||||
```tsx
|
||||
<Box overflow="visible">...</Box> // Content expands container (default)
|
||||
<Box overflow="hidden">...</Box> // Clip without scrolling
|
||||
<Box overflow="scroll">...</Box> // Enable scrolling (use ScrollBox)
|
||||
```
|
||||
|
||||
`overflowX` and `overflowY` control each axis independently.
|
||||
|
||||
#### Opaque
|
||||
|
||||
```tsx
|
||||
<Box opaque={true}>...</Box>
|
||||
```
|
||||
|
||||
Fills the box interior with spaces (using terminal's default background) before rendering children. Useful for absolute-positioned overlays where gaps would otherwise be transparent.
|
||||
|
||||
#### NoSelect
|
||||
|
||||
```tsx
|
||||
<Box noSelect={true}>...</Box> // Exclude from text selection
|
||||
<Box noSelect="from-left-edge">...</Box> // Exclude from column 0 to box edge
|
||||
```
|
||||
|
||||
Only affects alt-screen text selection. Useful for gutters (line numbers, diff markers).
|
||||
|
||||
## Spacer
|
||||
|
||||
`Spacer` fills all available space along the main axis (equivalent to `flexGrow: 1`).
|
||||
|
||||
```tsx
|
||||
<Box flexDirection="row">
|
||||
<Text>Left</Text>
|
||||
<Spacer />
|
||||
<Text>Right</Text>
|
||||
</Box>
|
||||
```
|
||||
|
||||
## Newline
|
||||
|
||||
Inserts line breaks.
|
||||
|
||||
```tsx
|
||||
<Text>
|
||||
Line 1
|
||||
<Newline />
|
||||
Line 2
|
||||
<Newline count={2} />
|
||||
Line 4 (after double break)
|
||||
</Text>
|
||||
```
|
||||
|
||||
## Layout Examples
|
||||
|
||||
### Two-column layout
|
||||
|
||||
```tsx
|
||||
<Box flexDirection="row" width={80}>
|
||||
<Box width="50%" padding={1}>
|
||||
<Text>Left column</Text>
|
||||
</Box>
|
||||
<Box width="50%" padding={1}>
|
||||
<Text>Right column</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
```
|
||||
|
||||
### Centered content
|
||||
|
||||
```tsx
|
||||
<Box justifyContent="center" alignItems="center" height={20}>
|
||||
<Text>Centered!</Text>
|
||||
</Box>
|
||||
```
|
||||
|
||||
### Sticky footer
|
||||
|
||||
```tsx
|
||||
<Box flexDirection="column" height={24}>
|
||||
<Box flexGrow={1}>
|
||||
<Text>Scrollable content area</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text>Status bar at bottom</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
```
|
||||
|
||||
### Bordered panel with title
|
||||
|
||||
```tsx
|
||||
<Box
|
||||
flexDirection="column"
|
||||
borderStyle="round"
|
||||
borderColor="rgb(87,105,247)"
|
||||
padding={1}
|
||||
width={60}
|
||||
>
|
||||
<Text bold>Panel Title</Text>
|
||||
<Text>Panel content goes here.</Text>
|
||||
</Box>
|
||||
```
|
||||
|
||||
## NoSelect
|
||||
|
||||
Wraps a region to exclude it from text selection in alt-screen mode. A convenience wrapper around `Box` with `noSelect` set.
|
||||
|
||||
```tsx
|
||||
import { NoSelect } from '@anthropic/ink'
|
||||
|
||||
<Box flexDirection="row">
|
||||
<NoSelect>
|
||||
<Text dimColor>1 │ </Text>
|
||||
</NoSelect>
|
||||
<Text>selectable code here</Text>
|
||||
</Box>
|
||||
```
|
||||
|
||||
### Props
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `children` | `ReactNode` | - | Content |
|
||||
| `fromLeftEdge` | `boolean` | `false` | Extend exclusion from column 0 to box's right edge |
|
||||
|
||||
Accepts all `BoxProps` except `noSelect`.
|
||||
|
||||
## BaseBox vs ThemedBox
|
||||
|
||||
Two versions of Box are exported:
|
||||
|
||||
- **`BaseBox`** (imported as `BaseBox`) -- Raw box, color props accept only raw `Color` values
|
||||
- **`Box`** (themed, imported as `Box`) -- Theme-aware, color props accept `keyof Theme | Color`
|
||||
|
||||
```tsx
|
||||
// Raw
|
||||
<BaseBox borderStyle="single" borderColor="rgb(255,0,0)" />
|
||||
|
||||
// Theme-aware (resolves 'permission' to the current theme's blue)
|
||||
<Box borderStyle="single" borderColor="permission" />
|
||||
```
|
||||
238
packages/@ant/ink/docs/03-text-and-styling.md
Normal file
238
packages/@ant/ink/docs/03-text-and-styling.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# Chapter 3: Text & Styling
|
||||
|
||||
## Text Component
|
||||
|
||||
`Text` renders styled text content. It supports colors, emphasis, and text wrapping.
|
||||
|
||||
```tsx
|
||||
import { Text } from '@anthropic/ink'
|
||||
|
||||
<Text bold color="success">Operation complete</Text>
|
||||
```
|
||||
|
||||
### Text Props
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `color` | `keyof Theme \| Color` | - | Foreground color |
|
||||
| `backgroundColor` | `keyof Theme` | - | Background color (theme-aware) |
|
||||
| `bold` | `boolean` | `false` | Bold text |
|
||||
| `dimColor` | `boolean` | `false` | Dim text (uses theme's `inactive` color) |
|
||||
| `italic` | `boolean` | `false` | Italic text |
|
||||
| `underline` | `boolean` | `false` | Underlined text |
|
||||
| `strikethrough` | `boolean` | `false` | Strikethrough text |
|
||||
| `inverse` | `boolean` | `false` | Swap foreground/background |
|
||||
| `wrap` | `TextWrap` | `'wrap'` | Wrapping/truncation mode |
|
||||
| `children` | `ReactNode` | - | Text content |
|
||||
|
||||
> **Note:** `bold` and `dimColor` are mutually exclusive (ANSI terminals cannot render both simultaneously).
|
||||
|
||||
### BaseText vs ThemedText
|
||||
|
||||
- **`BaseText`** -- Accepts raw `Color` values only
|
||||
- **`Text`** (default export) -- Theme-aware, accepts `keyof Theme | Color` for `color`, and `keyof Theme` for `backgroundColor`
|
||||
|
||||
```tsx
|
||||
// Raw color
|
||||
<BaseText color="rgb(255,0,0)">Red text</BaseText>
|
||||
|
||||
// Theme key (resolved to current theme palette)
|
||||
<Text color="error">Error message</Text>
|
||||
|
||||
// Mixed
|
||||
<Text color="#FF0000">Custom red</Text>
|
||||
```
|
||||
|
||||
### Text Wrap Modes
|
||||
|
||||
```tsx
|
||||
<Text wrap="wrap">...</Text> // Word-wrap at container width (default)
|
||||
<Text wrap="wrap-trim">...</Text> // Wrap + trim trailing whitespace
|
||||
<Text wrap="end">...</Text> // Truncate with "..." at end
|
||||
<Text wrap="truncate-end">...</Text> // Same as "end"
|
||||
<Text wrap="truncate">...</Text> // Truncate (no ellipsis)
|
||||
<Text wrap="middle">...</Text> // "start...end"
|
||||
<Text wrap="truncate-middle">...</Text> // Same as "middle"
|
||||
<Text wrap="truncate-start">...</Text> // "...text"
|
||||
```
|
||||
|
||||
### TextHoverColorContext
|
||||
|
||||
Uncolored `Text` children inherit a hover color from context:
|
||||
|
||||
```tsx
|
||||
import { TextHoverColorContext } from '@anthropic/ink'
|
||||
|
||||
<TextHoverColorContext.Provider value="suggestion">
|
||||
<Text>Uncolored text gets the suggestion color</Text>
|
||||
<Text color="error">This stays red</Text>
|
||||
</TextHoverColorContext.Provider>
|
||||
```
|
||||
|
||||
Precedence: explicit `color` > `TextHoverColorContext` > `dimColor`.
|
||||
|
||||
## Color System
|
||||
|
||||
### Raw Color Formats
|
||||
|
||||
Four formats are supported for raw color values:
|
||||
|
||||
```tsx
|
||||
// RGB
|
||||
<Text color="rgb(255,107,128)">Bright red</Text>
|
||||
|
||||
// Hex
|
||||
<Text color="#FF6B80">Bright red</Text>
|
||||
|
||||
// ANSI 256-color
|
||||
<Text color="ansi256(196)">Red from 256-color palette</Text>
|
||||
|
||||
// Named ANSI 16-color
|
||||
<Text color="ansi:red">Red</Text>
|
||||
<Text color="ansi:greenBright">Bright green</Text>
|
||||
```
|
||||
|
||||
### ANSI Named Colors
|
||||
|
||||
Full list of `ansi:` prefixed names:
|
||||
|
||||
| Name | Color |
|
||||
|------|-------|
|
||||
| `ansi:black` | Black |
|
||||
| `ansi:red` | Red |
|
||||
| `ansi:green` | Green |
|
||||
| `ansi:yellow` | Yellow |
|
||||
| `ansi:blue` | Blue |
|
||||
| `ansi:magenta` | Magenta |
|
||||
| `ansi:cyan` | Cyan |
|
||||
| `ansi:white` | White |
|
||||
| `ansi:blackBright` | Dark gray |
|
||||
| `ansi:redBright` | Bright red |
|
||||
| `ansi:greenBright` | Bright green |
|
||||
| `ansi:yellowBright` | Bright yellow |
|
||||
| `ansi:blueBright` | Bright blue |
|
||||
| `ansi:magentaBright` | Bright magenta |
|
||||
| `ansi:cyanBright` | Bright cyan |
|
||||
| `ansi:whiteBright` | Bright white |
|
||||
|
||||
## Utility Functions
|
||||
|
||||
### `color(colorValue, themeName, type?)`
|
||||
|
||||
Curried theme-aware color function. Resolves theme keys to raw color values.
|
||||
|
||||
```tsx
|
||||
import { color } from '@anthropic/ink'
|
||||
|
||||
const paint = color('error', 'dark') // Returns (text: string) => string
|
||||
console.log(paint('failed')) // 'failed' wrapped in ANSI red codes
|
||||
|
||||
const paintFg = color('rgb(255,0,0)', 'dark', 'foreground')
|
||||
const paintBg = color('success', 'dark', 'background')
|
||||
```
|
||||
|
||||
Parameters:
|
||||
- `c` -- `keyof Theme | Color | undefined` -- Theme key or raw color
|
||||
- `theme` -- `ThemeName` -- Current theme
|
||||
- `type` -- `'foreground' | 'background'` (default `'foreground'`)
|
||||
|
||||
### `stringWidth(text)`
|
||||
|
||||
Measures the visual width of a string in terminal columns, accounting for:
|
||||
- CJK characters (2 columns each)
|
||||
- Emoji (2 columns each)
|
||||
- ANSI escape sequences (0 columns)
|
||||
|
||||
```tsx
|
||||
import { stringWidth } from '@anthropic/ink'
|
||||
|
||||
stringWidth('hello') // 5
|
||||
stringWidth('你好') // 4
|
||||
stringWidth('\x1b[31mhi') // 2 (ANSI codes ignored)
|
||||
```
|
||||
|
||||
### `wrapText(text, width, textWrap)`
|
||||
|
||||
Wraps text to a given width with the specified wrapping mode.
|
||||
|
||||
```tsx
|
||||
import { wrapText } from '@anthropic/ink'
|
||||
|
||||
wrapText('Hello World', 5, 'wrap') // 'Hello\nWorld'
|
||||
wrapText('Hello World', 8, 'end') // 'Hello...'
|
||||
```
|
||||
|
||||
### `wrapAnsi(text, width)`
|
||||
|
||||
Wraps text containing ANSI escape codes while preserving styling.
|
||||
|
||||
```tsx
|
||||
import { wrapAnsi } from '@anthropic/ink'
|
||||
|
||||
wrapAnsi('\x1b[31mHello World\x1b[0m', 5)
|
||||
// Wraps at word boundaries, keeps color codes intact
|
||||
```
|
||||
|
||||
### `measureElement(node)`
|
||||
|
||||
Measures a rendered DOM element's dimensions.
|
||||
|
||||
```tsx
|
||||
import { measureElement } from '@anthropic/ink'
|
||||
|
||||
const { width, height } = measureElement(domElement)
|
||||
```
|
||||
|
||||
## Link Component
|
||||
|
||||
Renders an OSC 8 terminal hyperlink (clickable URL in supported terminals).
|
||||
|
||||
```tsx
|
||||
import { Link } from '@anthropic/ink'
|
||||
|
||||
<Link url="https://example.com">
|
||||
<Text underline color="suggestion">example.com</Text>
|
||||
</Link>
|
||||
```
|
||||
|
||||
Props:
|
||||
- `url` -- `string` (required) -- Target URL
|
||||
- `children` -- `ReactNode` -- Display content
|
||||
- `fallback` -- `ReactNode` -- Shown when hyperlinks are unsupported
|
||||
|
||||
## RawAnsi Component
|
||||
|
||||
Renders pre-formatted ANSI strings directly into the layout.
|
||||
|
||||
```tsx
|
||||
import { RawAnsi } from '@anthropic/ink'
|
||||
|
||||
<RawAnsi
|
||||
lines={['\x1b[31mRed line 1\x1b[0m', '\x1b[32mGreen line 2\x1b[0m']}
|
||||
width={40}
|
||||
/>
|
||||
```
|
||||
|
||||
Props:
|
||||
- `lines` -- `string[]` -- Pre-rendered ANSI lines (one terminal row each)
|
||||
- `width` -- `number` -- Column width the producer wrapped to
|
||||
|
||||
## Border Rendering
|
||||
|
||||
### `renderBorder(box, output, options?)`
|
||||
|
||||
Low-level border rendering function used internally by Box.
|
||||
|
||||
```tsx
|
||||
import { renderBorder } from '@anthropic/ink'
|
||||
import type { BorderTextOptions } from '@anthropic/ink'
|
||||
```
|
||||
|
||||
Border styles available (from `cli-boxes`):
|
||||
- `single` -- Thin lines `─│┌┐└┘`
|
||||
- `double` -- Double lines `═║╔╗╚╝`
|
||||
- `round` -- Rounded corners `─│╭╮╰╯`
|
||||
- `bold` -- Bold lines `━┃┏┓┗┛`
|
||||
- `singleDouble` -- Single horizontal, double vertical
|
||||
- `doubleSingle` -- Double horizontal, single vertical
|
||||
- `classic` -- ASCII `─|++++`
|
||||
213
packages/@ant/ink/docs/04-theme-system.md
Normal file
213
packages/@ant/ink/docs/04-theme-system.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# Chapter 4: Theme System
|
||||
|
||||
The theme system provides consistent, accessible color palettes across the application. It supports dark mode, light mode, ANSI-only terminals, and colorblind-accessible variants.
|
||||
|
||||
## ThemeProvider
|
||||
|
||||
Wraps the application to provide theme context.
|
||||
|
||||
```tsx
|
||||
import { ThemeProvider } from '@anthropic/ink'
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ThemeProvider initialState="dark" onThemeSave={(setting) => saveConfig(setting)}>
|
||||
<MyComponent />
|
||||
</ThemeProvider>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Props
|
||||
|
||||
| Prop | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `children` | `ReactNode` | Child components |
|
||||
| `initialState` | `ThemeSetting` | Initial theme (default: loads from config) |
|
||||
| `onThemeSave` | `(setting: ThemeSetting) => void` | Called when theme is saved |
|
||||
|
||||
### Theme Configuration Injection
|
||||
|
||||
Before mounting, inject config persistence callbacks:
|
||||
|
||||
```tsx
|
||||
import { setThemeConfigCallbacks } from '@anthropic/ink'
|
||||
|
||||
setThemeConfigCallbacks({
|
||||
loadTheme: () => configStore.get('theme', 'dark'),
|
||||
saveTheme: (setting) => configStore.set('theme', setting),
|
||||
})
|
||||
```
|
||||
|
||||
## Theme Settings
|
||||
|
||||
```ts
|
||||
type ThemeSetting = 'auto' | 'dark' | 'light' | 'light-daltonized' | 'dark-daltonized' | 'light-ansi' | 'dark-ansi'
|
||||
type ThemeName = 'dark' | 'light' | 'light-daltonized' | 'dark-daltonized' | 'light-ansi' | 'dark-ansi'
|
||||
```
|
||||
|
||||
| Theme | Description |
|
||||
|-------|-------------|
|
||||
| `dark` | Dark theme with RGB colors (default) |
|
||||
| `light` | Light theme with RGB colors |
|
||||
| `dark-daltonized` | Colorblind-accessible dark theme |
|
||||
| `light-daltonized` | Colorblind-accessible light theme |
|
||||
| `dark-ansi` | Dark theme using only 16 ANSI colors |
|
||||
| `light-ansi` | Light theme using only 16 ANSI colors |
|
||||
| `auto` | Follows terminal's dark/light mode (resolved at runtime) |
|
||||
|
||||
## Theme Hooks
|
||||
|
||||
### `useTheme()`
|
||||
|
||||
Returns the resolved theme name and setter.
|
||||
|
||||
```tsx
|
||||
const [currentTheme, setTheme] = useTheme()
|
||||
// currentTheme: ThemeName (never 'auto')
|
||||
// setTheme: (setting: ThemeSetting) => void
|
||||
```
|
||||
|
||||
### `useThemeSetting()`
|
||||
|
||||
Returns the raw setting (may be `'auto'`).
|
||||
|
||||
```tsx
|
||||
const setting = useThemeSetting() // 'auto' | 'dark' | ...
|
||||
```
|
||||
|
||||
### `usePreviewTheme()`
|
||||
|
||||
Returns preview controls for a theme picker UI.
|
||||
|
||||
```tsx
|
||||
const { setPreviewTheme, savePreview, cancelPreview } = usePreviewTheme()
|
||||
|
||||
// Show preview
|
||||
setPreviewTheme('light')
|
||||
|
||||
// User confirms
|
||||
savePreview()
|
||||
|
||||
// User cancels
|
||||
cancelPreview()
|
||||
```
|
||||
|
||||
## Theme Color Palette
|
||||
|
||||
Every theme defines these semantic color keys:
|
||||
|
||||
### Brand & Identity
|
||||
|
||||
| Key | Purpose |
|
||||
|-----|---------|
|
||||
| `claude` | Brand orange |
|
||||
| `claudeShimmer` | Lighter brand orange (animated) |
|
||||
| `permission` | Permission/blue |
|
||||
| `permissionShimmer` | Lighter permission blue |
|
||||
| `autoAccept` | Electric violet |
|
||||
| `planMode` | Teal/sage |
|
||||
| `ide` | Muted blue |
|
||||
|
||||
### Semantic Colors
|
||||
|
||||
| Key | Purpose |
|
||||
|-----|---------|
|
||||
| `text` | Primary text color |
|
||||
| `inverseText` | Text on inverse backgrounds |
|
||||
| `inactive` | Dimmed/disabled elements |
|
||||
| `inactiveShimmer` | Lighter inactive |
|
||||
| `subtle` | Very subtle text |
|
||||
| `suggestion` | Interactive/accent |
|
||||
| `background` | General background accent |
|
||||
| `success` | Positive/success |
|
||||
| `error` | Negative/error |
|
||||
| `warning` | Caution/warning |
|
||||
| `warningShimmer` | Lighter warning |
|
||||
| `merged` | Merged state |
|
||||
|
||||
### Diff Colors
|
||||
|
||||
| Key | Purpose |
|
||||
|-----|---------|
|
||||
| `diffAdded` | Added lines background |
|
||||
| `diffRemoved` | Removed lines background |
|
||||
| `diffAddedDimmed` | Dimmed added |
|
||||
| `diffRemovedDimmed` | Dimmed removed |
|
||||
| `diffAddedWord` | Word-level added |
|
||||
| `diffRemovedWord` | Word-level removed |
|
||||
|
||||
### UI Colors
|
||||
|
||||
| Key | Purpose |
|
||||
|-----|---------|
|
||||
| `promptBorder` | Input prompt border |
|
||||
| `promptBorderShimmer` | Lighter prompt border |
|
||||
| `bashBorder` | Shell block border |
|
||||
| `selectionBg` | Text selection highlight background |
|
||||
| `userMessageBackground` | User message background |
|
||||
| `userMessageBackgroundHover` | User message hover |
|
||||
| `messageActionsBackground` | Action buttons background |
|
||||
|
||||
### Agent Colors
|
||||
|
||||
| Key | Purpose |
|
||||
|-----|---------|
|
||||
| `red_FOR_SUBAGENTS_ONLY` | Agent color assignment |
|
||||
| `blue_FOR_SUBAGENTS_ONLY` | Agent color assignment |
|
||||
| `green_FOR_SUBAGENTS_ONLY` | Agent color assignment |
|
||||
| `yellow_FOR_SUBAGENTS_ONLY` | Agent color assignment |
|
||||
| `purple_FOR_SUBAGENTS_ONLY` | Agent color assignment |
|
||||
| `orange_FOR_SUBAGENTS_ONLY` | Agent color assignment |
|
||||
| `pink_FOR_SUBAGENTS_ONLY` | Agent color assignment |
|
||||
| `cyan_FOR_SUBAGENTS_ONLY` | Agent color assignment |
|
||||
|
||||
## Using Theme Colors in Components
|
||||
|
||||
### ThemedText
|
||||
|
||||
```tsx
|
||||
<Text color="success">Operation complete</Text>
|
||||
<Text color="error" bold>Failed!</Text>
|
||||
<Text color="claude">Claude says...</Text>
|
||||
<Text dimColor>Secondary info</Text>
|
||||
<Text backgroundColor="userMessageBackground">Highlighted</Text>
|
||||
```
|
||||
|
||||
### ThemedBox
|
||||
|
||||
```tsx
|
||||
<Box borderStyle="single" borderColor="permission" backgroundColor="userMessageBackground">
|
||||
<Text>Themed content</Text>
|
||||
</Box>
|
||||
```
|
||||
|
||||
### color() Utility
|
||||
|
||||
```tsx
|
||||
import { color, useTheme } from '@anthropic/ink'
|
||||
|
||||
function MyComponent() {
|
||||
const [themeName] = useTheme()
|
||||
const paint = color('success', themeName)
|
||||
// paint('text') returns ANSI-colored string
|
||||
}
|
||||
```
|
||||
|
||||
## Daltonized Themes
|
||||
|
||||
The daltonized themes (`light-daltonized`, `dark-daltonized`) are designed for users with protanopia/deuteranopia:
|
||||
|
||||
- Green/red diffs replaced with blue/red
|
||||
- Status colors use blue instead of green
|
||||
- Warning colors adjusted for better distinction
|
||||
- All color pairs verified for sufficient contrast
|
||||
|
||||
## System Theme Detection
|
||||
|
||||
When `ThemeSetting` is `'auto'`:
|
||||
|
||||
1. Seeds from `$COLORFGBG` environment variable
|
||||
2. Queries terminal via OSC 11 for live background color
|
||||
3. Watches for changes (terminal theme switch) in real-time
|
||||
4. Resolves to `'dark'` or `'light'` based on detected brightness
|
||||
390
packages/@ant/ink/docs/05-design-system.md
Normal file
390
packages/@ant/ink/docs/05-design-system.md
Normal file
@@ -0,0 +1,390 @@
|
||||
# Chapter 5: Design System Components
|
||||
|
||||
Pre-built theme-aware UI components for common terminal interface patterns.
|
||||
|
||||
## Dialog
|
||||
|
||||
Modal dialog with border, title, and keyboard navigation.
|
||||
|
||||
```tsx
|
||||
import { Dialog } from '@anthropic/ink'
|
||||
|
||||
<Dialog
|
||||
title="Confirm Action"
|
||||
subtitle="This cannot be undone"
|
||||
onCancel={() => setShowDialog(false)}
|
||||
color="warning"
|
||||
>
|
||||
<Text>Are you sure you want to proceed?</Text>
|
||||
</Dialog>
|
||||
```
|
||||
|
||||
### Props
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `title` | `ReactNode` | - | Dialog title (required) |
|
||||
| `subtitle` | `ReactNode` | - | Optional subtitle |
|
||||
| `children` | `ReactNode` | - | Dialog body content |
|
||||
| `onCancel` | `() => void` | - | Called on Esc/n (required) |
|
||||
| `color` | `keyof Theme` | `'permission'` | Title and border color |
|
||||
| `hideInputGuide` | `boolean` | `false` | Hide the keyboard hint footer |
|
||||
| `hideBorder` | `boolean` | `false` | Render without Pane border |
|
||||
| `inputGuide` | `(exitState) => ReactNode` | - | Custom input guide footer |
|
||||
| `isCancelActive` | `boolean` | `true` | Enable/disable cancel keybindings |
|
||||
|
||||
### Keyboard Shortcuts
|
||||
|
||||
- **Enter** -- Confirm (consumer handles this)
|
||||
- **Esc / n** -- Cancel (calls `onCancel`)
|
||||
- **Ctrl+C / Ctrl+D** -- Double-press to exit
|
||||
|
||||
### Custom Input Guide
|
||||
|
||||
```tsx
|
||||
<Dialog
|
||||
title="Save file?"
|
||||
onCancel={handleCancel}
|
||||
inputGuide={(exitState) => (
|
||||
exitState.pending
|
||||
? <Text>Press {exitState.keyName} again to exit</Text>
|
||||
: <Text>Press Enter to save, Esc to cancel</Text>
|
||||
)}
|
||||
>
|
||||
...
|
||||
</Dialog>
|
||||
```
|
||||
|
||||
## Pane
|
||||
|
||||
Bordered container with themed top border.
|
||||
|
||||
```tsx
|
||||
import { Pane } from '@anthropic/ink'
|
||||
|
||||
<Pane color="permission">
|
||||
<Text>Content inside a bordered pane</Text>
|
||||
</Pane>
|
||||
```
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `children` | `ReactNode` | - | Content |
|
||||
| `color` | `keyof Theme` | `'permission'` | Top border color |
|
||||
|
||||
## ProgressBar
|
||||
|
||||
Visual progress indicator.
|
||||
|
||||
```tsx
|
||||
import { ProgressBar } from '@anthropic/ink'
|
||||
|
||||
<ProgressBar
|
||||
ratio={0.65}
|
||||
width={40}
|
||||
fillColor="rate_limit_fill"
|
||||
emptyColor="rate_limit_empty"
|
||||
/>
|
||||
```
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `ratio` | `number` | - | Progress 0..1 (required) |
|
||||
| `width` | `number` | - | Character width (required) |
|
||||
| `fillColor` | `keyof Theme` | - | Filled portion color |
|
||||
| `emptyColor` | `keyof Theme` | - | Empty portion color |
|
||||
|
||||
## Spinner
|
||||
|
||||
Animated loading spinner. No props.
|
||||
|
||||
```tsx
|
||||
import { Spinner } from '@anthropic/ink'
|
||||
|
||||
<Box gap={1}>
|
||||
<Spinner />
|
||||
<Text>Loading...</Text>
|
||||
</Box>
|
||||
```
|
||||
|
||||
## LoadingState
|
||||
|
||||
Loading message with spinner and optional subtitle.
|
||||
|
||||
```tsx
|
||||
import { LoadingState } from '@anthropic/ink'
|
||||
|
||||
<LoadingState
|
||||
message="Installing dependencies"
|
||||
subtitle="This may take a moment"
|
||||
bold={true}
|
||||
/>
|
||||
```
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `message` | `string` | - | Loading message (required) |
|
||||
| `bold` | `boolean` | `false` | Bold message |
|
||||
| `dimColor` | `boolean` | `false` | Dimmed message |
|
||||
| `subtitle` | `string` | - | Secondary text below |
|
||||
|
||||
## StatusIcon
|
||||
|
||||
Semantic status indicator with icon and color.
|
||||
|
||||
```tsx
|
||||
import { StatusIcon } from '@anthropic/ink'
|
||||
|
||||
<StatusIcon status="success" withSpace />
|
||||
<Text>Build complete</Text>
|
||||
```
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `status` | `'success' \| 'error' \| 'warning' \| 'info' \| 'pending' \| 'loading'` | - | Status type (required) |
|
||||
| `withSpace` | `boolean` | `false` | Add trailing space |
|
||||
|
||||
Status icons:
|
||||
- `success` -- Green checkmark
|
||||
- `error` -- Red cross
|
||||
- `warning` -- Yellow warning
|
||||
- `info` -- Blue info
|
||||
- `pending` -- Dimmed circle
|
||||
- `loading` -- Dimmed ellipsis
|
||||
|
||||
## FuzzyPicker
|
||||
|
||||
Full-featured fuzzy search selector with preview support.
|
||||
|
||||
```tsx
|
||||
import { FuzzyPicker } from '@anthropic/ink'
|
||||
|
||||
<FuzzyPicker
|
||||
title="Select a file"
|
||||
items={files}
|
||||
getKey={(f) => f.path}
|
||||
renderItem={(f, focused) => <Text>{f.name}</Text>}
|
||||
onQueryChange={(q) => setFilteredFiles(filterFiles(q))}
|
||||
onSelect={(f) => openFile(f)}
|
||||
onCancel={() => setShowPicker(false)}
|
||||
/>
|
||||
```
|
||||
|
||||
### Props
|
||||
|
||||
| Prop | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `title` | `string` | Picker title (required) |
|
||||
| `items` | `readonly T[]` | Items to display (required) |
|
||||
| `getKey` | `(item: T) => string` | Unique key extractor (required) |
|
||||
| `renderItem` | `(item: T, isFocused: boolean) => ReactNode` | Item renderer (required) |
|
||||
| `onQueryChange` | `(query: string) => void` | Filter callback (required) |
|
||||
| `onSelect` | `(item: T) => void` | Enter key handler (required) |
|
||||
| `onCancel` | `() => void` | Esc handler (required) |
|
||||
| `renderPreview` | `(item: T) => ReactNode` | Preview panel renderer |
|
||||
| `previewPosition` | `'bottom' \| 'right'` | Preview placement |
|
||||
| `visibleCount` | `number` | Max visible items |
|
||||
| `direction` | `'down' \| 'up'` | Item ordering |
|
||||
| `onTab` | `PickerAction<T>` | Tab key handler |
|
||||
| `onShiftTab` | `PickerAction<T>` | Shift+Tab handler |
|
||||
| `onFocus` | `(item: T \| undefined) => void` | Focus change callback |
|
||||
| `emptyMessage` | `string \| ((query: string) => string)` | Empty state message |
|
||||
| `matchLabel` | `string` | Status line below list |
|
||||
| `placeholder` | `string` | Input placeholder |
|
||||
| `initialQuery` | `string` | Initial search query |
|
||||
| `selectAction` | `string` | Action label for byline |
|
||||
| `extraHints` | `ReactNode` | Additional keyboard hints |
|
||||
|
||||
## Tabs / Tab
|
||||
|
||||
Tabbed interface with keyboard navigation.
|
||||
|
||||
```tsx
|
||||
import { Tabs, Tab } from '@anthropic/ink'
|
||||
|
||||
<Tabs title="Settings" color="claude">
|
||||
<Tab title="General" id="general">
|
||||
<GeneralSettings />
|
||||
</Tab>
|
||||
<Tab title="Advanced" id="advanced">
|
||||
<AdvancedSettings />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
```
|
||||
|
||||
### Tabs Props
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `children` | `ReactElement<TabProps>[]` | - | Tab elements |
|
||||
| `title` | `string` | - | Header title |
|
||||
| `color` | `keyof Theme` | - | Active tab indicator color |
|
||||
| `defaultTab` | `string` | - | Initial tab id |
|
||||
| `selectedTab` | `string` | - | Controlled selected tab |
|
||||
| `onTabChange` | `(tabId: string) => void` | - | Tab change callback |
|
||||
| `hidden` | `boolean` | `false` | Hide tab headers |
|
||||
| `useFullWidth` | `boolean` | `false` | Use full terminal width |
|
||||
| `banner` | `ReactNode` | - | Banner below tab headers |
|
||||
| `disableNavigation` | `boolean` | `false` | Disable keyboard nav |
|
||||
| `initialHeaderFocused` | `boolean` | `true` | Start with header focused |
|
||||
| `contentHeight` | `number` | - | Fixed content height |
|
||||
| `navFromContent` | `boolean` | `false` | Allow Tab/Arrow from content |
|
||||
|
||||
### Tab Props
|
||||
|
||||
| Prop | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `title` | `string` | Tab label (required) |
|
||||
| `id` | `string` | Tab identifier |
|
||||
| `children` | `ReactNode` | Tab content |
|
||||
|
||||
### Tab Hooks
|
||||
|
||||
```tsx
|
||||
import { useTabsWidth, useTabHeaderFocus } from '@anthropic/ink'
|
||||
|
||||
const width = useTabsWidth() // Available content width
|
||||
const focused = useTabHeaderFocus() // Whether tab header is focused
|
||||
```
|
||||
|
||||
## ListItem
|
||||
|
||||
Selectable list item with focus/selection indicators.
|
||||
|
||||
```tsx
|
||||
import { ListItem } from '@anthropic/ink'
|
||||
|
||||
<ListItem isFocused={index === focusedIndex} isSelected={item.checked}>
|
||||
<Text>{item.label}</Text>
|
||||
</ListItem>
|
||||
```
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `isFocused` | `boolean` | - | Keyboard focus (required) |
|
||||
| `isSelected` | `boolean` | `false` | Checked/active state |
|
||||
| `children` | `ReactNode` | - | Content |
|
||||
| `description` | `string` | - | Secondary text below |
|
||||
| `styled` | `boolean` | `true` | Auto-style based on state |
|
||||
| `disabled` | `boolean` | `false` | Dimmed, non-interactive |
|
||||
| `showScrollDown` | `boolean` | `false` | Scroll-down hint arrow |
|
||||
| `showScrollUp` | `boolean` | `false` | Scroll-up hint arrow |
|
||||
| `declareCursor` | `boolean` | `true` | Declare terminal cursor |
|
||||
|
||||
## SearchBox
|
||||
|
||||
Search input with theme-aware styling.
|
||||
|
||||
```tsx
|
||||
import { SearchBox } from '@anthropic/ink'
|
||||
|
||||
<SearchBox
|
||||
query={searchQuery}
|
||||
placeholder="Search..."
|
||||
isFocused={true}
|
||||
isTerminalFocused={true}
|
||||
width="100%"
|
||||
/>
|
||||
```
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `query` | `string` | - | Current search text |
|
||||
| `placeholder` | `string` | - | Placeholder text |
|
||||
| `isFocused` | `boolean` | - | Focus state |
|
||||
| `isTerminalFocused` | `boolean` | - | Terminal focus state |
|
||||
| `prefix` | `string` | - | Input prefix label |
|
||||
| `width` | `number \| string` | - | Input width |
|
||||
| `cursorOffset` | `number` | - | Cursor position offset |
|
||||
| `borderless` | `boolean` | `false` | Remove border |
|
||||
|
||||
## Divider
|
||||
|
||||
Horizontal/vertical divider line.
|
||||
|
||||
```tsx
|
||||
import { Divider } from '@anthropic/ink'
|
||||
|
||||
<Divider width={60} color="subtle" />
|
||||
<Divider title="Section Title" />
|
||||
```
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `width` | `number` | Terminal width | Divider width |
|
||||
| `color` | `keyof Theme` | Dimmed | Line color |
|
||||
| `char` | `string` | `'─'` | Line character |
|
||||
| `padding` | `number` | `0` | Width reduction |
|
||||
| `title` | `string` | - | Centered title text |
|
||||
|
||||
## Byline
|
||||
|
||||
Footer with middot-separated items.
|
||||
|
||||
```tsx
|
||||
import { Byline } from '@anthropic/ink'
|
||||
|
||||
<Byline>
|
||||
<KeyboardShortcutHint shortcut="Enter" action="confirm" />
|
||||
<KeyboardShortcutHint shortcut="Esc" action="cancel" />
|
||||
</Byline>
|
||||
```
|
||||
|
||||
## KeyboardShortcutHint
|
||||
|
||||
Display a keyboard shortcut with its action.
|
||||
|
||||
```tsx
|
||||
import { KeyboardShortcutHint } from '@anthropic/ink'
|
||||
|
||||
<KeyboardShortcutHint shortcut="Enter" action="confirm" />
|
||||
<KeyboardShortcutHint shortcut="↑/↓" action="navigate" parens />
|
||||
```
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `shortcut` | `string` | - | Key or chord to display |
|
||||
| `action` | `string` | - | Action description |
|
||||
| `parens` | `boolean` | `false` | Wrap in parentheses |
|
||||
| `bold` | `boolean` | `false` | Bold shortcut text |
|
||||
|
||||
## ConfigurableShortcutHint
|
||||
|
||||
Displays a shortcut hint that reads the actual keybinding from config.
|
||||
|
||||
```tsx
|
||||
import { ConfigurableShortcutHint } from '@anthropic/ink'
|
||||
|
||||
<ConfigurableShortcutHint
|
||||
action="confirm:no"
|
||||
context="Confirmation"
|
||||
fallback="Esc"
|
||||
description="cancel"
|
||||
/>
|
||||
```
|
||||
|
||||
| Prop | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `action` | `string` | Keybinding action name |
|
||||
| `context` | `string` | Keybinding context |
|
||||
| `fallback` | `string` | Default shortcut if unbound |
|
||||
| `description` | `string` | Action description |
|
||||
| `parens` | `boolean` | Wrap in parentheses |
|
||||
| `bold` | `boolean` | Bold shortcut text |
|
||||
|
||||
## Ratchet
|
||||
|
||||
Animated counter component that prevents layout jumps.
|
||||
|
||||
```tsx
|
||||
import { Ratchet } from '@anthropic/ink'
|
||||
|
||||
<Ratchet lock="always">
|
||||
<Text>{count}</Text>
|
||||
</Ratchet>
|
||||
```
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|------|------|---------|-------------|
|
||||
| `children` | `ReactNode` | - | Content |
|
||||
| `lock` | `'always' \| 'offscreen'` | `'always'` | Width locking strategy. `'always'` locks always; `'offscreen'` only locks when the element is scrolled off-screen |
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user