Compare commits

..

15 Commits

Author SHA1 Message Date
unraid
bddffa216a feat: integrate 5 feature branches, upstream fixes, and MIME detection fix
Squashed 5 commits:

Features (from 5 feature branches):
- MCP fix, pipe mute, stub recovery
- KAIROS activation, openclaw autonomy
- Daemon/job command hierarchy + cross-platform bg engine

Upstream fixes:
- fix: Bun.hash compatibility
- chore: chrome dependency update
- docs: browser support guide

MIME detection fix:
- Screenshot detectMimeFromBase64(): decode raw bytes from base64
  instead of broken charCodeAt comparison
- Fixes API 400 on Windows (JPEG) and macOS (PNG) screenshots
2026-04-14 18:32:19 +08:00
claude-code-best
be80da4ce0 fix: 修复缓存 2026-04-13 20:09:23 +08:00
claude-code-best
fce40fed1f feat: 加上 userId 的传递 2026-04-13 19:04:51 +08:00
claude-code-best
a7e03a5b30 fix: 修复 interrupt 日志不上传 2026-04-13 18:12:23 +08:00
claude-code-best
05cabbbd73 feat: langfuse 工具调用显示为嵌套结构 2026-04-13 18:09:12 +08:00
claude-code-best
d4b30d32c3 fix: 修复 chrome 链接版本 2026-04-13 17:30:47 +08:00
claude-code-best
e0484e2817 fix: 使用简化版本的 chrome 桥接器 2026-04-13 16:03:47 +08:00
claude-code-best
2fb1c9dcd8 feat: 工具层及 mcp 大重构 (#252)
* feat: 第一版大重构

* fix: 修复类型问题

* chore: 更新版本到 1.3.2

* Add brave as alternative WebSearchTool

* fix: 修正顺序

* fix: 修复对穷鬼模式的 auto dream 和 session memory 越过

* feat: 穷鬼模式去除 session-summary

* feat: 创建 builtin-tools 包,搬运所有工具实现

将 src/tools/ 下的全部 60 个工具目录迁移至 packages/builtin-tools/src/tools/,
内部导入路径已更新为 src/ alias 模式。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: 更新 src/ 中所有工具引用至 builtin-tools 包,删除 src/tools/

- src/tools.ts 及 178 个 src/ 文件的 import 路径从 ./tools/ 改为 builtin-tools/tools/
- 删除 src/tools/ 整个目录(已迁移至 packages/builtin-tools/)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: 添加 builtin-tools 路径别名至 tsconfig,更新 bun.lock

- tsconfig.json 新增 builtin-tools/* 和 builtin-tools 路径映射
- 新增 packages/builtin-tools/src 至 include

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: 为 builtin-tools、mcp-client、agent-tools 添加 @claude-code-best 作用域前缀

所有包名及 import 路径统一添加 @claude-code-best/ 前缀:
- builtin-tools → @claude-code-best/builtin-tools
- mcp-client → @claude-code-best/mcp-client
- agent-tools → @claude-code-best/agent-tools

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: 修复 node 环境没有 bun 的问题

---------

Co-authored-by: Eric-Guo <eric.guocz@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 09:52:05 +08:00
claude-code-best
bbb8b613a9 docs: update contributors 2026-04-13 01:51:00 +00:00
claude-code-best
c63b875ae3 chore: 1.3.3 版本 2026-04-13 09:50:38 +08:00
claude-code-best
9b8503d13d fix: 修复 node 环境没有 bun 的问题 2026-04-13 09:47:33 +08:00
claude-code-best
3cf94fbda0 fix: 修复对穷鬼模式的 auto dream 和 session memory 越过 2026-04-12 23:24:12 +08:00
claude-code-best
9a3081dff6 Merge branch 'pr/Eric-Guo/245' 2026-04-12 23:12:23 +08:00
claude-code-best
bd6448ecda fix: 修正顺序 2026-04-12 23:12:09 +08:00
Eric-Guo
711440474c Add brave as alternative WebSearchTool 2026-04-12 22:23:11 +08:00
653 changed files with 20100 additions and 3411 deletions

2
.gitignore vendored
View File

@@ -15,7 +15,7 @@ src/utils/vendor/
.claude/
.codex/
.omx/
.docs/task/
# Binary / screenshot files (root only)
/*.png
*.bmp

204
02-kairos (1).md Normal file
View File

@@ -0,0 +1,204 @@
# KAIROS — 永不关机的 Claude
> 源码位置:`src/assistant/`、`src/proactive/`、`src/services/autoDream/`
> 编译开关:`feature('KAIROS')`、`feature('KAIROS_BRIEF')`、`feature('KAIROS_CHANNELS')`
> 远程开关GrowthBook `tengu_kairos`
关掉终端 Claude 还在运行的持久助手模式。KAIROS 是 Claude Code 中最复杂的隐藏功能之一。
---
## 核心概念
KAIROS 让 Claude 从"一次性对话工具"变成"持久运行的 AI 助手"
- 关闭终端后 Claude 仍在后台运行
- 每天自动写日志
- 晚上自动"做梦"整理记忆
- 没人说话时自己找活干
- 命令超 15 秒自动丢后台
---
## 激活流程
定义在 `src/main.tsx`(约第 1054-1092 行),需要通过五层检查:
```
1. feature('KAIROS') ← 编译时 flag
2. settings.assistant: true ← .claude/settings.json
3. 目录信任状态检查 ← 防恶意仓库劫持
4. tengu_kairos ← GrowthBook 远程开关
5. setKairosActive(true) ← 全局状态激活
```
`--assistant` CLI 参数可跳过远程开关检查(用于 Agent SDK daemon 模式)。
全局状态存储在 `src/bootstrap/state.ts`
- `kairosActive: boolean`(默认 `false`
- `getKairosActive()` / `setKairosActive(true)`
---
## 跨会话持久运行
### 会话恢复
`src/utils/conversationRecovery.ts` 中使用 `feature('KAIROS')` 条件导入 `BriefTool``SendUserFileTool`。在反序列化会话时识别这些工具的结果为"终端工具结果",判断 turn 是正常完成还是被中断。
### 持久 Cron 任务
关键在 `.claude/scheduled_tasks.json`。标记为 `permanent: true` 的任务不受 7 天自动过期限制:
- `catch-up`:恢复中断的工作
- `morning-checkin`:每日早间签到
- `dream`:记忆整合
### 会话历史 API
`src/assistant/sessionHistory.ts` 通过 OAuth API 加载远程会话历史,使用 `v1/sessions/{sessionId}/events` 端点,支持分页拉取。
---
## 做梦机制Dream
KAIROS 最精巧的子系统——后台运行的子代理,将分散的会话记忆整合为持久的结构化知识。
### 触发条件(三层门控,由廉到贵)
定义在 `src/services/autoDream/autoDream.ts`
```
1. 时间门控:距上次整合超过 24 小时minHours
2. 会话门控:至少 5 个新会话minSessions
3. 锁门控:没有其他进程正在整合
```
阈值通过 GrowthBook `tengu_onyx_plover` 远程配置动态控制。
### 四阶段整合流程
定义在 `src/services/autoDream/consolidationPrompt.ts`
| 阶段 | 动作 |
|------|------|
| **Orient** | 列出记忆目录、读取 `MEMORY.md` 索引、浏览已有主题文件 |
| **Gather** | 从每日日志、已有记忆、JSONL transcript 中搜集新信号 |
| **Consolidate** | 合并新信号到主题文件,转换相对日期为绝对日期,删除过时事实 |
| **Prune** | 更新 `MEMORY.md` 索引,保持在行数和大小限制内 |
### 锁机制
`src/services/autoDream/consolidationLock.ts`
- 使用 `.consolidate-lock` 文件
- 文件 mtime = `lastConsolidatedAt`
- 文件内容 = 持有者 PID
- 支持 PID 存活检查1 小时超时)
- double-write 后 re-read 验证防竞争
### 每日日志
路径由 `src/memdir/paths.ts``getAutoMemDailyLogPath()` 计算:
```
<autoMemPath>/logs/YYYY/MM/YYYY-MM-DD.md
```
### UI 呈现
- Footer pill 标签显示 **"dreaming"**
- `src/components/tasks/DreamDetailDialog.tsx` 提供专门的详情对话框
- 支持查看实时进度和手动中止
- `Shift+Down` 打开后台任务对话框
---
## 主动模式Proactive Mode
没人说话时 Claude 自己找活干。
### 核心状态
`src/proactive/index.ts` 维护三个状态:
| 状态 | 说明 |
|------|------|
| `active` | 是否激活 |
| `paused` | 是否暂停(用户按 Esc 取消时暂停,下次输入恢复) |
| `contextBlocked` | API 错误时阻塞 tick防止 tick-error-tick 死循环 |
### 激活方式
- `--proactive` CLI 参数
- `CLAUDE_CODE_PROACTIVE` 环境变量
-`feature('PROACTIVE') || feature('KAIROS')` 保护
### 系统提示
激活后追加:
```
# Proactive Mode
You are in proactive mode. Take initiative -- explore, act, and make progress
without waiting for instructions.
Start by briefly greeting the user.
You will receive periodic <tick> prompts. These are check-ins. Do whatever
seems most useful, or call Sleep if there's nothing to do.
```
### SleepTool 集成
设置中的 `minSleepDurationMs``maxSleepDurationMs` 控制 Sleep 持续时间范围,节流 proactive tick 频率。没活干就 Sleep 等着。
---
## 后台任务管理
### Cron 调度器
`src/utils/cronScheduler.ts`
- 每 1 秒 tick 一次(`CHECK_INTERVAL_MS = 1000`
- 使用 chokidar 监视 `.claude/scheduled_tasks.json`
- 支持调度器锁(`src/utils/cronTasksLock.ts`),防止多实例重复触发
- 锁探测间隔 5 秒,持有者崩溃时自动接管
### 任务类型
| 类型 | 说明 |
|------|------|
| 一次性(`recurring: false` | 触发后自动删除,支持错过任务检测 |
| 循环(`recurring: true` | 触发后重新调度,默认 7 天过期 |
| 永久(`permanent: true` | 不受过期限制KAIROS 专用) |
| 会话级(`durable: false` | 仅内存中,进程退出即消失 |
### Jitter 防雷群机制
`src/utils/cronJitterConfig.ts`
- 循环任务:基于 taskId 的确定性延迟interval 的 10%,上限 15 分钟)
- 一次性任务:在 :00 和 :30 施加最多 90 秒提前量
- 运维可在事故期间推送配置变更60 秒内全客户端生效
---
## 关键源码文件
| 文件 | 职责 |
|------|------|
| `src/bootstrap/state.ts` | KAIROS 全局状态 |
| `src/assistant/index.ts` | 助手模式入口 |
| `src/assistant/sessionHistory.ts` | 远程会话历史 API |
| `src/proactive/index.ts` | 主动模式状态管理 |
| `src/services/autoDream/autoDream.ts` | Auto-Dream 引擎 |
| `src/services/autoDream/consolidationPrompt.ts` | 整合提示(四阶段) |
| `src/services/autoDream/consolidationLock.ts` | 整合锁 |
| `src/services/autoDream/config.ts` | Dream 配置 |
| `src/tasks/DreamTask/DreamTask.ts` | Dream 任务定义 |
| `src/utils/cronScheduler.ts` | Cron 调度器 |
| `src/utils/cronTasks.ts` | Cron 任务持久化 |
| `src/skills/bundled/dream.ts` | `/dream` Skill存根 |

View File

@@ -22,8 +22,10 @@
| 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) |
| 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) |
| Computer Use | 屏幕截图、键鼠控制 | [文档](https://ccb.agent-aura.top/docs/features/computer-use) |
| Chrome Use | 浏览器自动化、表单填写、数据抓取 | [魔改版](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) |
| Langfuse 监控 | LLM 调用/工具执行/多 Agent 全链路追踪 | [文档](https://ccb.agent-aura.top/docs/features/langfuse-monitoring) |
| Poor Mode | 穷鬼模式,关闭记忆提取和键入建议 | /poor 可以开关 |

View File

@@ -40,6 +40,8 @@ const DEFAULT_BUILD_FEATURES = [
'KAIROS',
'COORDINATOR_MODE',
'LAN_PIPES',
'BG_SESSIONS',
'TEMPLATES',
// 'REVIEW_ARTIFACT', // API 请求无响应,需进一步排查 schema 兼容性
// P3: poor mode (disable extract_memories + prompt_suggestion)
'POOR',
@@ -118,7 +120,47 @@ 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')
// Node.js entry needs a Bun API polyfill because Bun.build({ target: 'bun' })
// emits globalThis.Bun references (e.g. Bun.$ shell tag in computer-use-input,
// Bun.which in chunk-ys6smqg9) that crash at import time under plain Node.js.
const NODE_BUN_POLYFILL = `#!/usr/bin/env node
// Bun API polyfill for Node.js runtime
if (typeof globalThis.Bun === "undefined") {
const { execFileSync } = await import("child_process");
const { resolve, delimiter } = await import("path");
const { accessSync, constants: { X_OK } } = await import("fs");
function which(bin) {
const isWin = process.platform === "win32";
const pathExt = isWin ? (process.env.PATHEXT || ".EXE").split(";") : [""];
for (const dir of (process.env.PATH || "").split(delimiter)) {
for (const ext of pathExt) {
const candidate = resolve(dir, bin + ext);
try { accessSync(candidate, X_OK); return candidate; } catch {}
}
}
return null;
}
// Bun.$ is the shell template tag (e.g. $\`osascript ...\`). Only used by
// computer-use-input/darwin — stub it so the top-level destructuring
// \`var { $ } = globalThis.Bun\` doesn't crash.
function $(parts, ...args) {
throw new Error("Bun.$ shell API is not available in Node.js. Use Bun runtime for this feature.");
}
function hash(data, seed) {
let h = ((seed || 0) ^ 0x811c9dc5) >>> 0;
for (let i = 0; i < data.length; i++) {
h ^= data.charCodeAt(i);
h = Math.imul(h, 0x01000193) >>> 0;
}
return h;
}
globalThis.Bun = { which, $, hash };
}
import "./cli.js"
`
await writeFile(cliNode, NODE_BUN_POLYFILL)
// NOTE: when new Bun-specific globals appear in bundled output, add them here.
// Make both executable
const { chmodSync } = await import('fs')

215
bun.lock
View File

@@ -5,7 +5,7 @@
"": {
"name": "claude-code-best",
"dependencies": {
"mcp-chrome-bridge": "^1.0.31",
"@claude-code-best/mcp-chrome-bridge": "^2.0.7",
},
"devDependencies": {
"@alcalzone/ansi-tokenize": "^0.3.0",
@@ -28,6 +28,9 @@
"@aws-sdk/credential-providers": "^3.1020.0",
"@azure/identity": "^4.13.1",
"@biomejs/biome": "^2.4.10",
"@claude-code-best/agent-tools": "workspace:*",
"@claude-code-best/builtin-tools": "workspace:*",
"@claude-code-best/mcp-client": "workspace:*",
"@commander-js/extra-typings": "^14.0.0",
"@growthbook/growthbook": "^1.6.5",
"@langfuse/otel": "^5.1.0",
@@ -176,10 +179,24 @@
"wrap-ansi": "^10.0.0",
},
},
"packages/agent-tools": {
"name": "@claude-code-best/agent-tools",
"version": "1.0.0",
"dependencies": {
"zod": "^3.25.0",
},
},
"packages/audio-capture-napi": {
"name": "audio-capture-napi",
"version": "1.0.0",
},
"packages/builtin-tools": {
"name": "@claude-code-best/builtin-tools",
"version": "1.0.0",
"dependencies": {
"@claude-code-best/agent-tools": "workspace:*",
},
},
"packages/color-diff-napi": {
"name": "color-diff-napi",
"version": "1.0.0",
@@ -194,6 +211,18 @@
"sharp": "^0.33.5",
},
},
"packages/mcp-client": {
"name": "@claude-code-best/mcp-client",
"version": "1.0.0",
"dependencies": {
"@claude-code-best/agent-tools": "workspace:*",
"@modelcontextprotocol/sdk": "^1.29.0",
"lodash-es": "^4.17.21",
"lru-cache": "^10.0.0",
"p-map": "^4.0.0",
"zod": "^3.25.0",
},
},
"packages/modifiers-napi": {
"name": "modifiers-napi",
"version": "1.0.0",
@@ -410,6 +439,14 @@
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.11", "https://registry.npmmirror.com/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.11.tgz", { "os": "win32", "cpu": "x64" }, "sha512-A8D3JM/00C2KQgUV3oj8Ba15EHEYwebAGCy5Sf9GAjr5Y3+kJIYOiESoqRDeuRZueuMdCsbLZIUqmPhpYXJE9A=="],
"@claude-code-best/agent-tools": ["@claude-code-best/agent-tools@workspace:packages/agent-tools"],
"@claude-code-best/builtin-tools": ["@claude-code-best/builtin-tools@workspace:packages/builtin-tools"],
"@claude-code-best/mcp-chrome-bridge": ["@claude-code-best/mcp-chrome-bridge@2.0.7", "", { "dependencies": { "@fastify/cors": "^11.0.1", "@modelcontextprotocol/sdk": "^1.11.0", "chalk": "^5.4.1", "chrome-mcp-shared": "^1.0.2", "commander": "^13.1.0", "fastify": "^5.3.2", "is-admin": "^4.0.0", "pino": "^9.6.0", "uuid": "^11.1.0" }, "bin": { "mcp-chrome-bridge": "dist/cli.js", "mcp-chrome-stdio": "dist/mcp/mcp-server-stdio.js" } }, "sha512-gb64+Ga6li3A8Ll9NKV+ePBn5/U0fccCdrH43tGYveLKZIZxURz8cbY+Z3BdbTdYSPVdFXtfUlp3TMxu4OT5gg=="],
"@claude-code-best/mcp-client": ["@claude-code-best/mcp-client@workspace:packages/mcp-client"],
"@commander-js/extra-typings": ["@commander-js/extra-typings@14.0.0", "https://registry.npmmirror.com/@commander-js/extra-typings/-/extra-typings-14.0.0.tgz", { "peerDependencies": { "commander": "~14.0.0" } }, "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg=="],
"@emnapi/core": ["@emnapi/core@1.9.2", "https://registry.npmmirror.com/@emnapi/core/-/core-1.9.2.tgz", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
@@ -470,21 +507,21 @@
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
"@fastify/ajv-compiler": ["@fastify/ajv-compiler@4.0.5", "https://registry.npmmirror.com/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz", { "dependencies": { "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "fast-uri": "^3.0.0" } }, "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A=="],
"@fastify/ajv-compiler": ["@fastify/ajv-compiler@4.0.5", "", { "dependencies": { "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "fast-uri": "^3.0.0" } }, "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A=="],
"@fastify/cors": ["@fastify/cors@11.2.0", "https://registry.npmmirror.com/@fastify/cors/-/cors-11.2.0.tgz", { "dependencies": { "fastify-plugin": "^5.0.0", "toad-cache": "^3.7.0" } }, "sha512-LbLHBuSAdGdSFZYTLVA3+Ch2t+sA6nq3Ejc6XLAKiQ6ViS2qFnvicpj0htsx03FyYeLs04HfRNBsz/a8SvbcUw=="],
"@fastify/cors": ["@fastify/cors@11.2.0", "", { "dependencies": { "fastify-plugin": "^5.0.0", "toad-cache": "^3.7.0" } }, "sha512-LbLHBuSAdGdSFZYTLVA3+Ch2t+sA6nq3Ejc6XLAKiQ6ViS2qFnvicpj0htsx03FyYeLs04HfRNBsz/a8SvbcUw=="],
"@fastify/error": ["@fastify/error@4.2.0", "https://registry.npmmirror.com/@fastify/error/-/error-4.2.0.tgz", {}, "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ=="],
"@fastify/error": ["@fastify/error@4.2.0", "", {}, "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ=="],
"@fastify/fast-json-stringify-compiler": ["@fastify/fast-json-stringify-compiler@5.0.3", "https://registry.npmmirror.com/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz", { "dependencies": { "fast-json-stringify": "^6.0.0" } }, "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ=="],
"@fastify/fast-json-stringify-compiler": ["@fastify/fast-json-stringify-compiler@5.0.3", "", { "dependencies": { "fast-json-stringify": "^6.0.0" } }, "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ=="],
"@fastify/forwarded": ["@fastify/forwarded@3.0.1", "https://registry.npmmirror.com/@fastify/forwarded/-/forwarded-3.0.1.tgz", {}, "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw=="],
"@fastify/forwarded": ["@fastify/forwarded@3.0.1", "", {}, "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw=="],
"@fastify/merge-json-schemas": ["@fastify/merge-json-schemas@0.2.1", "https://registry.npmmirror.com/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A=="],
"@fastify/merge-json-schemas": ["@fastify/merge-json-schemas@0.2.1", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A=="],
"@fastify/otel": ["@fastify/otel@0.18.0", "https://registry.npmmirror.com/@fastify/otel/-/otel-0.18.0.tgz", { "dependencies": { "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.212.0", "@opentelemetry/semantic-conventions": "^1.28.0", "minimatch": "^10.2.4" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0" } }, "sha512-3TASCATfw+ctICSb4ymrv7iCm0qJ0N9CarB+CZ7zIJ7KqNbwI5JjyDL1/sxoC0ccTO1Zyd1iQ+oqncPg5FJXaA=="],
"@fastify/proxy-addr": ["@fastify/proxy-addr@5.1.0", "https://registry.npmmirror.com/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz", { "dependencies": { "@fastify/forwarded": "^3.0.0", "ipaddr.js": "^2.1.0" } }, "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw=="],
"@fastify/proxy-addr": ["@fastify/proxy-addr@5.1.0", "", { "dependencies": { "@fastify/forwarded": "^3.0.0", "ipaddr.js": "^2.1.0" } }, "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw=="],
"@growthbook/growthbook": ["@growthbook/growthbook@1.6.5", "https://registry.npmmirror.com/@growthbook/growthbook/-/growthbook-1.6.5.tgz", { "dependencies": { "dom-mutator": "^0.6.0" } }, "sha512-mUaMsgeUTpRIUOTn33EUXHRK6j7pxBjwqH4WpQyq+pukjd1AIzWlEa6w7i6bInJUcweGgP2beXZmaP6b6UPn7A=="],
@@ -1018,8 +1055,6 @@
"@types/node": ["@types/node@25.6.0", "https://registry.npmmirror.com/@types/node/-/node-25.6.0.tgz", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="],
"@types/node-fetch": ["@types/node-fetch@2.6.13", "https://registry.npmmirror.com/@types/node-fetch/-/node-fetch-2.6.13.tgz", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
"@types/pg": ["@types/pg@8.15.6", "https://registry.npmmirror.com/@types/pg/-/pg-8.15.6.tgz", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" } }, "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ=="],
"@types/pg-pool": ["@types/pg-pool@2.0.7", "https://registry.npmmirror.com/@types/pg-pool/-/pg-pool-2.0.7.tgz", { "dependencies": { "@types/pg": "*" } }, "sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng=="],
@@ -1064,7 +1099,7 @@
"@xmldom/xmldom": ["@xmldom/xmldom@0.8.12", "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.12.tgz", {}, "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg=="],
"abstract-logging": ["abstract-logging@2.0.1", "https://registry.npmmirror.com/abstract-logging/-/abstract-logging-2.0.1.tgz", {}, "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="],
"abstract-logging": ["abstract-logging@2.0.1", "", {}, "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="],
"accepts": ["accepts@2.0.0", "https://registry.npmmirror.com/accepts/-/accepts-2.0.0.tgz", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
@@ -1074,6 +1109,8 @@
"agent-base": ["agent-base@8.0.0", "https://registry.npmmirror.com/agent-base/-/agent-base-8.0.0.tgz", {}, "sha512-QT8i0hCz6C/KQ+KTAbSNwCHDGdmUJl2tp2ZpNlGSWCfhUNVbYG2WLE3MdZGBAgXPV4GAvjGMxo+C1hroyxmZEg=="],
"aggregate-error": ["aggregate-error@3.1.0", "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
"ajv": ["ajv@8.18.0", "https://registry.npmmirror.com/ajv/-/ajv-8.18.0.tgz", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
"ajv-formats": ["ajv-formats@3.0.1", "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-3.0.1.tgz", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
@@ -1096,7 +1133,7 @@
"auto-bind": ["auto-bind@5.0.1", "https://registry.npmmirror.com/auto-bind/-/auto-bind-5.0.1.tgz", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="],
"avvio": ["avvio@9.2.0", "https://registry.npmmirror.com/avvio/-/avvio-9.2.0.tgz", { "dependencies": { "@fastify/error": "^4.0.0", "fastq": "^1.17.1" } }, "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ=="],
"avvio": ["avvio@9.2.0", "", { "dependencies": { "@fastify/error": "^4.0.0", "fastq": "^1.17.1" } }, "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ=="],
"axios": ["axios@1.15.0", "https://registry.npmmirror.com/axios/-/axios-1.15.0.tgz", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^2.1.0" } }, "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q=="],
@@ -1106,16 +1143,10 @@
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.18", "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.18.tgz", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A=="],
"better-sqlite3": ["better-sqlite3@11.10.0", "https://registry.npmmirror.com/better-sqlite3/-/better-sqlite3-11.10.0.tgz", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ=="],
"bidi-js": ["bidi-js@1.0.3", "https://registry.npmmirror.com/bidi-js/-/bidi-js-1.0.3.tgz", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="],
"bignumber.js": ["bignumber.js@9.3.1", "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.3.1.tgz", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
"bindings": ["bindings@1.5.0", "https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
"bl": ["bl@4.1.0", "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
"body-parser": ["body-parser@2.2.2", "https://registry.npmmirror.com/body-parser/-/body-parser-2.2.2.tgz", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
"bowser": ["bowser@2.14.1", "https://registry.npmmirror.com/bowser/-/bowser-2.14.1.tgz", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
@@ -1126,8 +1157,6 @@
"browserslist": ["browserslist@4.28.2", "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.2.tgz", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
"buffer": ["buffer@5.7.1", "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
"bun-types": ["bun-types@1.3.12", "https://registry.npmmirror.com/bun-types/-/bun-types-1.3.12.tgz", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
@@ -1152,14 +1181,12 @@
"chokidar": ["chokidar@5.0.0", "https://registry.npmmirror.com/chokidar/-/chokidar-5.0.0.tgz", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
"chownr": ["chownr@1.1.4", "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
"chrome-devtools-frontend": ["chrome-devtools-frontend@1.0.1611825", "https://registry.npmmirror.com/chrome-devtools-frontend/-/chrome-devtools-frontend-1.0.1611825.tgz", {}, "sha512-xp7EQPurkgJgYiSjIyLc3d7+BMevetrVeXHm5zEK0Zbr99/XjOlUzMnj18twLsrb/fYXYnMD4g5SjzcJkYATfQ=="],
"chrome-mcp-shared": ["chrome-mcp-shared@1.0.2", "https://registry.npmmirror.com/chrome-mcp-shared/-/chrome-mcp-shared-1.0.2.tgz", { "dependencies": { "@modelcontextprotocol/sdk": "^1.11.0", "zod": "^3.24.4" } }, "sha512-v+6HBmcgXrIfyVbkkrVgfFDzqOfDutI8yZM0yA8k7SiicqL1MfBoqnsOy5idYNvxyQymxCxXNuTmajn8xaGsgQ=="],
"cjs-module-lexer": ["cjs-module-lexer@2.2.0", "https://registry.npmmirror.com/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", {}, "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ=="],
"clean-stack": ["clean-stack@2.2.0", "https://registry.npmmirror.com/clean-stack/-/clean-stack-2.2.0.tgz", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="],
"cli-boxes": ["cli-boxes@4.0.1", "https://registry.npmmirror.com/cli-boxes/-/cli-boxes-4.0.1.tgz", {}, "sha512-5IOn+jcCEHEraYolBPs/sT4BxYCe2nHg374OPiItB1O96KZFseS2gthU4twyYzeDcFew4DaUM/xwc5BQf08JJw=="],
"cli-highlight": ["cli-highlight@2.1.11", "https://registry.npmmirror.com/cli-highlight/-/cli-highlight-2.1.11.tgz", { "dependencies": { "chalk": "^4.0.0", "highlight.js": "^10.7.1", "mz": "^2.4.0", "parse5": "^5.1.1", "parse5-htmlparser2-tree-adapter": "^6.0.0", "yargs": "^16.0.0" }, "bin": { "highlight": "bin/highlight" } }, "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg=="],
@@ -1210,10 +1237,6 @@
"decamelize": ["decamelize@1.2.0", "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
"decompress-response": ["decompress-response@6.0.0", "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
"deep-extend": ["deep-extend@0.6.0", "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
"default-browser": ["default-browser@5.5.0", "https://registry.npmmirror.com/default-browser/-/default-browser-5.5.0.tgz", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="],
"default-browser-id": ["default-browser-id@5.0.1", "https://registry.npmmirror.com/default-browser-id/-/default-browser-id-5.0.1.tgz", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="],
@@ -1224,7 +1247,7 @@
"depd": ["depd@2.0.0", "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
"dequal": ["dequal@2.0.3", "https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
"detect-libc": ["detect-libc@2.1.2", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
@@ -1234,8 +1257,6 @@
"dom-mutator": ["dom-mutator@0.6.0", "https://registry.npmmirror.com/dom-mutator/-/dom-mutator-0.6.0.tgz", {}, "sha512-iCt9o0aYfXMUkz/43ZOAUFQYotjGB+GNbYJiJdz4TgXkyToXbbRy5S6FbTp72lRBtfpUMwEc1KmpFEU4CZeoNg=="],
"drizzle-orm": ["drizzle-orm@0.38.4", "https://registry.npmmirror.com/drizzle-orm/-/drizzle-orm-0.38.4.tgz", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/react": ">=18", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "react": ">=18", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/react", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "knex", "kysely", "mysql2", "pg", "postgres", "react", "sql.js", "sqlite3"] }, "sha512-s7/5BpLKO+WJRHspvpqTydxFob8i1vo2rEx4pY6TGY7QSMuUfWUuzaY0DIpXCkgHOo37BaFC+SJQb99dDUXT3Q=="],
"dunder-proto": ["dunder-proto@1.0.1", "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
@@ -1248,8 +1269,6 @@
"encodeurl": ["encodeurl@2.0.0", "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
"end-of-stream": ["end-of-stream@1.4.5", "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
"enhanced-resolve": ["enhanced-resolve@5.20.1", "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="],
"env-paths": ["env-paths@4.0.0", "https://registry.npmmirror.com/env-paths/-/env-paths-4.0.0.tgz", { "dependencies": { "is-safe-filename": "^0.1.0" } }, "sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw=="],
@@ -1278,8 +1297,6 @@
"execa": ["execa@9.6.1", "https://registry.npmmirror.com/execa/-/execa-9.6.1.tgz", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="],
"expand-template": ["expand-template@2.0.3", "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
"express": ["express@5.2.1", "https://registry.npmmirror.com/express/-/express-5.2.1.tgz", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
"express-rate-limit": ["express-rate-limit@8.3.2", "https://registry.npmmirror.com/express-rate-limit/-/express-rate-limit-8.3.2.tgz", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg=="],
@@ -1288,15 +1305,15 @@
"external-editor": ["external-editor@3.1.0", "https://registry.npmmirror.com/external-editor/-/external-editor-3.1.0.tgz", { "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew=="],
"fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "https://registry.npmmirror.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
"fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-glob": ["fast-glob@3.3.3", "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
"fast-json-stringify": ["fast-json-stringify@6.3.0", "https://registry.npmmirror.com/fast-json-stringify/-/fast-json-stringify-6.3.0.tgz", { "dependencies": { "@fastify/merge-json-schemas": "^0.2.0", "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "fast-uri": "^3.0.0", "json-schema-ref-resolver": "^3.0.0", "rfdc": "^1.2.0" } }, "sha512-oRCntNDY/329HJPlmdNLIdogNtt6Vyjb1WuT01Soss3slIdyUp8kAcDU3saQTOquEK8KFVfwIIF7FebxUAu+yA=="],
"fast-json-stringify": ["fast-json-stringify@6.3.0", "", { "dependencies": { "@fastify/merge-json-schemas": "^0.2.0", "ajv": "^8.12.0", "ajv-formats": "^3.0.1", "fast-uri": "^3.0.0", "json-schema-ref-resolver": "^3.0.0", "rfdc": "^1.2.0" } }, "sha512-oRCntNDY/329HJPlmdNLIdogNtt6Vyjb1WuT01Soss3slIdyUp8kAcDU3saQTOquEK8KFVfwIIF7FebxUAu+yA=="],
"fast-querystring": ["fast-querystring@1.1.2", "https://registry.npmmirror.com/fast-querystring/-/fast-querystring-1.1.2.tgz", { "dependencies": { "fast-decode-uri-component": "^1.0.1" } }, "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg=="],
"fast-querystring": ["fast-querystring@1.1.2", "", { "dependencies": { "fast-decode-uri-component": "^1.0.1" } }, "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg=="],
"fast-uri": ["fast-uri@3.1.0", "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.0.tgz", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
@@ -1304,9 +1321,9 @@
"fast-xml-parser": ["fast-xml-parser@5.5.8", "https://registry.npmmirror.com/fast-xml-parser/-/fast-xml-parser-5.5.8.tgz", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
"fastify": ["fastify@5.8.4", "https://registry.npmmirror.com/fastify/-/fastify-5.8.4.tgz", { "dependencies": { "@fastify/ajv-compiler": "^4.0.5", "@fastify/error": "^4.0.0", "@fastify/fast-json-stringify-compiler": "^5.0.0", "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", "avvio": "^9.0.0", "fast-json-stringify": "^6.0.0", "find-my-way": "^9.0.0", "light-my-request": "^6.0.0", "pino": "^9.14.0 || ^10.1.0", "process-warning": "^5.0.0", "rfdc": "^1.3.1", "secure-json-parse": "^4.0.0", "semver": "^7.6.0", "toad-cache": "^3.7.0" } }, "sha512-sa42J1xylbBAYUWALSBoyXKPDUvM3OoNOibIefA+Oha57FryXKKCZarA1iDntOCWp3O35voZLuDg2mdODXtPzQ=="],
"fastify": ["fastify@5.8.4", "", { "dependencies": { "@fastify/ajv-compiler": "^4.0.5", "@fastify/error": "^4.0.0", "@fastify/fast-json-stringify-compiler": "^5.0.0", "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", "avvio": "^9.0.0", "fast-json-stringify": "^6.0.0", "find-my-way": "^9.0.0", "light-my-request": "^6.0.0", "pino": "^9.14.0 || ^10.1.0", "process-warning": "^5.0.0", "rfdc": "^1.3.1", "secure-json-parse": "^4.0.0", "semver": "^7.6.0", "toad-cache": "^3.7.0" } }, "sha512-sa42J1xylbBAYUWALSBoyXKPDUvM3OoNOibIefA+Oha57FryXKKCZarA1iDntOCWp3O35voZLuDg2mdODXtPzQ=="],
"fastify-plugin": ["fastify-plugin@5.1.0", "https://registry.npmmirror.com/fastify-plugin/-/fastify-plugin-5.1.0.tgz", {}, "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw=="],
"fastify-plugin": ["fastify-plugin@5.1.0", "", {}, "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw=="],
"fastq": ["fastq@1.20.1", "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
@@ -1320,13 +1337,11 @@
"figures": ["figures@6.1.0", "https://registry.npmmirror.com/figures/-/figures-6.1.0.tgz", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
"file-uri-to-path": ["file-uri-to-path@1.0.0", "https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
"fill-range": ["fill-range@7.1.1", "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
"finalhandler": ["finalhandler@2.1.1", "https://registry.npmmirror.com/finalhandler/-/finalhandler-2.1.1.tgz", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
"find-my-way": ["find-my-way@9.5.0", "https://registry.npmmirror.com/find-my-way/-/find-my-way-9.5.0.tgz", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-querystring": "^1.0.0", "safe-regex2": "^5.0.0" } }, "sha512-VW2RfnmscZO5KgBY5XVyKREMW5nMZcxDy+buTOsL+zIPnBlbKm+00sgzoQzq1EVh4aALZLfKdwv6atBGcjvjrQ=="],
"find-my-way": ["find-my-way@9.5.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-querystring": "^1.0.0", "safe-regex2": "^5.0.0" } }, "sha512-VW2RfnmscZO5KgBY5XVyKREMW5nMZcxDy+buTOsL+zIPnBlbKm+00sgzoQzq1EVh4aALZLfKdwv6atBGcjvjrQ=="],
"find-up": ["find-up@4.1.0", "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
@@ -1346,8 +1361,6 @@
"fresh": ["fresh@2.0.0", "https://registry.npmmirror.com/fresh/-/fresh-2.0.0.tgz", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
"fs-constants": ["fs-constants@1.0.0", "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
"fs-extra": ["fs-extra@10.1.0", "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="],
"fs-minipass": ["fs-minipass@3.0.3", "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-3.0.3.tgz", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw=="],
@@ -1378,8 +1391,6 @@
"get-tsconfig": ["get-tsconfig@4.13.7", "https://registry.npmmirror.com/get-tsconfig/-/get-tsconfig-4.13.7.tgz", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q=="],
"github-from-package": ["github-from-package@0.0.0", "https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="],
"glob": ["glob@13.0.6", "https://registry.npmmirror.com/glob/-/glob-13.0.6.tgz", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="],
"glob-parent": ["glob-parent@5.1.2", "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
@@ -1418,8 +1429,6 @@
"iconv-lite": ["iconv-lite@0.7.2", "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.2.tgz", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
"ieee754": ["ieee754@1.2.1", "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"ignore": ["ignore@7.0.5", "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"image-processor-napi": ["image-processor-napi@workspace:packages/image-processor-napi"],
@@ -1430,8 +1439,6 @@
"inherits": ["inherits@2.0.4", "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"ini": ["ini@1.3.8", "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
"ip-address": ["ip-address@10.1.0", "https://registry.npmmirror.com/ip-address/-/ip-address-10.1.0.tgz", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="],
"ipaddr.js": ["ipaddr.js@1.9.1", "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
@@ -1476,7 +1483,7 @@
"json-bigint": ["json-bigint@1.0.0", "https://registry.npmmirror.com/json-bigint/-/json-bigint-1.0.0.tgz", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
"json-schema-ref-resolver": ["json-schema-ref-resolver@3.0.0", "https://registry.npmmirror.com/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A=="],
"json-schema-ref-resolver": ["json-schema-ref-resolver@3.0.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A=="],
"json-schema-to-ts": ["json-schema-to-ts@3.1.1", "https://registry.npmmirror.com/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="],
@@ -1498,7 +1505,7 @@
"knip": ["knip@6.4.0", "https://registry.npmmirror.com/knip/-/knip-6.4.0.tgz", { "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", "formatly": "^0.3.0", "get-tsconfig": "4.13.7", "jiti": "^2.6.0", "minimist": "^1.2.8", "oxc-parser": "^0.121.0", "oxc-resolver": "^11.19.1", "picocolors": "^1.1.1", "picomatch": "^4.0.1", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", "unbash": "^2.2.0", "yaml": "^2.8.2", "zod": "^4.1.11" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-SAEeggehgkPdoLZWVEcFKzPw+vNlnrUBDqcX8cOcHGydRInSn5pnn9LN3dDJ8SkDHKXR7xYzNq3HtRJaYmxOHg=="],
"light-my-request": ["light-my-request@6.6.0", "https://registry.npmmirror.com/light-my-request/-/light-my-request-6.6.0.tgz", { "dependencies": { "cookie": "^1.0.1", "process-warning": "^4.0.0", "set-cookie-parser": "^2.6.0" } }, "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A=="],
"light-my-request": ["light-my-request@6.6.0", "", { "dependencies": { "cookie": "^1.0.1", "process-warning": "^4.0.0", "set-cookie-parser": "^2.6.0" } }, "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A=="],
"lightningcss": ["lightningcss@1.32.0", "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.32.0.tgz", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
@@ -1556,8 +1563,6 @@
"math-intrinsics": ["math-intrinsics@1.1.0", "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"mcp-chrome-bridge": ["mcp-chrome-bridge@1.0.31", "https://registry.npmmirror.com/mcp-chrome-bridge/-/mcp-chrome-bridge-1.0.31.tgz", { "dependencies": { "@anthropic-ai/claude-agent-sdk": "^0.1.69", "@fastify/cors": "^11.0.1", "@modelcontextprotocol/sdk": "^1.11.0", "@types/node-fetch": "2", "better-sqlite3": "^11.6.0", "chalk": "^5.4.1", "chrome-devtools-frontend": "^1.0.1299282", "chrome-mcp-shared": "1.0.2", "commander": "^13.1.0", "drizzle-orm": "^0.38.2", "fastify": "^5.3.2", "is-admin": "^4.0.0", "node-fetch": "2", "pino": "^9.6.0", "uuid": "^11.1.0" }, "bin": { "mcp-chrome-bridge": "dist/cli.js", "chrome-mcp-bridge": "dist/cli.js", "mcp-chrome-stdio": "dist/mcp/mcp-server-stdio.js" } }, "sha512-bcl4POvdXhf9PX0+EIJ9guR+n6oVPNfbSBnhwf0LVg9MWwMJYpdvLszUT77NG2gBJCJF+JV/+CNz5xHnt9GwFg=="],
"media-typer": ["media-typer@1.1.0", "https://registry.npmmirror.com/media-typer/-/media-typer-1.1.0.tgz", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
"merge-descriptors": ["merge-descriptors@2.0.0", "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
@@ -1574,8 +1579,6 @@
"mimic-fn": ["mimic-fn@2.1.0", "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
"mimic-response": ["mimic-response@3.1.0", "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
"minimatch": ["minimatch@10.2.5", "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.5.tgz", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
"minimist": ["minimist@1.2.8", "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
@@ -1588,8 +1591,6 @@
"minipass-pipeline": ["minipass-pipeline@1.2.4", "https://registry.npmmirror.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="],
"mkdirp-classic": ["mkdirp-classic@0.5.3", "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
"modifiers-napi": ["modifiers-napi@workspace:packages/modifiers-napi"],
"module-details-from-path": ["module-details-from-path@1.0.4", "https://registry.npmmirror.com/module-details-from-path/-/module-details-from-path-1.0.4.tgz", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="],
@@ -1602,15 +1603,11 @@
"nanoid": ["nanoid@3.3.11", "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"napi-build-utils": ["napi-build-utils@2.0.0", "https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
"negotiator": ["negotiator@1.0.0", "https://registry.npmmirror.com/negotiator/-/negotiator-1.0.0.tgz", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
"node-abi": ["node-abi@3.89.0", "https://registry.npmmirror.com/node-abi/-/node-abi-3.89.0.tgz", { "dependencies": { "semver": "^7.3.5" } }, "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA=="],
"node-domexception": ["node-domexception@1.0.0", "https://registry.npmmirror.com/node-domexception/-/node-domexception-1.0.0.tgz", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
"node-fetch": ["node-fetch@2.7.0", "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"node-fetch": ["node-fetch@3.3.2", "https://registry.npmmirror.com/node-fetch/-/node-fetch-3.3.2.tgz", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
"node-forge": ["node-forge@1.4.0", "https://registry.npmmirror.com/node-forge/-/node-forge-1.4.0.tgz", {}, "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ=="],
@@ -1698,8 +1695,6 @@
"postgres-interval": ["postgres-interval@1.2.0", "https://registry.npmmirror.com/postgres-interval/-/postgres-interval-1.2.0.tgz", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
"prebuild-install": ["prebuild-install@7.1.3", "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.3.tgz", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
"pretty-bytes": ["pretty-bytes@5.6.0", "https://registry.npmmirror.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz", {}, "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg=="],
"pretty-ms": ["pretty-ms@9.3.0", "https://registry.npmmirror.com/pretty-ms/-/pretty-ms-9.3.0.tgz", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="],
@@ -1714,8 +1709,6 @@
"proxy-from-env": ["proxy-from-env@2.1.0", "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
"pump": ["pump@3.0.4", "https://registry.npmmirror.com/pump/-/pump-3.0.4.tgz", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="],
"qrcode": ["qrcode@1.5.4", "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="],
"qs": ["qs@6.15.1", "https://registry.npmmirror.com/qs/-/qs-6.15.1.tgz", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg=="],
@@ -1728,8 +1721,6 @@
"raw-body": ["raw-body@3.0.2", "https://registry.npmmirror.com/raw-body/-/raw-body-3.0.2.tgz", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
"rc": ["rc@1.2.8", "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
"react": ["react@19.2.5", "https://registry.npmmirror.com/react/-/react-19.2.5.tgz", {}, "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA=="],
"react-compiler-runtime": ["react-compiler-runtime@1.0.0", "https://registry.npmmirror.com/react-compiler-runtime/-/react-compiler-runtime-1.0.0.tgz", { "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental" } }, "sha512-rRfjYv66HlG8896yPUDONgKzG5BxZD1nV9U6rkm+7VCuvQc903C4MjcoZR4zPw53IKSOX9wMQVpA1IAbRtzQ7w=="],
@@ -1740,8 +1731,6 @@
"react-refresh": ["react-refresh@0.17.0", "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.17.0.tgz", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
"readable-stream": ["readable-stream@3.6.2", "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"readdirp": ["readdirp@5.0.0", "https://registry.npmmirror.com/readdirp/-/readdirp-5.0.0.tgz", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
"real-require": ["real-require@0.2.0", "https://registry.npmmirror.com/real-require/-/real-require-0.2.0.tgz", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="],
@@ -1756,13 +1745,13 @@
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
"ret": ["ret@0.5.0", "https://registry.npmmirror.com/ret/-/ret-0.5.0.tgz", {}, "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw=="],
"ret": ["ret@0.5.0", "", {}, "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw=="],
"retry": ["retry@0.12.0", "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
"reusify": ["reusify@1.1.0", "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
"rfdc": ["rfdc@1.4.1", "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
"rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
"rollup": ["rollup@4.60.1", "https://registry.npmmirror.com/rollup/-/rollup-4.60.1.tgz", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.1", "@rollup/rollup-android-arm64": "4.60.1", "@rollup/rollup-darwin-arm64": "4.60.1", "@rollup/rollup-darwin-x64": "4.60.1", "@rollup/rollup-freebsd-arm64": "4.60.1", "@rollup/rollup-freebsd-x64": "4.60.1", "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", "@rollup/rollup-linux-arm-musleabihf": "4.60.1", "@rollup/rollup-linux-arm64-gnu": "4.60.1", "@rollup/rollup-linux-arm64-musl": "4.60.1", "@rollup/rollup-linux-loong64-gnu": "4.60.1", "@rollup/rollup-linux-loong64-musl": "4.60.1", "@rollup/rollup-linux-ppc64-gnu": "4.60.1", "@rollup/rollup-linux-ppc64-musl": "4.60.1", "@rollup/rollup-linux-riscv64-gnu": "4.60.1", "@rollup/rollup-linux-riscv64-musl": "4.60.1", "@rollup/rollup-linux-s390x-gnu": "4.60.1", "@rollup/rollup-linux-x64-gnu": "4.60.1", "@rollup/rollup-linux-x64-musl": "4.60.1", "@rollup/rollup-openbsd-x64": "4.60.1", "@rollup/rollup-openharmony-arm64": "4.60.1", "@rollup/rollup-win32-arm64-msvc": "4.60.1", "@rollup/rollup-win32-ia32-msvc": "4.60.1", "@rollup/rollup-win32-x64-gnu": "4.60.1", "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w=="],
@@ -1774,7 +1763,7 @@
"safe-buffer": ["safe-buffer@5.2.1", "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"safe-regex2": ["safe-regex2@5.1.0", "https://registry.npmmirror.com/safe-regex2/-/safe-regex2-5.1.0.tgz", { "dependencies": { "ret": "~0.5.0" }, "bin": { "safe-regex2": "bin/safe-regex2.js" } }, "sha512-pNHAuBW7TrcleFHsxBr5QMi/Iyp0ENjUKz7GCcX1UO7cMh+NmVK6HxQckNL1tJp1XAJVjG6B8OKIPqodqj9rtw=="],
"safe-regex2": ["safe-regex2@5.1.0", "", { "dependencies": { "ret": "~0.5.0" }, "bin": { "safe-regex2": "bin/safe-regex2.js" } }, "sha512-pNHAuBW7TrcleFHsxBr5QMi/Iyp0ENjUKz7GCcX1UO7cMh+NmVK6HxQckNL1tJp1XAJVjG6B8OKIPqodqj9rtw=="],
"safe-stable-stringify": ["safe-stable-stringify@2.5.0", "https://registry.npmmirror.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="],
@@ -1782,7 +1771,7 @@
"scheduler": ["scheduler@0.27.0", "https://registry.npmmirror.com/scheduler/-/scheduler-0.27.0.tgz", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"secure-json-parse": ["secure-json-parse@4.1.0", "https://registry.npmmirror.com/secure-json-parse/-/secure-json-parse-4.1.0.tgz", {}, "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA=="],
"secure-json-parse": ["secure-json-parse@4.1.0", "", {}, "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA=="],
"semver": ["semver@7.7.4", "https://registry.npmmirror.com/semver/-/semver-7.7.4.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
@@ -1792,7 +1781,7 @@
"set-blocking": ["set-blocking@2.0.0", "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="],
"set-cookie-parser": ["set-cookie-parser@2.7.2", "https://registry.npmmirror.com/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="],
"set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="],
"setprototypeof": ["setprototypeof@1.2.0", "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
@@ -1814,10 +1803,6 @@
"signal-exit": ["signal-exit@4.1.0", "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"simple-concat": ["simple-concat@1.0.1", "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="],
"simple-get": ["simple-get@4.0.1", "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
"simple-swizzle": ["simple-swizzle@0.2.4", "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.4.tgz", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="],
"smol-toml": ["smol-toml@1.6.1", "https://registry.npmmirror.com/smol-toml/-/smol-toml-1.6.1.tgz", {}, "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg=="],
@@ -1836,8 +1821,6 @@
"string-width": ["string-width@8.2.0", "https://registry.npmmirror.com/string-width/-/string-width-8.2.0.tgz", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw=="],
"string_decoder": ["string_decoder@1.3.0", "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
"strip-ansi": ["strip-ansi@7.2.0", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.2.0.tgz", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
"strip-final-newline": ["strip-final-newline@4.0.0", "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
@@ -1856,10 +1839,6 @@
"tapable": ["tapable@2.3.2", "https://registry.npmmirror.com/tapable/-/tapable-2.3.2.tgz", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="],
"tar-fs": ["tar-fs@2.1.4", "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.4.tgz", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="],
"tar-stream": ["tar-stream@2.2.0", "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
"thenify": ["thenify@3.3.1", "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="],
"thenify-all": ["thenify-all@1.6.0", "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="],
@@ -1872,7 +1851,7 @@
"to-regex-range": ["to-regex-range@5.0.1", "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"toad-cache": ["toad-cache@3.7.0", "https://registry.npmmirror.com/toad-cache/-/toad-cache-3.7.0.tgz", {}, "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw=="],
"toad-cache": ["toad-cache@3.7.0", "", {}, "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw=="],
"toidentifier": ["toidentifier@1.0.1", "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
@@ -1884,8 +1863,6 @@
"tslib": ["tslib@2.8.1", "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"tunnel-agent": ["tunnel-agent@0.6.0", "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
"turndown": ["turndown@7.2.4", "https://registry.npmmirror.com/turndown/-/turndown-7.2.4.tgz", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ=="],
"type-fest": ["type-fest@5.5.0", "https://registry.npmmirror.com/type-fest/-/type-fest-5.5.0.tgz", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g=="],
@@ -1912,8 +1889,6 @@
"usehooks-ts": ["usehooks-ts@3.1.1", "https://registry.npmmirror.com/usehooks-ts/-/usehooks-ts-3.1.1.tgz", { "dependencies": { "lodash.debounce": "^4.0.8" }, "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA=="],
"util-deprecate": ["util-deprecate@1.0.2", "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"uuid": ["uuid@11.1.0", "https://registry.npmmirror.com/uuid/-/uuid-11.1.0.tgz", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
"vary": ["vary@1.1.2", "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
@@ -2142,9 +2117,17 @@
"@babel/helper-compilation-targets/semver": ["semver@6.3.1", "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"@claude-code-best/agent-tools/zod": ["zod@3.25.76", "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"@claude-code-best/mcp-client/lru-cache": ["lru-cache@10.4.3", "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"@claude-code-best/mcp-client/p-map": ["p-map@4.0.0", "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="],
"@claude-code-best/mcp-client/zod": ["zod@3.25.76", "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
"@fastify/otel/@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.212.0", "https://registry.npmmirror.com/@opentelemetry/instrumentation/-/instrumentation-0.212.0.tgz", { "dependencies": { "@opentelemetry/api-logs": "0.212.0", "import-in-the-middle": "^2.0.6", "require-in-the-middle": "^8.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IyXmpNnifNouMOe0I/gX7ENfv2ZCNdYTF0FpCsoBcpbIHzk81Ww9rQTYTnvghszCg7qGrIhNvWC8dhEifgX9Jg=="],
"@fastify/proxy-addr/ipaddr.js": ["ipaddr.js@2.3.0", "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-2.3.0.tgz", {}, "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg=="],
"@fastify/proxy-addr/ipaddr.js": ["ipaddr.js@2.3.0", "", {}, "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg=="],
"@grpc/proto-loader/yargs": ["yargs@17.7.2", "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
@@ -2282,6 +2265,8 @@
"@typespec/ts-http-runtime/https-proxy-agent": ["https-proxy-agent@7.0.6", "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
"aggregate-error/indent-string": ["indent-string@4.0.0", "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
"ansi-escapes/type-fest": ["type-fest@0.21.3", "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
"chrome-mcp-shared/zod": ["zod@3.25.76", "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
@@ -2302,8 +2287,6 @@
"gaxios/https-proxy-agent": ["https-proxy-agent@7.0.6", "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
"gaxios/node-fetch": ["node-fetch@3.3.2", "https://registry.npmmirror.com/node-fetch/-/node-fetch-3.3.2.tgz", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
"gtoken/gaxios": ["gaxios@6.7.1", "https://registry.npmmirror.com/gaxios/-/gaxios-6.7.1.tgz", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9", "uuid": "^9.0.1" } }, "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ=="],
"http-proxy-agent/agent-base": ["agent-base@7.1.4", "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
@@ -2312,11 +2295,9 @@
"is-admin/execa": ["execa@5.1.1", "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
"light-my-request/cookie": ["cookie@1.1.1", "https://registry.npmmirror.com/cookie/-/cookie-1.1.1.tgz", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
"light-my-request/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
"light-my-request/process-warning": ["process-warning@4.0.1", "https://registry.npmmirror.com/process-warning/-/process-warning-4.0.1.tgz", {}, "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.1.77", "https://registry.npmmirror.com/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.1.77.tgz", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-ZEjWQtkoB2MEY6K16DWMmF+8OhywAynH0m08V265cerbZ8xPD/2Ng2jPzbbO40mPeFSsMDJboShL+a3aObP0Jg=="],
"light-my-request/process-warning": ["process-warning@4.0.1", "", {}, "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q=="],
"micromatch/picomatch": ["picomatch@2.3.2", "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
@@ -2332,8 +2313,6 @@
"qrcode/yargs": ["yargs@15.4.1", "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="],
"rc/strip-json-comments": ["strip-json-comments@2.0.1", "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
"vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "https://registry.npmmirror.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
"xss/commander": ["commander@2.20.3", "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
@@ -2466,6 +2445,8 @@
"gtoken/gaxios/is-stream": ["is-stream@2.0.1", "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
"gtoken/gaxios/node-fetch": ["node-fetch@2.7.0", "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"gtoken/gaxios/uuid": ["uuid@9.0.1", "https://registry.npmmirror.com/uuid/-/uuid-9.0.1.tgz", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
"image-processor-napi/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "https://registry.npmmirror.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
@@ -2518,22 +2499,6 @@
"is-admin/execa/strip-final-newline": ["strip-final-newline@2.0.0", "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "https://registry.npmmirror.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "https://registry.npmmirror.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "https://registry.npmmirror.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "https://registry.npmmirror.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "https://registry.npmmirror.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "https://registry.npmmirror.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "https://registry.npmmirror.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "https://registry.npmmirror.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
"qrcode/yargs/cliui": ["cliui@6.0.0", "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
"qrcode/yargs/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
@@ -2552,6 +2517,8 @@
"@anthropic-ai/vertex-sdk/google-auth-library/gaxios/is-stream": ["is-stream@2.0.1", "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
"@anthropic-ai/vertex-sdk/google-auth-library/gaxios/node-fetch": ["node-fetch@2.7.0", "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"@anthropic-ai/vertex-sdk/google-auth-library/gaxios/uuid": ["uuid@9.0.1", "https://registry.npmmirror.com/uuid/-/uuid-9.0.1.tgz", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
"@anthropic-ai/vertex-sdk/google-auth-library/gcp-metadata/google-logging-utils": ["google-logging-utils@0.0.2", "https://registry.npmmirror.com/google-logging-utils/-/google-logging-utils-0.0.2.tgz", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
@@ -2614,20 +2581,6 @@
"gtoken/gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-darwin-arm64/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-darwin-x64/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-linux-arm/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-linux-arm64/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-linux-x64/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-linuxmusl-arm64/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="],
"mcp-chrome-bridge/@anthropic-ai/claude-agent-sdk/@img/sharp-linuxmusl-x64/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="],
"qrcode/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"qrcode/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],

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

View 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`,否则会报错。
### SSEServer-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 缓存避免重复提示。
### HTTPStreamable 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:` + 原始 URLunwrap 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 双向通知 |

View File

@@ -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
├── 创建 Transportstdio/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 传输的进程管理
@@ -112,9 +308,17 @@ timer.unref?.() // 不阻止进程退出
```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 |

View File

@@ -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 集成 |
@@ -121,12 +121,16 @@ 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}`
- **文件**:
- `src/tools/WebSearchTool/adapters/bingAdapter.ts`
- `src/tools/WebSearchTool/adapters/braveAdapter.ts`
另外还有 Domain Blocklist 查询:
- **端点**: `https://api.anthropic.com/api/web/domain_info?domain={domain}`
@@ -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 |

View File

@@ -0,0 +1,30 @@
# Chrome Use — 浏览器自动化快速指南
让 Claude Code 直接控制你的 Chrome 浏览器,用自然语言完成网页操作。
## 快速开始3 分钟)
### 第一步:安装 Chrome 扩展
1. 下载扩展https://github.com/hangwin/mcp-chrome/releases下载最新 zip
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

View 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](</e:/Source_code/Claude-code-bast/src/daemon/main.ts:1>)
[src/daemon/workerRegistry.ts](</e:/Source_code/Claude-code-bast/src/daemon/workerRegistry.ts:1>)
- `status` / `stop` 目前只是占位输出:
[src/daemon/main.ts](</e:/Source_code/Claude-code-bast/src/daemon/main.ts:49>)
- `/remote-control-server` 有自己的命令内 UI 状态,但只维护当前进程内的 `daemonProcess`,并不适合作为跨进程 CLI 管理基础:
[src/commands/remoteControlServer/remoteControlServer.tsx](</e:/Source_code/Claude-code-bast/src/commands/remoteControlServer/remoteControlServer.tsx:32>)
### 目标
-`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](</e:/Source_code/Claude-code-bast/src/daemon/main.ts:1>)
- 轻量修改 [src/commands/remoteControlServer/remoteControlServer.tsx](</e:/Source_code/Claude-code-bast/src/commands/remoteControlServer/remoteControlServer.tsx:32>),让 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](</e:/Source_code/Claude-code-bast/src/entrypoints/cli.tsx:218>)
- session registry 已有真实实现:
[src/utils/concurrentSessions.ts](</e:/Source_code/Claude-code-bast/src/utils/concurrentSessions.ts:1>)
- `exit` 在 bg session 内已会 `tmux detach-client`
[src/commands/exit/exit.tsx](</e:/Source_code/Claude-code-bast/src/commands/exit/exit.tsx:20>)
- 但 CLI handler 仍全空:
[src/cli/bg.ts](</e:/Source_code/Claude-code-bast/src/cli/bg.ts:1>)
- task summary 仍然是 stub
[src/utils/taskSummary.ts](</e:/Source_code/Claude-code-bast/src/utils/taskSummary.ts:1>)
### 目标
- 先把 `ps` / `logs` / `kill` 做成真正有用的 session 管理命令。
- 不在第一阶段就强行补完 `attach` / `--bg`
### Phase 2AMVP
- 实现 `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](</e:/Source_code/Claude-code-bast/src/cli/bg.ts:1>)
- 修改 [src/utils/concurrentSessions.ts](</e:/Source_code/Claude-code-bast/src/utils/concurrentSessions.ts:1>) 以便后续 attach/--bg 扩展
- 修改 [src/utils/taskSummary.ts](</e:/Source_code/Claude-code-bast/src/utils/taskSummary.ts:1>)
- 复用:
[src/utils/sessionStorage.ts](</e:/Source_code/Claude-code-bast/src/utils/sessionStorage.ts:3870>)
[src/utils/udsClient.ts](</e:/Source_code/Claude-code-bast/src/utils/udsClient.ts:1>)
### 验证
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](</e:/Source_code/Claude-code-bast/src/entrypoints/cli.tsx:249>)
- handler 是空的:
[src/cli/handlers/templateJobs.ts](</e:/Source_code/Claude-code-bast/src/cli/handlers/templateJobs.ts:1>)
- `markdownConfigLoader` 已把 `templates` 纳入配置目录:
[src/utils/markdownConfigLoader.ts](</e:/Source_code/Claude-code-bast/src/utils/markdownConfigLoader.ts:29>)
- `query / stopHooks` 已预留 job classifier 链路:
[src/query/stopHooks.ts](</e:/Source_code/Claude-code-bast/src/query/stopHooks.ts:103>)
- `jobs/classifier.ts` 仍是 stub
[src/jobs/classifier.ts](</e:/Source_code/Claude-code-bast/src/jobs/classifier.ts:1>)
### 目标
-`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](</e:/Source_code/Claude-code-bast/src/jobs/classifier.ts:1>)
- 让带 `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 4AMVP
- 只支持 `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”拆开恢复。

View File

@@ -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,9 +21,13 @@ 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 模块结构
@@ -37,8 +41,9 @@ WebSearchTool.call()
| 适配器工厂 | `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` | 真实网络请求验证 |
| Brave 适配器 | `src/tools/WebSearchTool/adapters/braveAdapter.ts` | Brave LLM Context API 适配与结果映射 |
| 单元测试 | `src/tools/WebSearchTool/__tests__/bingAdapter.test.ts`, `src/tools/WebSearchTool/__tests__/braveAdapter*.test.ts`, `src/tools/WebSearchTool/__tests__/adapterFactory.test.ts` | Bing / Brave 解析与工厂逻辑测试 |
| 集成测试 | `src/tools/WebSearchTool/__tests__/bingAdapter.integration.ts`, `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`
## 五、接口定义

View 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不处理多实例并发
## 依赖
无外部依赖,可独立实施。

View 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 状态管理可复用模式,但非硬性依赖)

View 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:249`)
- handler 是空的 (`src/cli/handlers/templateJobs.ts`)
- `markdownConfigLoader` 已把 `templates` 纳入配置目录 (`src/utils/markdownConfigLoader.ts:29`)
- `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",范围会明显膨胀
## 依赖
无硬性依赖,可独立实施。

View 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.ts`
- `src/commands/assistant/assistant.ts: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` (无参数) — 返回明确提示: 当前版本需要显式 sessionIddiscovery 尚未启用
**验证:**
- [ ] `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 模式可复用

View 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.

View File

@@ -146,14 +146,15 @@ AI 的信息获取不局限于本地代码:
### WebSearch 实现机制
WebSearch 通过适配器模式支持种搜索后端,由 `src/tools/WebSearchTool/adapters/` 中的工厂函数 `createAdapter()` 选择:
WebSearch 通过适配器模式支持种搜索后端,由 `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` |
适配器是无状态的,同一会话内缓存复用。

View File

@@ -86,6 +86,7 @@
"group": "可扩展性",
"pages": [
"docs/extensibility/mcp-protocol",
"docs/extensibility/mcp-configuration",
"docs/extensibility/hooks",
"docs/extensibility/skills",
"docs/extensibility/custom-agents"
@@ -177,21 +178,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"
}

View File

@@ -1,6 +1,6 @@
{
"name": "claude-code-best",
"version": "1.3.2",
"version": "1.3.5",
"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>",
@@ -55,7 +55,7 @@
"rcs": "bun run scripts/rcs.ts"
},
"dependencies": {
"mcp-chrome-bridge": "^1.0.31"
"@claude-code-best/mcp-chrome-bridge": "^2.0.7"
},
"devDependencies": {
"@types/he": "^1.2.3",
@@ -75,6 +75,9 @@
"@anthropic-ai/sdk": "^0.80.0",
"@anthropic-ai/vertex-sdk": "^0.14.4",
"@anthropic/ink": "workspace:*",
"@claude-code-best/builtin-tools": "workspace:*",
"@claude-code-best/agent-tools": "workspace:*",
"@claude-code-best/mcp-client": "workspace:*",
"@aws-sdk/client-bedrock": "^3.1020.0",
"@aws-sdk/client-bedrock-runtime": "^3.1020.0",
"@aws-sdk/client-sts": "^3.1020.0",

View File

@@ -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";
}

View 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.

View 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" />
```

View 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 `─|++++`

View 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

View 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 |

View File

@@ -0,0 +1,189 @@
# Chapter 6: Scrolling
## ScrollBox
A scrollable container with imperative scroll API, viewport culling, and sticky scroll support.
```tsx
import { ScrollBox } from '@anthropic/ink'
import type { ScrollBoxHandle } from '@anthropic/ink'
function MessageList({ messages }) {
const scrollRef = useRef<ScrollBoxHandle>(null)
// Auto-scroll to bottom on new messages
useEffect(() => {
scrollRef.current?.scrollToBottom()
}, [messages.length])
return (
<ScrollBox ref={scrollRef} stickyScroll flexDirection="column" height={20}>
{messages.map(msg => (
<Text key={msg.id}>{msg.text}</Text>
))}
</ScrollBox>
)
}
```
### Props
ScrollBox accepts all Box layout props except `textWrap`, `overflow`, `overflowX`, `overflowY` (these are managed internally):
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `ref` | `Ref<ScrollBoxHandle>` | - | Imperative handle |
| `stickyScroll` | `boolean` | `false` | Auto-follow new content |
| *(layout props)* | `Styles` | - | Width, height, padding, etc. |
### ScrollBoxHandle (Imperative API)
```ts
interface ScrollBoxHandle {
// Absolute positioning
scrollTo(y: number): void
scrollToElement(el: DOMElement, offset?: number): void
scrollToBottom(): void
// Relative positioning
scrollBy(dy: number): void
// Query state
getScrollTop(): number
getPendingDelta(): number
getScrollHeight(): number
getFreshScrollHeight(): number
getViewportHeight(): number
getViewportTop(): number
isSticky(): boolean
// Events
subscribe(listener: () => void): () => void
// Virtual scroll support
setClampBounds(min?: number, max?: number): void
}
```
### Method Details
#### `scrollTo(y)`
Jump to an absolute position. Breaks sticky scroll.
```tsx
scrollRef.current?.scrollTo(0) // Scroll to top
```
#### `scrollBy(dy)`
Scroll by a relative amount. Accumulates deltas for smooth scrolling.
```tsx
scrollRef.current?.scrollBy(3) // Scroll down 3 rows
scrollRef.current?.scrollBy(-5) // Scroll up 5 rows
```
#### `scrollToElement(el, offset?)`
Scroll so a specific DOM element is at the viewport top. More reliable than `scrollTo` because it reads the element's position at render time (avoids stale layout values).
```tsx
const elementRef = useRef<DOMElement>(null)
scrollRef.current?.scrollToElement(elementRef.current!, 2)
```
#### `scrollToBottom()`
Pin scroll to bottom. Enables sticky mode.
```tsx
scrollRef.current?.scrollToBottom()
```
#### `isSticky()`
Returns `true` when scroll is pinned to the bottom.
```tsx
if (scrollRef.current?.isSticky()) {
// User hasn't scrolled up
}
```
#### `subscribe(listener)`
Subscribe to imperative scroll changes. Returns unsubscribe function.
```tsx
useEffect(() => {
return scrollRef.current?.subscribe(() => {
console.log('Scroll position changed')
})
}, [])
```
### Sticky Scroll
When `stickyScroll` is enabled:
1. Scroll automatically follows new content at the bottom
2. User scroll (via `scrollBy`/`scrollTo`) breaks stickiness
3. `scrollToBottom()` re-enables stickiness
4. Content growth at the bottom is detected and followed automatically
```tsx
<ScrollBox stickyScroll height={20}>
{/* New items auto-scroll to bottom */}
{items.map(renderItem)}
</ScrollBox>
```
### Viewport Culling
ScrollBox only renders children that intersect the visible viewport. Children outside the viewport are still mounted in React but skipped during terminal rendering. This makes large lists performant.
### Virtual Scrolling
For very large lists, use `setClampBounds` in combination with a virtual scrolling hook:
```tsx
const scrollRef = useRef<ScrollBoxHandle>(null)
// After computing visible range
scrollRef.current?.setClampBounds(firstVisibleRow, lastVisibleRow)
```
This prevents burst `scrollTo` calls from showing blank space beyond mounted content.
### Scroll Events
ScrollBox bypasses React state for scroll operations. Instead:
1. `scrollTo`/`scrollBy` mutate `scrollTop` directly on the DOM node
2. The node is marked dirty
3. A microtask-deferred render fires to coalesce multiple scroll events
4. The Ink renderer reads `scrollTop` during layout
This avoids React reconciler overhead per wheel event.
### Integration with Mouse Wheel
In alt-screen mode, mouse wheel events are captured by the `App` component and forwarded to the focused ScrollBox:
```
Wheel event → App.handleMouseEvent → ScrollBox.scrollBy(delta)
```
### Layout Structure
ScrollBox creates a two-level DOM structure:
```
ink-box (overflow: scroll, constrained height)
└── Box (flexGrow: 1, flexShrink: 0, width: 100%)
├── Child 1
├── Child 2
└── ...
```
The outer `ink-box` is the viewport with constrained size. The inner `Box` grows to fit all content. The renderer computes `scrollHeight` from the inner box and translates content by `-scrollTop`.

View File

@@ -0,0 +1,267 @@
# Chapter 7: User Input
## useInput
The primary hook for handling keyboard input.
```tsx
import { useInput } from '@anthropic/ink'
function MyComponent() {
useInput((input, key, event) => {
if (input === 'q') {
// 'q' key pressed
}
if (key.leftArrow) {
// Left arrow
}
if (key.ctrl && input === 'c') {
// Ctrl+C (only if exitOnCtrlC is false)
}
if (key.meta && input === 'b') {
// Alt+B (Option+B on Mac)
}
if (key.shift && input === 'Tab') {
// Shift+Tab
}
})
return <Text>Press keys...</Text>
}
```
### Signature
```ts
function useInput(
handler: (input: string, key: Key, event: InputEvent) => void,
options?: { isActive?: boolean }
): void
```
### Parameters
- **`input`** (`string`) -- The character entered. Empty string for non-printable keys (arrows, function keys). For paste events, the entire pasted text.
- **`key`** (`Key`) -- Parsed key metadata (see below)
- **`event`** (`InputEvent`) -- Raw event with `stopImmediatePropagation()`
### Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `isActive` | `boolean` | `true` | Enable/disable input handling |
### Key Object
```ts
type Key = {
upArrow: boolean
downArrow: boolean
leftArrow: boolean
rightArrow: boolean
pageDown: boolean
pageUp: boolean
wheelUp: boolean // Mouse wheel in alt-screen
wheelDown: boolean // Mouse wheel in alt-screen
home: boolean
end: boolean
return: boolean
escape: boolean
ctrl: boolean
shift: boolean
fn: boolean
tab: boolean
backspace: boolean
delete: boolean
meta: boolean // Alt / Option
super: boolean // Cmd (macOS) / Win key
}
```
### Event Propagation
Multiple `useInput` handlers form a chain. Call `event.stopImmediatePropagation()` to prevent downstream handlers from receiving the event:
```tsx
useInput((input, key, event) => {
if (input === 'j') {
// Consumed by this handler
event.stopImmediatePropagation()
}
// Other handlers won't see 'j'
})
useInput((input, key) => {
// This won't fire for 'j'
})
```
### Raw Mode
`useInput` automatically enables raw mode on stdin when active. Raw mode is reference-counted -- it stays enabled as long as any hook has `isActive: true`.
In raw mode:
- Keystrokes don't echo
- Ctrl+C is not sent as signal (app must handle it)
- Line buffering is disabled
## InputEvent
```ts
class InputEvent extends Event {
readonly input: string
readonly key: Key
readonly keypress: ParsedKey // Raw parsed keypress data
}
```
## KeyboardEvent
DOM-like keyboard event dispatched to focused elements:
```ts
class KeyboardEvent extends Event {
readonly key: Key
}
```
Used with `Box`'s `onKeyDown` and `onKeyDownCapture` props:
```tsx
<Box
tabIndex={0}
autoFocus
onKeyDown={(event) => {
if (event.key.return) {
handleSubmit()
}
}}
>
<Text>Press Enter to submit</Text>
</Box>
```
## Key Parsing
Ink supports multiple keyboard protocols:
### Standard Escape Sequences
- Arrow keys, function keys, Home/End, Page Up/Down
- Ctrl+letter combinations
- Shift, Alt, Meta modifiers
### Kitty Keyboard Protocol (CSI u)
Extended key reporting with full modifier support:
- Distinguishes Ctrl+Shift+A from Ctrl+A
- Reports Super (Cmd/Win) key
- Sends key release events
### xterm modifyOtherKeys
Alternative extended key reporting for xterm-compatible terminals.
### Application Keypad Mode
Numpad keys mapped to their digit characters.
## Paste Detection
When `Bracketed Paste` mode is enabled (DECSET 2004), pasted text is delivered as a single `InputEvent` with the full text in `input`. This distinguishes paste from rapid typing:
```tsx
useInput((input, key, event) => {
if (event.keypress.paste) {
// User pasted text -- handle as a batch
handlePaste(input)
} else {
// Regular keypress
handleKey(input, key)
}
})
```
## Mouse Events (Alt-Screen Only)
In alternate screen mode, mouse events are parsed and dispatched:
### Click Events
```tsx
<Box
onClick={(event) => {
console.log(`Clicked at (${event.x}, ${event.y})`)
event.stopImmediatePropagation()
}}
>
<Text>Click me</Text>
</Box>
```
### Hover Events
```tsx
<Box
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<Text>{hovered ? 'Hovered!' : 'Hover me'}</Text>
</Box>
```
Hover events use `mouseenter`/`mouseleave` semantics (no bubbling between children).
### Wheel Events
Mouse wheel events arrive as `Key.wheelUp`/`Key.wheelDown`:
```tsx
useInput((input, key) => {
if (key.wheelUp) scrollUp()
if (key.wheelDown) scrollDown()
})
```
## useStdin
Lower-level access to the stdin stream.
```tsx
import { useStdin } from '@anthropic/ink'
const {
stdin, // Raw stdin stream
setRawMode, // (enabled: boolean) => void
isRawModeSupported, // boolean
internal_exitOnCtrlC, // boolean
internal_eventEmitter, // EventEmitter | undefined
internal_querier, // Terminal querier
} = useStdin()
```
> **Prefer `useInput` for keyboard handling.** `useStdin` is for advanced use cases like terminal querying or custom event handling.
## Button Component
Interactive button that responds to keyboard and mouse:
```tsx
import { Button } from '@anthropic/ink'
<Button onAction={() => handleClick()} tabIndex={0} autoFocus>
{(state) => (
<Text bold={state.focused} color={state.focused ? 'claude' : 'text'}>
{state.focused ? '> Click Me' : ' Click Me'}
</Text>
)}
</Button>
```
Button receives a render prop with state:
```ts
type ButtonState = {
focused: boolean // Has keyboard focus
hovered: boolean // Mouse is over it (alt-screen)
active: boolean // True for 100ms after activation (flash effect)
}
```
Activation triggers: Enter key, Space key, or mouse click.

View File

@@ -0,0 +1,302 @@
# Chapter 8: Keybinding System
The keybinding system provides configurable, context-aware keyboard shortcuts with chord sequence support.
## Architecture
```
KeybindingSetup (loads config)
└── KeybindingProvider (provides context)
├── useKeybinding(action, handler)
├── useKeybindings({ action: handler })
├── useKeybindingContext()
└── useRegisterKeybindingContext(name, isActive)
```
## KeybindingSetup
Loads and validates keybinding configuration at app startup.
```tsx
import { KeybindingSetup } from '@anthropic/ink'
<KeybindingSetup
loadBindings={() => parseUserKeybindings(configFile)}
subscribeToChanges={(cb) => watchConfigFile(cb)}
onWarnings={(warnings, isReload) => {
warnings.forEach(w => console.warn(w.message))
}}
>
<App />
</KeybindingSetup>
```
### Props
| Prop | Type | Description |
|------|------|-------------|
| `children` | `ReactNode` | App tree |
| `loadBindings` | `() => KeybindingsLoadResult` | Load bindings from config |
| `subscribeToChanges` | `(cb) => unsubscribe` | Watch for config changes |
| `initWatcher` | `() => void \| Promise<void>` | One-time setup (optional) |
| `onWarnings` | `(warnings, isReload) => void` | Validation warnings (optional) |
| `onDebugLog` | `(message) => void` | Debug logging (optional) |
### KeybindingsLoadResult
```ts
type KeybindingsLoadResult = {
bindings: ParsedBinding[]
warnings: KeybindingWarning[]
}
```
### KeybindingWarning
```ts
type KeybindingWarning = {
type: 'parse_error' | 'duplicate' | 'reserved' | 'invalid_context' | 'invalid_action'
severity: 'error' | 'warning'
message: string
key?: string
context?: string
action?: string
suggestion?: string
}
```
## KeybindingProvider
Context provider that holds binding state and resolution logic. Automatically provided by `KeybindingSetup`.
## useKeybinding
Register a handler for a keybinding action.
```tsx
import { useKeybinding } from '@anthropic/ink'
function MyComponent() {
useKeybinding('app:toggleTodos', () => {
setShowTodos(prev => !prev)
}, { context: 'Global' })
// Return false to NOT consume the event (allow propagation)
useKeybinding('scroll:lineDown', () => {
if (!hasContent) return false // Don't consume
scrollBy(1)
})
}
```
### Signature
```ts
function useKeybinding(
action: string,
handler: () => void | false | Promise<void>,
options?: { context?: string; isActive?: boolean }
): void
```
### Handler Return Values
| Return | Effect |
|--------|--------|
| `undefined` / `void` | Event consumed, stop propagation |
| `false` | Event NOT consumed, propagate to other handlers |
| `Promise<void>` | Async handler, treated as consumed |
## useKeybindings
Register multiple handlers in one hook (reduces `useInput` overhead).
```tsx
import { useKeybindings } from '@anthropic/ink'
useKeybindings({
'chat:submit': () => handleSubmit(),
'chat:cancel': () => handleCancel(),
'scroll:pageDown': () => {
scrollBy(viewportHeight)
},
'scroll:lineDown': () => {
if (!hasContent) return false
scrollBy(1)
},
}, { context: 'Chat' })
```
## Keybinding Contexts
Contexts allow the same key to perform different actions depending on what's active.
```tsx
// Register a context as active
import { useRegisterKeybindingContext } from '@anthropic/ink'
function ThemePicker({ isOpen }) {
useRegisterKeybindingContext('ThemePicker', isOpen)
// While open, 'ThemePicker' context bindings take precedence
useKeybinding('picker:select', handleSelect, { context: 'ThemePicker' })
return isOpen ? <PickerUI /> : null
}
```
Context resolution order:
1. Registered active contexts (most recent first)
2. The hook's own `context` parameter
3. `'Global'` (always checked last)
## Chord Sequences
Keybindings support multi-key sequences (chords):
```
"ctrl+k ctrl+s" → Save (press Ctrl+K, then Ctrl+S)
"ctrl+k ctrl+c" → Close (press Ctrl+K, then Ctrl+C)
```
When a chord prefix is pressed:
- `result.type === 'chord_started'` -- Show "Ctrl+K ..." pending indicator
- Next key completes or cancels the chord
- `result.type === 'chord_cancelled'` -- Invalid key, reset
## KeybindingContext Hook
```tsx
import { useKeybindingContext, useOptionalKeybindingContext } from '@anthropic/ink'
const ctx = useKeybindingContext()
// ctx.resolve(input, key, contexts) → ResolveResult
// ctx.bindings → ParsedBinding[]
// ctx.pendingChord → ParsedKeystroke[] | null
// ctx.activeContexts → Set<string>
// ctx.getDisplayText(action, context) → string | undefined
// ctx.invokeAction(action) → boolean
// ctx.registerHandler(registration) → () => void (unsubscribe)
// Returns null outside provider (no throw)
const optionalCtx = useOptionalKeybindingContext()
```
## Parser Functions
Parse and format keybinding strings:
```tsx
import {
parseKeystroke,
parseChord,
keystrokeToString,
chordToString,
keystrokeToDisplayString,
chordToDisplayString,
parseBindings,
} from '@anthropic/ink'
```
### `parseKeystroke(str)`
Parse a single keystroke string:
```ts
parseKeystroke('ctrl+shift+enter')
// → { key: 'enter', ctrl: true, alt: false, shift: true, meta: false, super: false }
```
### `parseChord(str)`
Parse a chord (space-separated keystrokes):
```ts
parseChord('ctrl+k ctrl+s')
// → [{ key: 'k', ctrl: true, ... }, { key: 's', ctrl: true, ... }]
```
### `keystrokeToString(ks)` / `chordToString(chord)`
Convert parsed keystroke/chord back to string.
### `keystrokeToDisplayString(ks)` / `chordToDisplayString(chord)`
Convert to human-readable display string (platform-aware).
### `parseBindings(blocks)`
Parse a keybinding configuration:
```ts
parseBindings([
{
context: 'Global',
bindings: {
'ctrl+s': 'app:save',
'ctrl+k ctrl+s': 'app:saveAs',
}
}
])
// → ParsedBinding[]
```
## Match Functions
```tsx
import { getKeyName, matchesKeystroke, matchesBinding } from '@anthropic/ink'
```
### `getKeyName(input, key)`
Get the canonical key name from raw input:
```ts
getKeyName('\x1b[A', { upArrow: true }) // 'up'
```
### `matchesKeystroke(input, key, target)`
Check if raw input matches a parsed keystroke:
```ts
matchesKeystroke('s', { ctrl: true, shift: false }, { key: 's', ctrl: true })
```
### `matchesBinding(input, key, binding)`
Check if raw input matches any keystroke in a binding's chord.
## Resolver Functions
```tsx
import { resolveKey, resolveKeyWithChordState, getBindingDisplayText } from '@anthropic/ink'
```
### `resolveKey(input, key, contexts, bindings)`
Resolve input to a binding action:
```ts
const result = resolveKey('s', { ctrl: true, shift: false }, ['Global'], bindings)
// result.type: 'match' | 'none' | 'unbound'
// result.action: string (when type === 'match')
```
### `resolveKeyWithChordState(input, key, contexts, bindings, pendingChord)`
Resolve with chord state:
```ts
const result = resolveKeyWithChordState('k', key, ['Global'], bindings, null)
// result.type: 'match' | 'none' | 'unbound' | 'chord_started' | 'chord_cancelled'
// result.pending: ParsedKeystroke[] (when type === 'chord_started')
```
### `getBindingDisplayText(action, context, bindings)`
Get the display string for a binding:
```ts
getBindingDisplayText('app:save', 'Global', bindings) // 'Ctrl+S'
```

View File

@@ -0,0 +1,407 @@
# Chapter 9: Hooks Reference
Complete API reference for all hooks exported by `@anthropic/ink`.
---
## Application Hooks
### `useApp()`
Access app-level operations.
```ts
function useApp(): {
exit: (error?: Error) => void
}
```
Example:
```tsx
const { exit } = useApp()
// Gracefully unmount and exit
exit()
```
### `useStdin()`
Access the stdin stream and raw mode control.
```ts
function useStdin(): {
stdin: NodeJS.ReadStream
isRawModeSupported: boolean
setRawMode: (enabled: boolean) => void
internal_exitOnCtrlC: boolean
internal_eventEmitter: EventEmitter | undefined
internal_querier: TerminalQuerier | null
}
```
> Prefer `useInput` for keyboard handling.
---
## Input Hooks
### `useInput(handler, options?)`
Handle keyboard input. See [Chapter 7](./07-user-input.md) for full details.
```ts
function useInput(
handler: (input: string, key: Key, event: InputEvent) => void,
options?: { isActive?: boolean }
): void
```
---
## Terminal Hooks
### `useTerminalSize()`
Get current terminal dimensions.
```ts
function useTerminalSize(): {
columns: number
rows: number
}
```
Throws if used outside `<App>`.
### `useTerminalFocus()`
Track whether the terminal window is focused.
```ts
function useTerminalFocus(): boolean
```
Uses DECSET 1004 focus reporting. Returns `true` when focused.
### `useTerminalTitle(title)`
Set the terminal window title.
```ts
function useTerminalTitle(title: string | null): void
```
Pass `null` to clear the title.
### `useTerminalViewport()`
Track element visibility in the terminal viewport.
```ts
function useTerminalViewport(): [
ref: (element: DOMElement | null) => void,
entry: { isVisible: boolean }
]
```
Example:
```tsx
const [viewportRef, { isVisible }] = useTerminalViewport()
<Box ref={viewportRef}>
<Text>{isVisible ? 'Visible' : 'Scrolled off'}</Text>
</Box>
```
### `useTabStatus(kind)`
Set tab status indicator in terminal tab bar (OSC 21337).
```ts
type TabStatusKind = 'idle' | 'busy' | 'waiting'
function useTabStatus(kind: TabStatusKind | null): void
```
### `useTerminalNotification()`
Send terminal notifications (iTerm2, Kitty, Ghostty, bell).
```ts
function useTerminalNotification(): {
notifyITerm2: (opts: { message: string; title?: string }) => void
notifyKitty: (opts: { message: string; title: string; id: number }) => void
notifyGhostty: (opts: { message: string; title: string }) => void
notifyBell: () => void
progress: (state: Progress['state'] | null, percentage?: number) => void
}
```
Requires `TerminalWriteProvider` in the tree.
Progress states: `'running'`, `'completed'`, `'error'`, `'indeterminate'`, `null` (clear).
---
## Animation & Timing Hooks
### `useInterval(callback, intervalMs)`
Clock-backed interval timer.
```ts
function useInterval(callback: () => void, intervalMs: number | null): void
```
Pass `null` to pause. Shares the application clock for efficient batching.
### `useAnimationTimer(intervalMs)`
Returns the current clock time, updating at the given interval.
```ts
function useAnimationTimer(intervalMs: number): number
```
Subscribes as non-keepAlive -- won't keep the clock running on its own.
### `useAnimationFrame(intervalMs?)`
Synchronized animation hook that pauses when offscreen.
```ts
function useAnimationFrame(
intervalMs?: number | null, // default 16
): [ref: (element: DOMElement | null) => void, time: number]
```
Returns a ref callback (attach to animated element) and the current animation time. All instances share the same clock. Pass `null` to pause.
```tsx
const [ref, time] = useAnimationFrame(120)
const frame = Math.floor(time / 120) % FRAMES.length
return <Box ref={ref}>{FRAMES[frame]}</Box>
```
### `useTimeout(delayMs, resetTrigger?)`
One-shot timer.
```ts
function useTimeout(delay: number, resetTrigger?: number): boolean
```
Returns `true` when the timeout has elapsed. Change `resetTrigger` to restart.
### `useMinDisplayTime(value, minMs)`
Ensure a value is displayed for at least `minMs` milliseconds.
```ts
function useMinDisplayTime<T>(value: T, minMs: number): T
```
Holds the previous value until `minMs` has elapsed, then switches to the new value.
Example:
```tsx
// Keep showing "Loading" for at least 300ms to prevent flash
const displayValue = useMinDisplayTime(isLoading ? 'loading' : 'done', 300)
```
---
## Interaction Hooks
### `useDoublePress(setPending, onDoublePress, onFirstPress?)`
Detect double-press (double-click equivalent for keyboard).
```ts
export const DOUBLE_PRESS_TIMEOUT_MS = 800
function useDoublePress(
setPending: (pending: boolean) => void,
onDoublePress: () => void,
onFirstPress?: () => void
): () => void // Returns the press handler
```
Example:
```tsx
const [pendingExit, setPendingExit] = useState(false)
const handlePress = useDoublePress(
setPendingExit,
() => exit(), // Double press
() => {}, // First press
)
useInput((input, key) => {
if (key.escape) handlePress()
})
```
### `useExitOnCtrlCD(options?)`
Handle Ctrl+C / Ctrl+D with double-press confirmation.
```ts
type ExitState = {
pending: boolean
keyName: 'Ctrl-C' | 'Ctrl-D' | null
}
function useExitOnCtrlCDWithKeybindings(
onExit?: () => void,
onInterrupt?: () => boolean,
isActive?: boolean
): ExitState
```
Example:
```tsx
const exitState = useExitOnCtrlCDWithKeybindings(
() => exit(),
() => { /* return true to prevent exit */ }
)
if (exitState.pending) {
return <Text>Press {exitState.keyName} again to exit</Text>
}
```
---
## Selection Hooks (Alt-Screen Only)
### `useSelection()`
Text selection operations.
```ts
function useSelection(): {
copySelection: () => string
copySelectionNoClear: () => string
clearSelection: () => void
hasSelection: () => boolean
getState: () => SelectionState | null
subscribe: (cb: () => void) => () => void
shiftAnchor: (dRow: number, minRow: number, maxRow: number) => void
shiftSelection: (dRow: number, minRow: number, maxRow: number) => void
moveFocus: (move: FocusMove) => void
captureScrolledRows: (firstRow: number, lastRow: number, side: 'above' | 'below') => void
setSelectionBgColor: (color: string) => void
}
```
### `useHasSelection()`
Reactive boolean for selection state.
```ts
function useHasSelection(): boolean
```
Re-renders when selection is created or cleared.
---
## Search Hooks
### `useSearchHighlight()`
Set and manage search highlighting.
```ts
function useSearchHighlight(): {
setQuery: (query: string) => void
scanElement: (el: DOMElement) => MatchPosition[]
setPositions: (state: { positions: MatchPosition[]; rowOffset: number; currentIdx: number } | null) => void
}
```
### `useSearchInput(options)`
Search input handler with cursor management.
```ts
type UseSearchInputOptions = {
isActive: boolean
onExit: () => void
onCancel?: () => void
onExitUp?: () => void
columns?: number
passthroughCtrlKeys?: string[]
initialQuery?: string
backspaceExitsOnEmpty?: boolean
}
type UseSearchInputReturn = {
query: string
setQuery: (q: string) => void
cursorOffset: number
handleKeyDown: (e: KeyboardEvent) => void
}
function useSearchInput(options: UseSearchInputOptions): UseSearchInputReturn
```
---
## Cursor Hooks
### `useDeclaredCursor(options)`
Park the terminal cursor at a specific position for IME and accessibility.
```ts
function useDeclaredCursor({
line: number,
column: number,
active: boolean
}): (element: DOMElement | null) => void
```
Returns a ref callback. Position is relative to the ref'd element.
Example:
```tsx
const cursorRef = useDeclaredCursor({
line: 0,
column: cursorPosition,
active: isFocused,
})
return <Box ref={cursorRef}>...</Box>
```
---
## Tab Status Hooks
### `useTabStatus(kind)`
Set tab status indicator (OSC 21337) for terminal tab bars.
```ts
type TabStatusKind = 'idle' | 'busy' | 'waiting'
function useTabStatus(kind: TabStatusKind | null): void
```
Pass `null` to clear.
---
## Viewport Hooks
### `useTerminalViewport()`
Track element visibility within the terminal viewport.
```ts
function useTerminalViewport(): [
ref: (element: DOMElement | null) => void,
entry: { isVisible: boolean }
]
```
Returns a ref callback and visibility state.

View File

@@ -0,0 +1,232 @@
# Chapter 10: Events & Focus
## Event System
Ink implements a DOM-like event system with capture/bubble phases, propagation control, and prioritized dispatch.
### Event Classes
All events extend the base `Event` class:
```ts
class Event {
stopImmediatePropagation(): void
}
```
### InputEvent
Emitted for every keystroke or input action.
```ts
class InputEvent extends Event {
readonly input: string // Character(s) entered
readonly key: Key // Parsed key metadata
readonly keypress: ParsedKey // Raw keypress data
}
```
### KeyboardEvent
DOM-like keyboard event for focused elements.
```ts
class KeyboardEvent extends Event {
readonly key: Key
}
```
Dispatched via `onKeyDown` / `onKeyDownCapture` on `Box`.
### ClickEvent
Mouse click event (alt-screen only).
```ts
class ClickEvent extends Event {
readonly x: number // Column (0-indexed)
readonly y: number // Row (0-indexed)
}
```
Clicks bubble from the deepest hit Box up through ancestors.
### FocusEvent
Focus change event.
```ts
class FocusEvent extends Event {
readonly relatedTarget: DOMElement | null
}
```
### TerminalFocusEvent
Terminal window focus change.
```ts
class TerminalFocusEvent extends Event {
readonly type: 'terminalfocus' | 'terminalblur'
}
```
### ResizeEvent
Terminal resize event (internal).
### PasteEvent
Pasted text event (bracketed paste mode).
## Event Dispatch Flow
```
stdin data → parse-keypress → InputEvent
App.handleInput (useInput handlers)
Box.onKeyDown (focused element, bubble)
```
### Capture and Bubble Phases
```tsx
<Box
onKeyDownCapture={(e) => {
// Capture phase: fires top-down
console.log('Parent captures key')
}}
onKeyDown={(e) => {
// Bubble phase: fires bottom-up
console.log('Parent receives bubbled key')
}}
>
<Box
onKeyDown={(e) => {
// Target: fires first in bubble phase
console.log('Child handles key')
e.stopImmediatePropagation() // Stop here
}}
>
<Text>Focus here</Text>
</Box>
</Box>
```
### Event Propagation Methods
| Method | Effect |
|--------|--------|
| `event.stopImmediatePropagation()` | Stop all subsequent handlers |
| `event.preventDefault()` | Not supported in terminal context |
## FocusManager
DOM-like focus management system.
### How Focus Works
1. Elements with `tabIndex >= 0` participate in Tab/Shift+Tab cycling
2. Elements with `tabIndex === -1` are programmatically focusable only
3. Elements with `autoFocus` receive focus on mount
4. Clicking a focusable element focuses it
### Focus API
```ts
class FocusManager {
activeElement: DOMElement | null
focus(node: DOMElement): void
blur(): void
focusNext(root: DOMElement): void // Tab
focusPrevious(root: DOMElement): void // Shift+Tab
handleNodeRemoved(node: DOMElement, root: DOMElement): void
handleAutoFocus(node: DOMElement): void
handleClickFocus(node: DOMElement): void
enable(): void
disable(): void
}
```
### Tab Navigation
```tsx
<Box flexDirection="column">
<Button tabIndex={0} onAction={handleSave}>
{(s) => <Text>{s.focused ? '> Save' : ' Save'}</Text>}
</Button>
<Button tabIndex={0} onAction={handleCancel}>
{(s) => <Text>{s.focused ? '> Cancel' : ' Cancel'}</Text>}
</Button>
<Button tabIndex={-1} onAction={handleSecret}>
{/* Not reachable via Tab */}
{(s) => <Text>Secret</Text>}
</Button>
</Box>
```
### Auto Focus
```tsx
<Box tabIndex={0} autoFocus onKeyDown={handleKey}>
<Text>Receives focus immediately on mount</Text>
</Box>
```
### Focus Events
```tsx
<Box
tabIndex={0}
onFocus={(e) => console.log('Got focus')}
onBlur={(e) => console.log('Lost focus')}
onFocusCapture={(e) => console.log('Capture: focus in')}
onBlurCapture={(e) => console.log('Capture: focus out')}
>
<Text>Focusable element</Text>
</Box>
```
## Hit Testing
Mouse click/hover resolution:
1. Screen coordinates are mapped to DOM elements via Yoga layout
2. The deepest element at the click position is the target
3. Click events bubble upward through ancestors
4. Hover events use `mouseenter`/`mouseleave` semantics (no bubbling between children)
### Click Hit Testing
```ts
dispatchClick(rootNode, col, row): void
```
Walks the DOM tree, finds the deepest Box at (col, row), fires `onClick`, then bubbles to ancestors.
### Hover Hit Testing
```ts
dispatchHover(rootNode, col, row, hoveredNodes): void
```
Tracks which nodes are under the pointer. Fires `onMouseEnter`/`onMouseLeave` as the pointer moves between elements.
## EventEmitter
Custom event emitter for internal use:
```ts
class EventEmitter {
on(event: string, handler: Function): void
off(event: string, handler: Function): void
emit(event: string, ...args: any[]): void
removeListener(event: string, handler: Function): void
}
```
Used internally by the Ink instance for `input` events.

View File

@@ -0,0 +1,301 @@
# Chapter 11: Core Architecture
This chapter covers the internal rendering pipeline, DOM model, and screen buffer system. This is advanced material -- most users only need the component and hooks APIs.
## Rendering Pipeline
```
React Component Tree
↓ (React reconciler)
Ink DOM Tree (virtual terminal DOM)
↓ (Yoga layout)
Positioned DOM Tree (computed x, y, width, height)
↓ (renderNodeToOutput)
Output Buffer (styled characters)
↓ (renderer → Screen)
Screen Buffer (Int32Array of cells)
↓ (diffEach)
ANSI Diff Patches (minimal escape sequences)
↓ (writeDiffToTerminal)
Terminal stdout
```
### Frame Lifecycle
Each render cycle (`onRender`) follows these phases:
1. **React Commit** -- React reconciles the virtual tree; host config updates Ink DOM
2. **Yoga Layout** -- All dirty nodes have their styles applied and layout computed
3. **Renderer** -- Creates Output buffer, calls `renderNodeToOutput` for the full tree
4. **Screen Diff** -- New frame is compared against previous frame cell-by-cell
5. **Optimize** -- Patches are merged and ordered for minimal cursor movement
6. **Write** -- ANSI escape sequences are written to stdout
### Frame Timing
```ts
const FRAME_INTERVAL_MS = 16 // ~60fps cap
```
Renders are throttled. Multiple state updates in one frame are batched.
### Double Buffering
Two frames are maintained:
- **`frontFrame`** -- The currently displayed frame
- **`backFrame`** -- The frame being rendered
After rendering, they are swapped. This prevents partial updates from being visible.
## Ink DOM
### Node Types
```ts
type ElementNames =
| 'ink-root' // Root container
| 'ink-box' // Box component
| 'ink-text' // Text component
| 'ink-virtual-text' // Intermediate text wrapper
| 'ink-link' // Link component
| 'ink-raw-ansi' // Raw ANSI content
```
### DOMElement
```ts
type DOMElement = {
nodeName: ElementNames
attributes: Record<string, unknown>
childNodes: DOMNode[] // DOMElement | TextNode
yogaNode?: LayoutNode // Yoga layout node
textStyles?: TextStyles // Inherited text styles
// Scroll state
scrollTop?: number
scrollHeight?: number
scrollViewportHeight?: number
scrollViewportTop?: number
stickyScroll?: boolean
pendingScrollDelta?: number
scrollAnchor?: { el: DOMElement; offset: number }
// Dirty tracking
dirty: boolean
// Event handlers (stored separately)
onClick?: (event: ClickEvent) => void
onFocus?: (event: FocusEvent) => void
onBlur?: (event: FocusEvent) => void
onKeyDown?: (event: KeyboardEvent) => void
onMouseEnter?: () => void
onMouseLeave?: () => void
}
```
### TextNode
```ts
type TextNode = {
nodeName: '#text'
nodeValue: string
yogaNode?: LayoutNode
}
```
### DOM Operations
```ts
// Node creation
createNode(nodeName: string): DOMElement
createTextNode(text: string): TextNode
// Tree manipulation
appendChildNode(parent: DOMElement, child: DOMNode): void
insertBeforeNode(parent: DOMElement, child: DOMNode, before: DOMNode): void
removeChildNode(parent: DOMElement, child: DOMNode): void
// Attribute manipulation
setAttribute(node: DOMElement, key: string, value: unknown): void
setStyle(node: DOMElement, style: Styles): void
setTextStyles(node: DOMElement, styles: TextStyles): void
// Dirty tracking
markDirty(node: DOMElement): void
scheduleRenderFrom(node: DOMElement): void
```
## Screen Buffer
### Cell Storage
The screen buffer uses packed `Int32Array` storage for memory efficiency:
```ts
type Screen = {
width: number
height: number
cells: Int32Array // 2 Int32s per cell: [charId, packed_style_hyperlink_width]
cells64: BigInt64Array // For bulk fill operations
charPool: CharPool // String interning
stylePool: StylePool // ANSI code interning
hyperlinkPool: HyperlinkPool
emptyStyleId: number
damage: Rectangle | undefined // Bounding box of changed cells
noSelect: Uint8Array // Per-cell no-select bitmap
softWrap: Int32Array // Per-row soft-wrap markers
}
```
### Cell Width
```ts
enum CellWidth {
Narrow = 0, // Regular character (1 column)
Wide = 1, // CJK/emoji (2 columns)
SpacerTail = 2, // Right half of wide character
SpacerHead = 3, // Soft-wrapped wide character
}
```
### Style Pool
ANSI style codes are interned for efficiency:
```ts
class StylePool {
intern(codes: AnsiCode[]): number // Returns compact ID
get(id: number): AnsiCode[]
transition(from: number, to: number): string // Cached ANSI transition
withInverse(id: number): number // Selection overlay
setSelectionBg(bg: AnsiCode): void // Theme-aware selection bg
}
```
### Diff Algorithm
```ts
diffEach(prev: Screen, next: Screen, callback: (x, y, oldCell, newCell) => void): void
```
Only iterates cells within the damage bounding box. Unchanged regions are skipped entirely.
### Screen Operations
```ts
createScreen(width, height, stylePool, charPool, hyperlinkPool): Screen
setCellAt(screen, x, y, cell): void
cellAt(screen, x, y): Cell
clearRegion(screen, x, y, width, height): void
blitRegion(dst, src, x, y, maxX, maxY): void
shiftRows(screen, top, bottom, n): void
```
## Layout Engine
### Yoga Integration
Ink wraps Facebook's Yoga layout engine for Flexbox computation:
```ts
// Layout node types
enum LayoutDisplay { Flex, None }
enum LayoutPositionType { Absolute, Relative }
enum LayoutOverflow { Visible, Hidden, Scroll }
enum LayoutFlexDirection { Row, Column, RowReverse, ColumnReverse }
enum LayoutWrap { NoWrap, Wrap, WrapReverse }
enum LayoutAlign { FlexStart, Center, FlexEnd, Stretch }
enum LayoutJustify { FlexStart, Center, FlexEnd, SpaceBetween, SpaceAround, SpaceEvenly }
enum LayoutEdge { Top, Bottom, Left, Right, Start, End, Horizontal, Vertical, All }
enum LayoutGutter { Column, Row, All }
```
### Style Application
Styles from React props are applied to Yoga nodes during the commit phase:
```ts
function styles(node: LayoutNode, style: Styles, resolvedStyle?: Styles): void
```
This function maps each CSS-like prop to the corresponding Yoga setter.
## Output Buffer
Intermediate rendering target before screen diff:
```ts
class Output {
write(text: string, x: number, y: number, styles: TextStyles): void
wrap(width: number, textWrap: TextWrap): void
}
```
`renderNodeToOutput` walks the DOM tree and writes styled characters into this buffer.
## Reconciler
Custom React reconciler that bridges React and the Ink DOM:
- **Host config** -- Defines how React operations map to Ink DOM mutations
- **Concurrent mode** -- Supports `ConcurrentRoot` for React 19 features
- **Yoga integration** -- Applies styles during commit phase
- **DevTools** -- Connected in development mode
### Host Config Methods
| Method | Purpose |
|--------|---------|
| `createInstance` | Create `ink-box`, `ink-text`, etc. |
| `createTextInstance` | Create `#text` node |
| `appendChildNode` | Add child to parent |
| `removeChildNode` | Remove child from parent |
| `insertBefore` | Insert child before sibling |
| `commitUpdate` | Update element attributes/styles |
| `commitTextUpdate` | Update text content |
| `getPublicInstance` | Return DOMElement for refs |
## Performance Optimizations
1. **String Interning** -- CharPool deduplicates character strings across frames
2. **Style Caching** -- StylePool caches ANSI transition strings
3. **Damage Tracking** -- Only diff cells within the changed bounding box
4. **Bulk Operations** -- `Int32Array.set()` for fast region blit
5. **Throttled Rendering** -- Frame rate capped at ~60fps
6. **Viewport Culling** -- ScrollBox only renders visible children
7. **Microtask Coalescing** -- Multiple scroll deltas merged into one render
## Frame Events
Debug instrumentation for render performance:
```ts
type FrameEvent = {
durationMs: number
phases: {
renderer: number // Yoga + renderNodeToOutput
diff: number // Screen diff
optimize: number // Patch optimization
write: number // Terminal write
patches: number // Number of ANSI patches
yoga: number // Yoga layout time
commit: number // React commit time
yogaVisited: number // Yoga nodes visited
yogaMeasured: number // Yoga nodes measured
yogaCacheHits: number // Cached measurements
yogaLive: number // Active Yoga nodes
}
flickers: FlickerReason[]
}
```
Enable with `onFrame` in RenderOptions:
```tsx
render(<App />, {
onFrame: (event) => {
console.log(`Frame: ${event.durationMs}ms`)
}
})
```

View File

@@ -0,0 +1,381 @@
# Chapter 12: Terminal Integration
This chapter covers terminal-specific features: alternate screen, mouse tracking, clipboard, notifications, and terminal querying.
## Alternate Screen
Enter a fullscreen alternate screen buffer (like vim, less, htop).
```tsx
import { AlternateScreen } from '@anthropic/ink'
<AlternateScreen mouseTracking={true}>
<Box flexDirection="column" height="100%">
<Text>Fullscreen content</Text>
</Box>
</AlternateScreen>
```
### Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `ReactNode` | - | Content |
| `mouseTracking` | `boolean` | `true` | Enable SGR mouse tracking |
### Behavior
On mount:
1. Enters DEC 1049 alternate screen buffer
2. Hides cursor
3. Enables mouse tracking (if `mouseTracking=true`)
4. Constrains rendering height to terminal rows
On unmount:
1. Exits alternate screen buffer
2. Shows cursor
3. Disables mouse tracking
4. Restores original terminal content
### Mouse Tracking Modes
When enabled:
- **Mode 1003** -- Button press/release + motion (hover)
- **Mode 1006** -- SGR extended mouse format (coordinates > 223)
- **Wheel events** -- Scroll up/down
### External Editor Handoff
The Ink instance supports pausing for an external editor:
```ts
// Pause Ink, run external command, resume
ink.enterAlternateScreen() // Save state
// ... external editor runs ...
ink.reassertTerminalModes() // Restore on resume
```
This is triggered by Ctrl+Z (SIGTSTP) and SIGCONT.
## Mouse Events
### Click Events
```tsx
<Box onClick={(event) => {
console.log(`Clicked at col=${event.x}, row=${event.y}`)
event.stopImmediatePropagation()
}}>
<Text>Clickable area</Text>
</Box>
```
### Multi-Click
Double-click selects a word, triple-click selects a line. Handled by the App component:
```ts
// App prop
onMultiClick: (col: number, row: number, count: 2 | 3) => void
```
### Hover Events
```tsx
<Box
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
<Text>{hovered ? 'Hovered!' : 'Hover me'}</Text>
</Box>
```
Hover uses `mouseenter`/`mouseleave` semantics (no bubbling between children).
### Drag-to-Select
In alt-screen mode, click-drag creates a text selection:
```ts
// App prop
onSelectionDrag: (col: number, row: number) => void
```
## Clipboard
### OSC 52 Clipboard
```tsx
import { setClipboard } from '@anthropic/ink'
await setClipboard('Copied text')
```
### Copy Selection
```tsx
const { copySelection } = useSelection()
const text = copySelection() // Copies to clipboard and clears highlight
```
### Copy Without Clear
```tsx
const { copySelectionNoClear } = useSelection()
const text = copySelectionNoClear() // Copies but keeps highlight
```
## Terminal Notifications
Send desktop notifications from the terminal.
```tsx
import { useTerminalNotification } from '@anthropic/ink'
function MyComponent() {
const { notifyBell, progress } = useTerminalNotification()
// Terminal bell (audible/system notification)
notifyBell()
// Progress bar in terminal title/tab
progress('running', 65) // 65% complete
progress('completed') // Done
progress('error') // Error state
progress('indeterminate') // Unknown progress
progress(null) // Clear
}
```
### Terminal-Specific Notifications
```tsx
const { notifyITerm2, notifyKitty, notifyGhostty } = useTerminalNotification()
// iTerm2
notifyITerm2({ message: 'Build complete', title: 'My App' })
// Kitty
notifyKitty({ message: 'Build complete', title: 'My App', id: 1 })
// Ghostty
notifyGhostty({ message: 'Build complete', title: 'My App' })
```
## Terminal Queries
### Background Color (OSC 11)
Used for auto-theme detection:
```ts
import { getTerminalBackground } from '@anthropic/ink'
const bg = await getTerminalBackground()
// e.g., 'rgb:0000/0000/0000' (dark) or 'rgb:ffff/ffff/ffff' (light)
```
### Terminal Version (XTVERSION)
```ts
import { isXtermJs, setXtversionName, getXtversionName } from '@anthropic/ink'
```
### Feature Detection
```ts
import { supportsHyperlinks } from '@anthropic/ink'
if (supportsHyperlinks()) {
// OSC 8 hyperlinks supported
}
import { supportsExtendedKeys } from '@anthropic/ink'
if (supportsExtendedKeys()) {
// Kitty keyboard protocol / modifyOtherKeys available
}
```
## Terminal Focus
Track terminal window focus/unfocus:
```tsx
import { useTerminalFocus } from '@anthropic/ink'
const isFocused = useTerminalFocus()
```
Low-level API:
```ts
import { getTerminalFocused, subscribeTerminalFocus } from '@anthropic/ink'
getTerminalFocused() // boolean
subscribeTerminalFocus((focused: boolean) => {
// Called on focus change
})
```
Uses DECSET 1004 focus reporting.
## Terminal Title
Set the terminal window title:
```tsx
import { useTerminalTitle } from '@anthropic/ink'
useTerminalTitle('My App - Dashboard')
```
Clear:
```tsx
useTerminalTitle(null)
```
## Terminal I/O Sequences
Low-level ANSI sequence constants for advanced use.
### Cursor Control
```ts
import {
SHOW_CURSOR,
HIDE_CURSOR,
CURSOR_HOME,
} from '@anthropic/ink'
// cursorPosition(row, col) -- Move cursor to absolute position
// cursorMove(dx, dy) -- Move cursor relative
```
### Screen Control
```ts
import {
ENTER_ALT_SCREEN,
EXIT_ALT_SCREEN,
ERASE_SCREEN,
} from '@anthropic/ink'
```
### Mouse Control
```ts
import {
ENABLE_MOUSE_TRACKING,
DISABLE_MOUSE_TRACKING,
} from '@anthropic/ink'
```
### Keyboard Protocols
```ts
import {
ENABLE_KITTY_KEYBOARD,
DISABLE_KITTY_KEYBOARD,
ENABLE_MODIFY_OTHER_KEYS,
DISABLE_MODIFY_OTHER_KEYS,
} from '@anthropic/ink'
```
### Clipboard & Tab Status
```ts
import {
CLEAR_ITERM2_PROGRESS,
CLEAR_TAB_STATUS,
CLEAR_TERMINAL_TITLE,
wrapForMultiplexer,
} from '@anthropic/ink'
```
`wrapForMultiplexer` wraps OSC sequences for tmux compatibility.
## Terminal Compatibility
### Supported Terminals
| Terminal | Features |
|----------|----------|
| iTerm2 | Full support (hyperlinks, notifications, progress) |
| Kitty | Full support (keyboard protocol, notifications) |
| Ghostty | Full support |
| WezTerm | Full support |
| Alacritty | Most features |
| Windows Terminal | Most features |
| Apple Terminal | 256-color fallback |
| xterm.js (VS Code) | Detected and special-cased |
| tmux | Wrapped sequences via `wrapForMultiplexer` |
| Screen | Basic support |
### Feature Degradation
The framework gracefully degrades:
- No true color → Falls back to ANSI 16-color themes
- No OSC 52 → Clipboard operations silently fail
- No mouse tracking → Click/hover events are no-ops
- No extended keys → Standard escape sequences used
- No bracketed paste → Paste detected by timing heuristic
### Synchronized Output
```ts
import { isSynchronizedOutputSupported } from '@anthropic/ink'
if (isSynchronizedOutputSupported()) {
// BSU/ESU for tear-free rendering
}
```
Uses DECSET 2026 synchronized output to prevent partial frame display.
### Bracketed Paste
Uses DECSET 2004 to distinguish paste events from rapid typing. Automatically enabled by the App component.
## Text Selection (Alt-Screen)
### Selection State
```ts
type SelectionState = {
anchor: Point | null // Drag start
focus: Point | null // Current position
isDragging: boolean
anchorSpan: { lo: Point; hi: Point; kind: 'word' | 'line' } | null
scrolledOffAbove: string[] // Text scrolled out above
scrolledOffBelow: string[] // Text scrolled out below
}
```
### Selection Operations
- **Click-drag** -- Free-form selection
- **Double-click** -- Word selection
- **Triple-click** -- Line selection
- **Shift+Arrow** -- Extend selection from keyboard
- **Drag-to-scroll** -- Auto-scroll when dragging near edges
### noSelect Regions
Exclude areas from selection (gutters, line numbers):
```tsx
<Box noSelect={true}>
<Text>1 </Text>
</Box>
<Box>
<Text>code here</Text> {/* Only this is selectable */}
</Box>
```
### Soft-Wrap Awareness
Selection correctly handles text that was wrapped across multiple rows:
- Wrapped lines are joined when copied
- Trailing whitespace is trimmed
- The `softWrap` bitmap tracks which rows are continuations

View File

@@ -0,0 +1,46 @@
# @anthropic/ink Documentation
A terminal React rendering framework for building rich command-line interfaces.
## Architecture Overview
`@anthropic/ink` is a forked/internal Ink framework that renders React components directly to the terminal using ANSI escape sequences. It uses Yoga (via a custom layout engine) for Flexbox layout, a custom React reconciler for terminal DOM, and a screen-buffer differ for efficient updates.
### Three-Layer Architecture
```
┌─────────────────────────────────────────┐
│ Layer 3: Theme │
│ ThemeProvider, ThemedBox, ThemedText, │
│ Dialog, FuzzyPicker, ProgressBar, etc. │
├─────────────────────────────────────────┤
│ Layer 2: Components │
│ Box, Text, ScrollBox, Button, Link, │
│ Newline, Spacer, AlternateScreen │
├─────────────────────────────────────────┤
│ Layer 1: Core │
│ Reconciler, Layout (Yoga), Terminal │
│ I/O, Screen Buffer, Event System │
└─────────────────────────────────────────┘
```
- **Core** (`src/core/`) -- Rendering engine: React reconciler, Yoga flexbox layout, terminal I/O, screen buffer with diff-based updates, event system (keyboard, mouse, focus, click).
- **Components** (`src/components/`) -- UI primitives: `Box`, `Text`, `ScrollBox`, `Button`, `Link`, `Newline`, `Spacer`, etc. Plus context providers (`App`, `StdinContext`).
- **Theme** (`src/theme/`) -- Theme system: `ThemeProvider`, theme-aware `Box`/`Text` wrappers, and design-system components (`Dialog`, `FuzzyPicker`, `ProgressBar`, `Tabs`, etc.).
### Documentation
| Chapter | File | Contents |
|---------|------|----------|
| 1 | [Getting Started](./01-getting-started.md) | Installation, rendering, basic concepts |
| 2 | [Layout System](./02-layout.md) | Box, Flexbox, Yoga, positioning, dimensions |
| 3 | [Text & Styling](./03-text-and-styling.md) | Text component, colors, text wrapping, ANSI styling |
| 4 | [Theme System](./04-theme-system.md) | ThemeProvider, themes, ThemedBox, ThemedText, color() |
| 5 | [Design System Components](./05-design-system.md) | Dialog, ProgressBar, FuzzyPicker, Tabs, Spinner, etc. |
| 6 | [Scrolling](./06-scrolling.md) | ScrollBox, sticky scroll, imperative scroll API |
| 7 | [User Input](./07-user-input.md) | useInput, Key types, raw mode, mouse events |
| 8 | [Keybinding System](./08-keybindings.md) | KeybindingProvider, useKeybinding, chord sequences, parser |
| 9 | [Hooks Reference](./09-hooks-reference.md) | All hooks with full API signatures |
| 10 | [Events & Focus](./10-events-and-focus.md) | Event system, FocusManager, click/hover, tab navigation |
| 11 | [Core Architecture](./11-core-architecture.md) | Reconciler, screen buffer, terminal I/O, rendering pipeline |
| 12 | [Terminal Integration](./12-terminal-integration.md) | Alternate screen, mouse tracking, clipboard, notifications |

View File

@@ -0,0 +1,11 @@
{
"name": "@claude-code-best/agent-tools",
"version": "1.0.0",
"private": true,
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"dependencies": {
"zod": "^3.25.0"
}
}

View File

@@ -0,0 +1,34 @@
import { describe, expect, test } from 'bun:test'
import type { CoreTool, Tool, Tools, AnyObject, ToolResult, ValidationResult, PermissionResult } from '@claude-code-best/agent-tools'
import type { Tool as HostTool } from '../../src/Tool.js'
describe('agent-tools compatibility', () => {
test('CoreTool structural compatibility with host Tool', () => {
// The host's Tool should structurally satisfy CoreTool
// because it has all required fields (name, call, description, etc.)
// This test verifies the type-level compatibility at runtime
const mockHostTool: HostTool = {
name: 'test',
aliases: [],
searchHint: 'test tool',
inputSchema: {} as any,
async call() { return { data: 'ok' } as any },
async description() { return 'test' },
async prompt() { return 'test prompt' },
isConcurrencySafe: () => false,
isEnabled: () => true,
isReadOnly: () => false,
async checkPermissions() { return { behavior: 'allow' as const, updatedInput: {} } },
toAutoClassifierInput: () => '',
userFacingName: () => 'test',
maxResultSizeChars: 100000,
mapToolResultToToolResultBlockParam: () => ({ type: 'tool_result', tool_use_id: '1', content: 'ok' }),
renderToolUseMessage: () => null,
}
// This assignment should work if HostTool structurally extends CoreTool
const coreTool: CoreTool = mockHostTool as CoreTool
expect(coreTool.name).toBe('test')
expect(coreTool.isEnabled()).toBe(true)
})
})

View File

@@ -0,0 +1,63 @@
import { describe, expect, test } from 'bun:test'
import { findToolByName, toolMatchesName } from '../registry.js'
import type { CoreTool, Tools } from '../types.js'
describe('toolMatchesName', () => {
test('matches primary name', () => {
expect(toolMatchesName({ name: 'bash' }, 'bash')).toBe(true)
})
test('does not match different name', () => {
expect(toolMatchesName({ name: 'bash' }, 'read')).toBe(false)
})
test('matches alias', () => {
expect(toolMatchesName({ name: 'bash', aliases: ['shell', 'sh'] }, 'shell')).toBe(true)
expect(toolMatchesName({ name: 'bash', aliases: ['shell', 'sh'] }, 'sh')).toBe(true)
})
test('handles empty aliases', () => {
expect(toolMatchesName({ name: 'bash', aliases: [] }, 'bash')).toBe(true)
expect(toolMatchesName({ name: 'bash', aliases: [] }, 'shell')).toBe(false)
})
test('handles undefined aliases', () => {
expect(toolMatchesName({ name: 'bash' }, 'bash')).toBe(true)
expect(toolMatchesName({ name: 'bash' }, 'shell')).toBe(false)
})
})
describe('findToolByName', () => {
const tools: Tools = [
{ name: 'bash' } as CoreTool,
{ name: 'read', aliases: ['cat'] } as CoreTool,
{ name: 'write', aliases: ['edit'] } as CoreTool,
]
test('finds tool by primary name', () => {
expect(findToolByName(tools, 'bash')?.name).toBe('bash')
})
test('finds tool by alias', () => {
expect(findToolByName(tools, 'cat')?.name).toBe('read')
expect(findToolByName(tools, 'edit')?.name).toBe('write')
})
test('returns undefined for unknown name', () => {
expect(findToolByName(tools, 'unknown')).toBeUndefined()
})
test('handles empty tools array', () => {
expect(findToolByName([], 'bash')).toBeUndefined()
})
test('returns first match for duplicate names', () => {
const dupTools: Tools = [
{ name: 'tool', aliases: ['a'] } as CoreTool,
{ name: 'tool', aliases: ['b'] } as CoreTool,
]
const found = findToolByName(dupTools, 'tool')
expect(found).toBeDefined()
expect(found!.aliases).toContain('a')
})
})

View File

@@ -0,0 +1,18 @@
// agent-tools — Tool interface definitions and registry utilities
// Pure types + pure functions, zero runtime dependencies
export type {
AnyObject,
ToolInputJSONSchema,
ToolProgressData,
ToolProgress,
ToolCallProgress,
ToolResult,
ValidationResult,
PermissionResult,
CoreTool,
Tool,
Tools,
} from './types.js'
export { findToolByName, toolMatchesName } from './registry.js'

View File

@@ -0,0 +1,21 @@
import type { CoreTool, Tools } from './types.js'
/**
* Checks if a tool matches the given name (primary name or alias).
*/
export function toolMatchesName(
tool: { name: string; aliases?: string[] },
name: string,
): boolean {
return tool.name === name || (tool.aliases?.includes(name) ?? false)
}
/**
* Finds a tool by name or alias from a list of tools.
*/
export function findToolByName(
tools: Tools,
name: string,
): CoreTool | undefined {
return tools.find(t => toolMatchesName(t, name))
}

View File

@@ -0,0 +1,221 @@
// agent-tools — Core Tool interface definitions
// Protocol-level types, independent of any host framework
import type { z } from 'zod/v4'
// ============================================================================
// Schema types
// ============================================================================
/**
* Zod schema type for any object with string keys.
* Used as the Input generic constraint for Tool.
*/
export type AnyObject = z.ZodType<{ [key: string]: unknown }>
/**
* JSON Schema format for MCP tool input schemas.
* MCP servers provide this directly instead of Zod schemas.
*/
export type ToolInputJSONSchema = {
[x: string]: unknown
type: 'object'
properties?: {
[x: string]: unknown
}
}
// ============================================================================
// Progress types
// ============================================================================
/**
* Progress data from a running tool. Host defines concrete subtypes.
* Typed as `any` at the protocol level — the host assigns real shapes.
*/
export type ToolProgressData = any
/**
* A progress event from a tool execution.
*/
export type ToolProgress<P extends ToolProgressData = ToolProgressData> = {
toolUseID: string
data: P
}
/**
* Callback for receiving progress updates during tool execution.
*/
export type ToolCallProgress<P extends ToolProgressData = ToolProgressData> = (
progress: ToolProgress<P>,
) => void
// ============================================================================
// Result types
// ============================================================================
/**
* Result returned by a tool's call() method.
* @template T - The output data type
* @template Message - The message type (host-specific, defaults to unknown)
*/
export type ToolResult<T, Message = unknown> = {
data: T
newMessages?: Message[]
contextModifier?: (context: any) => any
/** MCP protocol metadata (structuredContent, _meta) */
mcpMeta?: {
_meta?: Record<string, unknown>
structuredContent?: Record<string, unknown>
}
}
// ============================================================================
// Validation & Permission types
// ============================================================================
/**
* Result of tool input validation.
*/
export type ValidationResult =
| { result: true }
| { result: false; message: string; errorCode: number }
/**
* Result of a permission check for a tool invocation.
*/
export type PermissionResult =
| { behavior: 'allow'; updatedInput: Record<string, unknown> }
| { behavior: 'deny'; message: string }
| { behavior: 'passthrough' }
// ============================================================================
// Core Tool interface
// ============================================================================
/**
* The host-agnostic core Tool interface.
*
* This defines the protocol-level contract for any tool — independent of
* React rendering, specific context types, or host infrastructure.
*
* The host (Claude Code) extends this with render methods, richer context
* types, and other host-specific features. Host tools structurally satisfy
* this interface because they implement all required fields.
*
* @template Input - Zod schema type for tool input
* @template Output - Tool output data type
* @template P - Tool progress data type
* @template Context - Tool execution context type (host-specific)
*/
export interface CoreTool<
Input extends AnyObject = AnyObject,
Output = unknown,
P extends ToolProgressData = ToolProgressData,
Context = unknown,
> {
// ── Identity ──
readonly name: string
aliases?: string[]
searchHint?: string
// ── Schema ──
readonly inputSchema: Input
readonly inputJSONSchema?: ToolInputJSONSchema
outputSchema?: z.ZodType<unknown>
// ── Execution ──
call(
args: z.infer<Input>,
context: Context,
canUseTool: (...args: any[]) => Promise<any>,
parentMessage: any,
onProgress?: ToolCallProgress<P>,
): Promise<ToolResult<Output>>
// ── Description ──
description(
input: z.infer<Input>,
options: {
isNonInteractiveSession: boolean
toolPermissionContext: any
tools: readonly CoreTool[]
},
): Promise<string>
prompt(options: {
getToolPermissionContext: () => Promise<any>
tools: readonly CoreTool[]
agents: any[]
allowedAgentTypes?: string[]
}): Promise<string>
// ── Behavioral properties ──
isConcurrencySafe(input: z.infer<Input>): boolean
isEnabled(): boolean
isReadOnly(input: z.infer<Input>): boolean
isDestructive?(input: z.infer<Input>): boolean
isOpenWorld?(input: z.infer<Input>): boolean
interruptBehavior?(): 'cancel' | 'block'
requiresUserInteraction?(): boolean
// ── MCP markers ──
isMcp?: boolean
isLsp?: boolean
readonly shouldDefer?: boolean
readonly alwaysLoad?: boolean
mcpInfo?: { serverName: string; toolName: string }
// ── Permissions ──
validateInput?(
input: z.infer<Input>,
context: Context,
): Promise<ValidationResult>
checkPermissions(
input: z.infer<Input>,
context: Context,
): Promise<PermissionResult>
// ── Utility ──
inputsEquivalent?(a: z.infer<Input>, b: z.infer<Input>): boolean
getPath?(input: z.infer<Input>): string
toAutoClassifierInput(input: z.infer<Input>): unknown
backfillObservableInput?(input: Record<string, unknown>): void
// ── Output ──
maxResultSizeChars: number
userFacingName(input: Partial<z.infer<Input>> | undefined): string
mapToolResultToToolResultBlockParam(
content: Output,
toolUseID: string,
): any
// ── Optional output helpers ──
isResultTruncated?(output: Output): boolean
getToolUseSummary?(input: Partial<z.infer<Input>> | undefined): string | null
getActivityDescription?(
input: Partial<z.infer<Input>> | undefined,
): string | null
isTransparentWrapper?(): boolean
isSearchOrReadCommand?(input: z.infer<Input>): {
isSearch: boolean
isRead: boolean
isList?: boolean
}
}
/**
* A tool with a generic context type.
* This is the default export — hosts can specify their own Context type.
*/
export type Tool<
Input extends AnyObject = AnyObject,
Output = unknown,
P extends ToolProgressData = ToolProgressData,
> = CoreTool<Input, Output, P>
/**
* A collection of tools.
*/
export type Tools = readonly CoreTool[]

View File

@@ -0,0 +1,16 @@
{
"name": "@claude-code-best/builtin-tools",
"version": "1.0.0",
"private": true,
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./tools/*": "./src/tools/*",
"./utils": "./src/utils.ts"
},
"dependencies": {
"@claude-code-best/agent-tools": "workspace:*"
}
}

View File

@@ -0,0 +1,70 @@
// builtin-tools — All tool implementations for Claude Code
// This barrel file re-exports the main tool constants and utilities.
// For specific submodules, use deep imports: 'builtin-tools/tools/XTool/XTool.js'
// =============================================================================
// Main tool exports (used by src/tools.ts)
// =============================================================================
// Core tools
export { AgentTool } from './tools/AgentTool/AgentTool.js'
export { AskUserQuestionTool } from './tools/AskUserQuestionTool/AskUserQuestionTool.js'
export { BashTool } from './tools/BashTool/BashTool.js'
export { BriefTool } from './tools/BriefTool/BriefTool.js'
export { ConfigTool } from './tools/ConfigTool/ConfigTool.js'
export { EnterPlanModeTool } from './tools/EnterPlanModeTool/EnterPlanModeTool.js'
export { EnterWorktreeTool } from './tools/EnterWorktreeTool/EnterWorktreeTool.js'
export { ExitPlanModeV2Tool } from './tools/ExitPlanModeTool/ExitPlanModeV2Tool.js'
export { ExitWorktreeTool } from './tools/ExitWorktreeTool/ExitWorktreeTool.js'
export { FileEditTool } from './tools/FileEditTool/FileEditTool.js'
export { FileReadTool } from './tools/FileReadTool/FileReadTool.js'
export { FileWriteTool } from './tools/FileWriteTool/FileWriteTool.js'
export { GlobTool } from './tools/GlobTool/GlobTool.js'
export { GrepTool } from './tools/GrepTool/GrepTool.js'
export { LSPTool } from './tools/LSPTool/LSPTool.js'
export { ListMcpResourcesTool } from './tools/ListMcpResourcesTool/ListMcpResourcesTool.js'
export { ReadMcpResourceTool } from './tools/ReadMcpResourceTool/ReadMcpResourceTool.js'
export { NotebookEditTool } from './tools/NotebookEditTool/NotebookEditTool.js'
export { SkillTool } from './tools/SkillTool/SkillTool.js'
export { TaskOutputTool } from './tools/TaskOutputTool/TaskOutputTool.js'
export { TaskStopTool } from './tools/TaskStopTool/TaskStopTool.js'
export { TodoWriteTool } from './tools/TodoWriteTool/TodoWriteTool.js'
export { ToolSearchTool } from './tools/ToolSearchTool/ToolSearchTool.js'
export { TungstenTool } from './tools/TungstenTool/TungstenTool.js'
export { WebFetchTool } from './tools/WebFetchTool/WebFetchTool.js'
export { WebSearchTool } from './tools/WebSearchTool/WebSearchTool.js'
export { TestingPermissionTool } from './tools/testing/TestingPermissionTool.js'
// Feature-gated tools
export { OVERFLOW_TEST_TOOL_NAME } from './tools/OverflowTestTool/OverflowTestTool.js'
export { CtxInspectTool } from './tools/CtxInspectTool/CtxInspectTool.js'
export { ListPeersTool } from './tools/ListPeersTool/ListPeersTool.js'
export { MonitorTool } from './tools/MonitorTool/MonitorTool.js'
export { PowerShellTool } from './tools/PowerShellTool/PowerShellTool.js'
export { PushNotificationTool } from './tools/PushNotificationTool/PushNotificationTool.js'
export { REPLTool } from './tools/REPLTool/REPLTool.js'
export { RemoteTriggerTool } from './tools/RemoteTriggerTool/RemoteTriggerTool.js'
export { ReviewArtifactTool } from './tools/ReviewArtifactTool/ReviewArtifactTool.js'
export { CronCreateTool } from './tools/ScheduleCronTool/CronCreateTool.js'
export { CronDeleteTool } from './tools/ScheduleCronTool/CronDeleteTool.js'
export { CronListTool } from './tools/ScheduleCronTool/CronListTool.js'
export { SendMessageTool } from './tools/SendMessageTool/SendMessageTool.js'
export { SendUserFileTool } from './tools/SendUserFileTool/SendUserFileTool.js'
export { SleepTool } from './tools/SleepTool/SleepTool.js'
export { SnipTool } from './tools/SnipTool/SnipTool.js'
export { SubscribePRTool } from './tools/SubscribePRTool/SubscribePRTool.js'
export { SuggestBackgroundPRTool } from './tools/SuggestBackgroundPRTool/SuggestBackgroundPRTool.js'
export { TeamCreateTool } from './tools/TeamCreateTool/TeamCreateTool.js'
export { TeamDeleteTool } from './tools/TeamDeleteTool/TeamDeleteTool.js'
export { TerminalCaptureTool } from './tools/TerminalCaptureTool/TerminalCaptureTool.js'
export { VerifyPlanExecutionTool } from './tools/VerifyPlanExecutionTool/VerifyPlanExecutionTool.js'
export { WebBrowserTool } from './tools/WebBrowserTool/WebBrowserTool.js'
export { WorkflowTool } from './tools/WorkflowTool/WorkflowTool.js'
export { initBundledWorkflows } from './tools/WorkflowTool/bundled/index.js'
export { getWorkflowCommands } from './tools/WorkflowTool/createWorkflowCommand.js'
// Constants
export { SYNTHETIC_OUTPUT_TOOL_NAME, createSyntheticOutputTool } from './tools/SyntheticOutputTool/SyntheticOutputTool.js'
// Shared utilities
export { tagMessagesWithToolUseID, getToolUseIDFromParentMessage } from './tools/utils.js'

View File

@@ -11,19 +11,19 @@ import { z } from 'zod/v4'
import {
clearInvokedSkillsForAgent,
getSdkAgentProgressSummariesEnabled,
} from '../../bootstrap/state.js'
} from 'src/bootstrap/state.js'
import {
enhanceSystemPromptWithEnvDetails,
getSystemPrompt,
} from '../../constants/prompts.js'
import { isCoordinatorMode } from '../../coordinator/coordinatorMode.js'
import { startAgentSummarization } from '../../services/AgentSummary/agentSummary.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'
} from 'src/constants/prompts.js'
import { isCoordinatorMode } from 'src/coordinator/coordinatorMode.js'
import { startAgentSummarization } from 'src/services/AgentSummary/agentSummary.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from 'src/services/analytics/growthbook.js'
import {
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
logEvent,
} from '../../services/analytics/index.js'
import { clearDumpState } from '../../services/api/dumpPrompts.js'
} from 'src/services/analytics/index.js'
import { clearDumpState } from 'src/services/api/dumpPrompts.js'
import {
completeAgentTask as completeAsyncAgent,
createActivityDescriptionResolver,
@@ -39,53 +39,53 @@ import {
unregisterAgentForeground,
updateAgentProgress as updateAsyncAgentProgress,
updateProgressFromMessage,
} from '../../tasks/LocalAgentTask/LocalAgentTask.js'
} from 'src/tasks/LocalAgentTask/LocalAgentTask.js'
import {
checkRemoteAgentEligibility,
formatPreconditionError,
getRemoteTaskSessionUrl,
registerRemoteAgentTask,
type BackgroundRemoteSessionPrecondition,
} from '../../tasks/RemoteAgentTask/RemoteAgentTask.js'
import { assembleToolPool } from '../../tools.js'
import { asAgentId } from '../../types/ids.js'
import { runWithAgentContext, type SubagentContext } from '../../utils/agentContext.js'
import { isAgentSwarmsEnabled } from '../../utils/agentSwarmsEnabled.js'
import { getCwd, runWithCwdOverride } from '../../utils/cwd.js'
import { logForDebugging } from '../../utils/debug.js'
import { isEnvTruthy } from '../../utils/envUtils.js'
import { AbortError, errorMessage, toError } from '../../utils/errors.js'
import type { CacheSafeParams } from '../../utils/forkedAgent.js'
import { lazySchema } from '../../utils/lazySchema.js'
} from 'src/tasks/RemoteAgentTask/RemoteAgentTask.js'
import { assembleToolPool } from 'src/tools.js'
import { asAgentId } from 'src/types/ids.js'
import { runWithAgentContext, type SubagentContext } from 'src/utils/agentContext.js'
import { isAgentSwarmsEnabled } from 'src/utils/agentSwarmsEnabled.js'
import { getCwd, runWithCwdOverride } from 'src/utils/cwd.js'
import { logForDebugging } from 'src/utils/debug.js'
import { isEnvTruthy } from 'src/utils/envUtils.js'
import { AbortError, errorMessage, toError } from 'src/utils/errors.js'
import type { CacheSafeParams } from 'src/utils/forkedAgent.js'
import { lazySchema } from 'src/utils/lazySchema.js'
import {
createUserMessage,
extractTextContent,
isSyntheticMessage,
normalizeMessages,
} from '../../utils/messages.js'
import { getAgentModel } from '../../utils/model/agent.js'
import { permissionModeSchema } from '../../utils/permissions/PermissionMode.js'
import type { PermissionResult } from '../../utils/permissions/PermissionResult.js'
} from 'src/utils/messages.js'
import { getAgentModel } from 'src/utils/model/agent.js'
import { permissionModeSchema } from 'src/utils/permissions/PermissionMode.js'
import type { PermissionResult } from 'src/utils/permissions/PermissionResult.js'
import {
filterDeniedAgents,
getDenyRuleForAgent,
} from '../../utils/permissions/permissions.js'
import { enqueueSdkEvent } from '../../utils/sdkEventQueue.js'
import { writeAgentMetadata } from '../../utils/sessionStorage.js'
import { sleep } from '../../utils/sleep.js'
import { buildEffectiveSystemPrompt } from '../../utils/systemPrompt.js'
import { asSystemPrompt } from '../../utils/systemPromptType.js'
import { getTaskOutputPath } from '../../utils/task/diskOutput.js'
import { getParentSessionId, isTeammate } from '../../utils/teammate.js'
import { isInProcessTeammate } from '../../utils/teammateContext.js'
import { teleportToRemote } from '../../utils/teleport.js'
import { getAssistantMessageContentLength } from '../../utils/tokens.js'
import { createAgentId } from '../../utils/uuid.js'
} from 'src/utils/permissions/permissions.js'
import { enqueueSdkEvent } from 'src/utils/sdkEventQueue.js'
import { writeAgentMetadata } from 'src/utils/sessionStorage.js'
import { sleep } from 'src/utils/sleep.js'
import { buildEffectiveSystemPrompt } from 'src/utils/systemPrompt.js'
import { asSystemPrompt } from 'src/utils/systemPromptType.js'
import { getTaskOutputPath } from 'src/utils/task/diskOutput.js'
import { getParentSessionId, isTeammate } from 'src/utils/teammate.js'
import { isInProcessTeammate } from 'src/utils/teammateContext.js'
import { teleportToRemote } from 'src/utils/teleport.js'
import { getAssistantMessageContentLength } from 'src/utils/tokens.js'
import { createAgentId } from 'src/utils/uuid.js'
import {
createAgentWorktree,
hasWorktreeChanges,
removeAgentWorktree,
} from '../../utils/worktree.js'
} from 'src/utils/worktree.js'
import { BASH_TOOL_NAME } from '../BashTool/toolName.js'
import { BackgroundHint } from '../BashTool/UI.js'
import { FILE_READ_TOOL_NAME } from '../FileReadTool/prompt.js'
@@ -136,7 +136,7 @@ import {
/* eslint-disable @typescript-eslint/no-require-imports */
const proactiveModule =
feature('PROACTIVE') || feature('KAIROS')
? (require('../../proactive/index.js') as typeof import('../../proactive/index.js'))
? (require('src/proactive/index.js') as typeof import('src/proactive/index.js'))
: null
/* eslint-enable @typescript-eslint/no-require-imports */
@@ -332,7 +332,7 @@ export type RemoteLaunchedOutput = {
type InternalOutput = Output | TeammateSpawnedOutput | RemoteLaunchedOutput
import type { AgentToolProgress, ShellProgress } from '../../types/tools.js'
import type { AgentToolProgress, ShellProgress } from 'src/types/tools.js'
// AgentTool forwards both its own progress events and shell progress
// events from the sub-agent so the SDK receives tool_progress updates during bash/powershell runs.
export type Progress = AgentToolProgress | ShellProgress

View File

@@ -12,37 +12,37 @@ import {
} from 'src/components/CtrlOToExpand.js'
import { Byline, KeyboardShortcutHint } from '@anthropic/ink'
import type { z } from 'zod/v4'
import { AgentProgressLine } from '../../components/AgentProgressLine.js'
import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js'
import { FallbackToolUseRejectedMessage } from '../../components/FallbackToolUseRejectedMessage.js'
import { Markdown } from '../../components/Markdown.js'
import { Message as MessageComponent } from '../../components/Message.js'
import { MessageResponse } from '../../components/MessageResponse.js'
import { ToolUseLoader } from '../../components/ToolUseLoader.js'
import { AgentProgressLine } from 'src/components/AgentProgressLine.js'
import { FallbackToolUseErrorMessage } from 'src/components/FallbackToolUseErrorMessage.js'
import { FallbackToolUseRejectedMessage } from 'src/components/FallbackToolUseRejectedMessage.js'
import { Markdown } from 'src/components/Markdown.js'
import { Message as MessageComponent } from 'src/components/Message.js'
import { MessageResponse } from 'src/components/MessageResponse.js'
import { ToolUseLoader } from 'src/components/ToolUseLoader.js'
import { Box, Text } from '@anthropic/ink'
import { getDumpPromptsPath } from '../../services/api/dumpPrompts.js'
import { findToolByName, type Tools } from '../../Tool.js'
import type { Message, ProgressMessage } from '../../types/message.js'
import type { AgentToolProgress } from '../../types/tools.js'
import { count } from '../../utils/array.js'
import { getDumpPromptsPath } from 'src/services/api/dumpPrompts.js'
import { findToolByName, type Tools } from 'src/Tool.js'
import type { Message, ProgressMessage } from 'src/types/message.js'
import type { AgentToolProgress } from 'src/types/tools.js'
import { count } from 'src/utils/array.js'
import {
getSearchOrReadFromContent,
getSearchReadSummaryText,
} from '../../utils/collapseReadSearch.js'
import { getDisplayPath } from '../../utils/file.js'
import { formatDuration, formatNumber } from '../../utils/format.js'
} from 'src/utils/collapseReadSearch.js'
import { getDisplayPath } from 'src/utils/file.js'
import { formatDuration, formatNumber } from 'src/utils/format.js'
import {
buildSubagentLookups,
createAssistantMessage,
EMPTY_LOOKUPS,
} from '../../utils/messages.js'
import type { ModelAlias } from '../../utils/model/aliases.js'
} from 'src/utils/messages.js'
import type { ModelAlias } from 'src/utils/model/aliases.js'
import {
getMainLoopModel,
parseUserSpecifiedModel,
renderModelName,
} from '../../utils/model/model.js'
import type { Theme, ThemeName } from '../../utils/theme.js'
} from 'src/utils/model/model.js'
import type { Theme, ThemeName } from 'src/utils/theme.js'
import type {
outputSchema,
Progress,

View File

@@ -1,11 +1,11 @@
import { mock, describe, expect, test } from "bun:test";
// Mock heavy deps
mock.module("../../utils/model/agent.js", () => ({
mock.module("src/utils/model/agent.js", () => ({
getDefaultSubagentModel: () => undefined,
}));
mock.module("../../utils/settings/constants.js", () => ({
mock.module("src/utils/settings/constants.js", () => ({
getSourceDisplayName: (source: string) => source,
}));

View File

@@ -1,5 +1,5 @@
import { getAgentColorMap } from '../../bootstrap/state.js'
import type { Theme } from '../../utils/theme.js'
import { getAgentColorMap } from 'src/bootstrap/state.js'
import type { Theme } from 'src/utils/theme.js'
export type AgentColorName =
| 'red'

View File

@@ -3,11 +3,11 @@
* Used by both the CLI `claude agents` handler and the interactive `/agents` command.
*/
import { getDefaultSubagentModel } from '../../utils/model/agent.js'
import { getDefaultSubagentModel } from 'src/utils/model/agent.js'
import {
getSourceDisplayName,
type SettingSource,
} from '../../utils/settings/constants.js'
} from 'src/utils/settings/constants.js'
import type { AgentDefinition } from './loadAgentsDir.js'
type AgentSource = SettingSource | 'built-in' | 'plugin'

View File

@@ -1,13 +1,13 @@
import { join, normalize, sep } from 'path'
import { getProjectRoot } from '../../bootstrap/state.js'
import { getProjectRoot } from 'src/bootstrap/state.js'
import {
buildMemoryPrompt,
ensureMemoryDirExists,
} from '../../memdir/memdir.js'
import { getMemoryBaseDir } from '../../memdir/paths.js'
import { getCwd } from '../../utils/cwd.js'
import { findCanonicalGitRoot } from '../../utils/git.js'
import { sanitizePath } from '../../utils/path.js'
} from 'src/memdir/memdir.js'
import { getMemoryBaseDir } from 'src/memdir/paths.js'
import { getCwd } from 'src/utils/cwd.js'
import { findCanonicalGitRoot } from 'src/utils/git.js'
import { sanitizePath } from 'src/utils/path.js'
// Persistent agent memory scope: 'user' (~/.claude/agent-memory/), 'project' (.claude/agent-memory/), or 'local' (.claude/agent-memory-local/)
export type AgentMemoryScope = 'user' | 'project' | 'local'

View File

@@ -1,10 +1,10 @@
import { mkdir, readdir, readFile, unlink, writeFile } from 'fs/promises'
import { join } from 'path'
import { z } from 'zod/v4'
import { getCwd } from '../../utils/cwd.js'
import { logForDebugging } from '../../utils/debug.js'
import { lazySchema } from '../../utils/lazySchema.js'
import { jsonParse, jsonStringify } from '../../utils/slowOperations.js'
import { getCwd } from 'src/utils/cwd.js'
import { logForDebugging } from 'src/utils/debug.js'
import { lazySchema } from 'src/utils/lazySchema.js'
import { jsonParse, jsonStringify } from 'src/utils/slowOperations.js'
import { type AgentMemoryScope, getAgentMemoryDir } from './agentMemory.js'
const SNAPSHOT_BASE = 'agent-memory-snapshots'

View File

@@ -1,26 +1,26 @@
import { feature } from 'bun:bundle'
import { z } from 'zod/v4'
import { clearInvokedSkillsForAgent } from '../../bootstrap/state.js'
import { clearInvokedSkillsForAgent } from 'src/bootstrap/state.js'
import {
ALL_AGENT_DISALLOWED_TOOLS,
ASYNC_AGENT_ALLOWED_TOOLS,
CUSTOM_AGENT_DISALLOWED_TOOLS,
IN_PROCESS_TEAMMATE_ALLOWED_TOOLS,
} from '../../constants/tools.js'
import { startAgentSummarization } from '../../services/AgentSummary/agentSummary.js'
} from 'src/constants/tools.js'
import { startAgentSummarization } from 'src/services/AgentSummary/agentSummary.js'
import {
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
logEvent,
} from '../../services/analytics/index.js'
import { clearDumpState } from '../../services/api/dumpPrompts.js'
import type { AppState } from '../../state/AppState.js'
} from 'src/services/analytics/index.js'
import { clearDumpState } from 'src/services/api/dumpPrompts.js'
import type { AppState } from 'src/state/AppState.js'
import type {
Tool,
ToolPermissionContext,
Tools,
ToolUseContext,
} from '../../Tool.js'
import { toolMatchesName } from '../../Tool.js'
} from 'src/Tool.js'
import { toolMatchesName } from 'src/Tool.js'
import {
completeAgentTask as completeAsyncAgent,
createActivityDescriptionResolver,
@@ -34,28 +34,28 @@ import {
type ProgressTracker,
updateAgentProgress as updateAsyncAgentProgress,
updateProgressFromMessage,
} from '../../tasks/LocalAgentTask/LocalAgentTask.js'
import { asAgentId } from '../../types/ids.js'
import type { Message as MessageType, ContentItem } from '../../types/message.js'
import { isAgentSwarmsEnabled } from '../../utils/agentSwarmsEnabled.js'
import { logForDebugging } from '../../utils/debug.js'
import { isInProtectedNamespace } from '../../utils/envUtils.js'
import { AbortError, errorMessage } from '../../utils/errors.js'
import type { CacheSafeParams } from '../../utils/forkedAgent.js'
import { lazySchema } from '../../utils/lazySchema.js'
} from 'src/tasks/LocalAgentTask/LocalAgentTask.js'
import { asAgentId } from 'src/types/ids.js'
import type { Message as MessageType, ContentItem } from 'src/types/message.js'
import { isAgentSwarmsEnabled } from 'src/utils/agentSwarmsEnabled.js'
import { logForDebugging } from 'src/utils/debug.js'
import { isInProtectedNamespace } from 'src/utils/envUtils.js'
import { AbortError, errorMessage } from 'src/utils/errors.js'
import type { CacheSafeParams } from 'src/utils/forkedAgent.js'
import { lazySchema } from 'src/utils/lazySchema.js'
import {
extractTextContent,
getLastAssistantMessage,
} from '../../utils/messages.js'
import type { PermissionMode } from '../../utils/permissions/PermissionMode.js'
import { permissionRuleValueFromString } from '../../utils/permissions/permissionRuleParser.js'
} from 'src/utils/messages.js'
import type { PermissionMode } from 'src/utils/permissions/PermissionMode.js'
import { permissionRuleValueFromString } from 'src/utils/permissions/permissionRuleParser.js'
import {
buildTranscriptForClassifier,
classifyYoloAction,
} from '../../utils/permissions/yoloClassifier.js'
import { emitTaskProgress as emitTaskProgressEvent } from '../../utils/task/sdkProgress.js'
import { isInProcessTeammate } from '../../utils/teammateContext.js'
import { getTokenCountFromUsage } from '../../utils/tokens.js'
} from 'src/utils/permissions/yoloClassifier.js'
import { emitTaskProgress as emitTaskProgressEvent } from 'src/utils/task/sdkProgress.js'
import { isInProcessTeammate } from 'src/utils/teammateContext.js'
import { getTokenCountFromUsage } from 'src/utils/tokens.js'
import { EXIT_PLAN_MODE_V2_TOOL_NAME } from '../ExitPlanModeTool/constants.js'
import { AGENT_TOOL_NAME, LEGACY_AGENT_TOOL_NAME } from './constants.js'
import type { AgentDefinition } from './loadAgentsDir.js'

View File

@@ -1,14 +1,14 @@
import { BASH_TOOL_NAME } from 'src/tools/BashTool/toolName.js'
import { FILE_READ_TOOL_NAME } from 'src/tools/FileReadTool/prompt.js'
import { GLOB_TOOL_NAME } from 'src/tools/GlobTool/prompt.js'
import { GREP_TOOL_NAME } from 'src/tools/GrepTool/prompt.js'
import { SEND_MESSAGE_TOOL_NAME } from 'src/tools/SendMessageTool/constants.js'
import { WEB_FETCH_TOOL_NAME } from 'src/tools/WebFetchTool/prompt.js'
import { WEB_SEARCH_TOOL_NAME } from 'src/tools/WebSearchTool/prompt.js'
import { BASH_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/BashTool/toolName.js'
import { FILE_READ_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/FileReadTool/prompt.js'
import { GLOB_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/GlobTool/prompt.js'
import { GREP_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/GrepTool/prompt.js'
import { SEND_MESSAGE_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/SendMessageTool/constants.js'
import { WEB_FETCH_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/WebFetchTool/prompt.js'
import { WEB_SEARCH_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/WebSearchTool/prompt.js'
import { isUsing3PServices } from 'src/utils/auth.js'
import { hasEmbeddedSearchTools } from 'src/utils/embeddedTools.js'
import { getSettings_DEPRECATED } from 'src/utils/settings/settings.js'
import { jsonStringify } from '../../../utils/slowOperations.js'
import { jsonStringify } from 'src/utils/slowOperations.js'
import type {
AgentDefinition,
BuiltInAgentDefinition,

View File

@@ -1,11 +1,11 @@
import { BASH_TOOL_NAME } from 'src/tools/BashTool/toolName.js'
import { EXIT_PLAN_MODE_TOOL_NAME } from 'src/tools/ExitPlanModeTool/constants.js'
import { FILE_EDIT_TOOL_NAME } from 'src/tools/FileEditTool/constants.js'
import { FILE_READ_TOOL_NAME } from 'src/tools/FileReadTool/prompt.js'
import { FILE_WRITE_TOOL_NAME } from 'src/tools/FileWriteTool/prompt.js'
import { GLOB_TOOL_NAME } from 'src/tools/GlobTool/prompt.js'
import { GREP_TOOL_NAME } from 'src/tools/GrepTool/prompt.js'
import { NOTEBOOK_EDIT_TOOL_NAME } from 'src/tools/NotebookEditTool/constants.js'
import { BASH_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/BashTool/toolName.js'
import { EXIT_PLAN_MODE_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/ExitPlanModeTool/constants.js'
import { FILE_EDIT_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/FileEditTool/constants.js'
import { FILE_READ_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/FileReadTool/prompt.js'
import { FILE_WRITE_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/FileWriteTool/prompt.js'
import { GLOB_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/GlobTool/prompt.js'
import { GREP_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/GrepTool/prompt.js'
import { NOTEBOOK_EDIT_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/NotebookEditTool/constants.js'
import { hasEmbeddedSearchTools } from 'src/utils/embeddedTools.js'
import { AGENT_TOOL_NAME } from '../constants.js'
import type { BuiltInAgentDefinition } from '../loadAgentsDir.js'

View File

@@ -1,11 +1,11 @@
import { BASH_TOOL_NAME } from 'src/tools/BashTool/toolName.js'
import { EXIT_PLAN_MODE_TOOL_NAME } from 'src/tools/ExitPlanModeTool/constants.js'
import { FILE_EDIT_TOOL_NAME } from 'src/tools/FileEditTool/constants.js'
import { FILE_READ_TOOL_NAME } from 'src/tools/FileReadTool/prompt.js'
import { FILE_WRITE_TOOL_NAME } from 'src/tools/FileWriteTool/prompt.js'
import { GLOB_TOOL_NAME } from 'src/tools/GlobTool/prompt.js'
import { GREP_TOOL_NAME } from 'src/tools/GrepTool/prompt.js'
import { NOTEBOOK_EDIT_TOOL_NAME } from 'src/tools/NotebookEditTool/constants.js'
import { BASH_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/BashTool/toolName.js'
import { EXIT_PLAN_MODE_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/ExitPlanModeTool/constants.js'
import { FILE_EDIT_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/FileEditTool/constants.js'
import { FILE_READ_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/FileReadTool/prompt.js'
import { FILE_WRITE_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/FileWriteTool/prompt.js'
import { GLOB_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/GlobTool/prompt.js'
import { GREP_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/GrepTool/prompt.js'
import { NOTEBOOK_EDIT_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/NotebookEditTool/constants.js'
import { hasEmbeddedSearchTools } from 'src/utils/embeddedTools.js'
import { AGENT_TOOL_NAME } from '../constants.js'
import type { BuiltInAgentDefinition } from '../loadAgentsDir.js'

View File

@@ -1,9 +1,9 @@
import { BASH_TOOL_NAME } from 'src/tools/BashTool/toolName.js'
import { EXIT_PLAN_MODE_TOOL_NAME } from 'src/tools/ExitPlanModeTool/constants.js'
import { FILE_EDIT_TOOL_NAME } from 'src/tools/FileEditTool/constants.js'
import { FILE_WRITE_TOOL_NAME } from 'src/tools/FileWriteTool/prompt.js'
import { NOTEBOOK_EDIT_TOOL_NAME } from 'src/tools/NotebookEditTool/constants.js'
import { WEB_FETCH_TOOL_NAME } from 'src/tools/WebFetchTool/prompt.js'
import { BASH_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/BashTool/toolName.js'
import { EXIT_PLAN_MODE_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/ExitPlanModeTool/constants.js'
import { FILE_EDIT_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/FileEditTool/constants.js'
import { FILE_WRITE_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/FileWriteTool/prompt.js'
import { NOTEBOOK_EDIT_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/NotebookEditTool/constants.js'
import { WEB_FETCH_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/WebFetchTool/prompt.js'
import { AGENT_TOOL_NAME } from '../constants.js'
import type { BuiltInAgentDefinition } from '../loadAgentsDir.js'

View File

@@ -1,7 +1,7 @@
import { feature } from 'bun:bundle'
import { getIsNonInteractiveSession } from '../../bootstrap/state.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'
import { isEnvTruthy } from '../../utils/envUtils.js'
import { getIsNonInteractiveSession } from 'src/bootstrap/state.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from 'src/services/analytics/growthbook.js'
import { isEnvTruthy } from 'src/utils/envUtils.js'
import { CLAUDE_CODE_GUIDE_AGENT } from './built-in/claudeCodeGuideAgent.js'
import { EXPLORE_AGENT } from './built-in/exploreAgent.js'
import { GENERAL_PURPOSE_AGENT } from './built-in/generalPurposeAgent.js'
@@ -36,7 +36,7 @@ export function getBuiltInAgents(): AgentDefinition[] {
if (isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)) {
/* eslint-disable @typescript-eslint/no-require-imports */
const { getCoordinatorAgents } =
require('../../coordinator/workerAgent.js') as typeof import('../../coordinator/workerAgent.js')
require('src/coordinator/workerAgent.js') as typeof import('src/coordinator/workerAgent.js')
/* eslint-enable @typescript-eslint/no-require-imports */
return getCoordinatorAgents()
}

View File

@@ -1,18 +1,18 @@
import { feature } from 'bun:bundle'
import type { BetaToolUseBlock } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'
import { randomUUID } from 'crypto'
import { getIsNonInteractiveSession } from '../../bootstrap/state.js'
import { getIsNonInteractiveSession } from 'src/bootstrap/state.js'
import {
FORK_BOILERPLATE_TAG,
FORK_DIRECTIVE_PREFIX,
} from '../../constants/xml.js'
import { isCoordinatorMode } from '../../coordinator/coordinatorMode.js'
} from 'src/constants/xml.js'
import { isCoordinatorMode } from 'src/coordinator/coordinatorMode.js'
import type {
AssistantMessage,
Message as MessageType,
} from '../../types/message.js'
import { logForDebugging } from '../../utils/debug.js'
import { createUserMessage } from '../../utils/messages.js'
} from 'src/types/message.js'
import { logForDebugging } from 'src/utils/debug.js'
import { createUserMessage } from 'src/utils/messages.js'
import type { BuiltInAgentDefinition } from './loadAgentsDir.js'
/**

View File

@@ -3,41 +3,41 @@ import memoize from 'lodash-es/memoize.js'
import { basename } from 'path'
import type { SettingSource } from 'src/utils/settings/constants.js'
import { z } from 'zod/v4'
import { isAutoMemoryEnabled } from '../../memdir/paths.js'
import { isAutoMemoryEnabled } from 'src/memdir/paths.js'
import {
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
logEvent,
} from '../../services/analytics/index.js'
} from 'src/services/analytics/index.js'
import {
type McpServerConfig,
McpServerConfigSchema,
} from '../../services/mcp/types.js'
import type { ToolUseContext } from '../../Tool.js'
import { logForDebugging } from '../../utils/debug.js'
} from 'src/services/mcp/types.js'
import type { ToolUseContext } from 'src/Tool.js'
import { logForDebugging } from 'src/utils/debug.js'
import {
EFFORT_LEVELS,
type EffortValue,
parseEffortValue,
} from '../../utils/effort.js'
import { isEnvTruthy } from '../../utils/envUtils.js'
import { parsePositiveIntFromFrontmatter } from '../../utils/frontmatterParser.js'
import { lazySchema } from '../../utils/lazySchema.js'
import { logError } from '../../utils/log.js'
} from 'src/utils/effort.js'
import { isEnvTruthy } from 'src/utils/envUtils.js'
import { parsePositiveIntFromFrontmatter } from 'src/utils/frontmatterParser.js'
import { lazySchema } from 'src/utils/lazySchema.js'
import { logError } from 'src/utils/log.js'
import {
loadMarkdownFilesForSubdir,
parseAgentToolsFromFrontmatter,
parseSlashCommandToolsFromFrontmatter,
} from '../../utils/markdownConfigLoader.js'
} from 'src/utils/markdownConfigLoader.js'
import {
PERMISSION_MODES,
type PermissionMode,
} from '../../utils/permissions/PermissionMode.js'
} from 'src/utils/permissions/PermissionMode.js'
import {
clearPluginAgentCache,
loadPluginAgents,
} from '../../utils/plugins/loadPluginAgents.js'
import { HooksSchema, type HooksSettings } from '../../utils/settings/types.js'
import { jsonStringify } from '../../utils/slowOperations.js'
} from 'src/utils/plugins/loadPluginAgents.js'
import { HooksSchema, type HooksSettings } from 'src/utils/settings/types.js'
import { jsonStringify } from 'src/utils/slowOperations.js'
import { FILE_EDIT_TOOL_NAME } from '../FileEditTool/constants.js'
import { FILE_READ_TOOL_NAME } from '../FileReadTool/prompt.js'
import { FILE_WRITE_TOOL_NAME } from '../FileWriteTool/prompt.js'

View File

@@ -1,9 +1,9 @@
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'
import { getSubscriptionType } from '../../utils/auth.js'
import { hasEmbeddedSearchTools } from '../../utils/embeddedTools.js'
import { isEnvDefinedFalsy, isEnvTruthy } from '../../utils/envUtils.js'
import { isTeammate } from '../../utils/teammate.js'
import { isInProcessTeammate } from '../../utils/teammateContext.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from 'src/services/analytics/growthbook.js'
import { getSubscriptionType } from 'src/utils/auth.js'
import { hasEmbeddedSearchTools } from 'src/utils/embeddedTools.js'
import { isEnvDefinedFalsy, isEnvTruthy } from 'src/utils/envUtils.js'
import { isTeammate } from 'src/utils/teammate.js'
import { isInProcessTeammate } from 'src/utils/teammateContext.js'
import { FILE_READ_TOOL_NAME } from '../FileReadTool/prompt.js'
import { FILE_WRITE_TOOL_NAME } from '../FileWriteTool/prompt.js'
import { GLOB_TOOL_NAME } from '../GlobTool/prompt.js'

View File

@@ -1,32 +1,32 @@
import { promises as fsp } from 'fs'
import { getSdkAgentProgressSummariesEnabled } from '../../bootstrap/state.js'
import { getSystemPrompt } from '../../constants/prompts.js'
import { isCoordinatorMode } from '../../coordinator/coordinatorMode.js'
import type { CanUseToolFn } from '../../hooks/useCanUseTool.js'
import type { ToolUseContext } from '../../Tool.js'
import { registerAsyncAgent } from '../../tasks/LocalAgentTask/LocalAgentTask.js'
import { assembleToolPool } from '../../tools.js'
import { asAgentId } from '../../types/ids.js'
import { runWithAgentContext } from '../../utils/agentContext.js'
import { runWithCwdOverride } from '../../utils/cwd.js'
import { logForDebugging } from '../../utils/debug.js'
import { getSdkAgentProgressSummariesEnabled } from 'src/bootstrap/state.js'
import { getSystemPrompt } from 'src/constants/prompts.js'
import { isCoordinatorMode } from 'src/coordinator/coordinatorMode.js'
import type { CanUseToolFn } from 'src/hooks/useCanUseTool.js'
import type { ToolUseContext } from 'src/Tool.js'
import { registerAsyncAgent } from 'src/tasks/LocalAgentTask/LocalAgentTask.js'
import { assembleToolPool } from 'src/tools.js'
import { asAgentId } from 'src/types/ids.js'
import { runWithAgentContext } from 'src/utils/agentContext.js'
import { runWithCwdOverride } from 'src/utils/cwd.js'
import { logForDebugging } from 'src/utils/debug.js'
import {
createUserMessage,
filterOrphanedThinkingOnlyMessages,
filterUnresolvedToolUses,
filterWhitespaceOnlyAssistantMessages,
} from '../../utils/messages.js'
import { getAgentModel } from '../../utils/model/agent.js'
import { getQuerySourceForAgent } from '../../utils/promptCategory.js'
} from 'src/utils/messages.js'
import { getAgentModel } from 'src/utils/model/agent.js'
import { getQuerySourceForAgent } from 'src/utils/promptCategory.js'
import {
getAgentTranscript,
readAgentMetadata,
} from '../../utils/sessionStorage.js'
import { buildEffectiveSystemPrompt } from '../../utils/systemPrompt.js'
import type { SystemPrompt } from '../../utils/systemPromptType.js'
import { getTaskOutputPath } from '../../utils/task/diskOutput.js'
import { getParentSessionId } from '../../utils/teammate.js'
import { reconstructForSubagentResume } from '../../utils/toolResultStorage.js'
} from 'src/utils/sessionStorage.js'
import { buildEffectiveSystemPrompt } from 'src/utils/systemPrompt.js'
import type { SystemPrompt } from 'src/utils/systemPromptType.js'
import { getTaskOutputPath } from 'src/utils/task/diskOutput.js'
import { getParentSessionId } from 'src/utils/teammate.js'
import { reconstructForSubagentResume } from 'src/utils/toolResultStorage.js'
import { runAsyncAgentLifecycle } from './agentToolUtils.js'
import { GENERAL_PURPOSE_AGENT } from './built-in/generalPurposeAgent.js'
import { FORK_AGENT, isForkSubagentEnabled } from './forkSubagent.js'

View File

@@ -3,32 +3,32 @@ import type { UUID } from 'crypto'
import { randomUUID } from 'crypto'
import uniqBy from 'lodash-es/uniqBy.js'
import { logForDebugging } from 'src/utils/debug.js'
import { getProjectRoot, getSessionId } from '../../bootstrap/state.js'
import { getCommand, getSkillToolCommands, hasCommand } from '../../commands.js'
import { getProjectRoot, getSessionId } from 'src/bootstrap/state.js'
import { getCommand, getSkillToolCommands, hasCommand } from 'src/commands.js'
import {
DEFAULT_AGENT_PROMPT,
enhanceSystemPromptWithEnvDetails,
} from '../../constants/prompts.js'
import type { QuerySource } from '../../constants/querySource.js'
import { getSystemContext, getUserContext } from '../../context.js'
import type { CanUseToolFn } from '../../hooks/useCanUseTool.js'
import { query } from '../../query.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'
import { getDumpPromptsPath } from '../../services/api/dumpPrompts.js'
import { cleanupAgentTracking } from '../../services/api/promptCacheBreakDetection.js'
} from 'src/constants/prompts.js'
import type { QuerySource } from 'src/constants/querySource.js'
import { getSystemContext, getUserContext } from 'src/context.js'
import type { CanUseToolFn } from 'src/hooks/useCanUseTool.js'
import { query } from 'src/query.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from 'src/services/analytics/growthbook.js'
import { getDumpPromptsPath } from 'src/services/api/dumpPrompts.js'
import { cleanupAgentTracking } from 'src/services/api/promptCacheBreakDetection.js'
import {
connectToServer,
fetchToolsForClient,
} from '../../services/mcp/client.js'
import { getMcpConfigByName } from '../../services/mcp/config.js'
} from 'src/services/mcp/client.js'
import { getMcpConfigByName } from 'src/services/mcp/config.js'
import type {
MCPServerConnection,
ScopedMcpServerConfig,
} from '../../services/mcp/types.js'
import type { Tool, Tools, ToolUseContext } from '../../Tool.js'
import { killShellTasksForAgent } from '../../tasks/LocalShellTask/killShellTasks.js'
import type { Command } from '../../types/command.js'
import type { AgentId } from '../../types/ids.js'
} from 'src/services/mcp/types.js'
import type { Tool, Tools, ToolUseContext } from 'src/Tool.js'
import { killShellTasksForAgent } from 'src/tasks/LocalShellTask/killShellTasks.js'
import type { Command } from 'src/types/command.js'
import type { AgentId } from 'src/types/ids.js'
import type {
AssistantMessage,
Message,
@@ -39,52 +39,52 @@ import type {
TombstoneMessage,
ToolUseSummaryMessage,
UserMessage,
} from '../../types/message.js'
import { createAttachmentMessage } from '../../utils/attachments.js'
import { AbortError } from '../../utils/errors.js'
import { getDisplayPath } from '../../utils/file.js'
} from 'src/types/message.js'
import { createAttachmentMessage } from 'src/utils/attachments.js'
import { AbortError } from 'src/utils/errors.js'
import { getDisplayPath } from 'src/utils/file.js'
import {
cloneFileStateCache,
createFileStateCacheWithSizeLimit,
READ_FILE_STATE_CACHE_SIZE,
} from '../../utils/fileStateCache.js'
} from 'src/utils/fileStateCache.js'
import {
type CacheSafeParams,
createSubagentContext,
} from '../../utils/forkedAgent.js'
import { registerFrontmatterHooks } from '../../utils/hooks/registerFrontmatterHooks.js'
import { clearSessionHooks } from '../../utils/hooks/sessionHooks.js'
import { executeSubagentStartHooks } from '../../utils/hooks.js'
import { createUserMessage } from '../../utils/messages.js'
import { getAgentModel } from '../../utils/model/agent.js'
import { getAPIProvider } from '../../utils/model/providers.js'
} from 'src/utils/forkedAgent.js'
import { registerFrontmatterHooks } from 'src/utils/hooks/registerFrontmatterHooks.js'
import { clearSessionHooks } from 'src/utils/hooks/sessionHooks.js'
import { executeSubagentStartHooks } from 'src/utils/hooks.js'
import { createUserMessage } from 'src/utils/messages.js'
import { getAgentModel } from 'src/utils/model/agent.js'
import { getAPIProvider } from 'src/utils/model/providers.js'
import {
createSubagentTrace,
endTrace,
isLangfuseEnabled,
} from '../../services/langfuse/index.js'
import type { ModelAlias } from '../../utils/model/aliases.js'
} from 'src/services/langfuse/index.js'
import type { ModelAlias } from 'src/utils/model/aliases.js'
import {
clearAgentTranscriptSubdir,
recordSidechainTranscript,
setAgentTranscriptSubdir,
writeAgentMetadata,
} from '../../utils/sessionStorage.js'
} from 'src/utils/sessionStorage.js'
import {
isRestrictedToPluginOnly,
isSourceAdminTrusted,
} from '../../utils/settings/pluginOnlyPolicy.js'
} from 'src/utils/settings/pluginOnlyPolicy.js'
import {
asSystemPrompt,
type SystemPrompt,
} from '../../utils/systemPromptType.js'
} from 'src/utils/systemPromptType.js'
import {
isPerfettoTracingEnabled,
registerAgent as registerPerfettoAgent,
unregisterAgent as unregisterPerfettoAgent,
} from '../../utils/telemetry/perfettoTracing.js'
import type { ContentReplacementState } from '../../utils/toolResultStorage.js'
import { createAgentId } from '../../utils/uuid.js'
} from 'src/utils/telemetry/perfettoTracing.js'
import type { ContentReplacementState } from 'src/utils/toolResultStorage.js'
import { createAgentId } from 'src/utils/uuid.js'
import { resolveAgentTools } from './agentToolUtils.js'
import { type AgentDefinition, isBuiltInAgent } from './loadAgentsDir.js'
@@ -622,7 +622,7 @@ export async function* runAgent({
// Load all skill contents concurrently and add to initial messages
const { formatSkillLoadingMetadata } = await import(
'../../utils/processUserInput/processSlashCommand.js'
'src/utils/processUserInput/processSlashCommand.js'
)
const loaded = await Promise.all(
validSkills.map(async ({ skillName, skill }) => ({
@@ -875,7 +875,7 @@ export async function* runAgent({
/* eslint-disable @typescript-eslint/no-require-imports */
if (feature('MONITOR_TOOL')) {
const mcpMod =
require('../../tasks/MonitorMcpTask/MonitorMcpTask.js') as typeof import('../../tasks/MonitorMcpTask/MonitorMcpTask.js')
require('src/tasks/MonitorMcpTask/MonitorMcpTask.js') as typeof import('src/tasks/MonitorMcpTask/MonitorMcpTask.js')
mcpMod.killMonitorMcpTasksForAgent(
agentId,
toolUseContext.getAppState,

View File

@@ -9,9 +9,9 @@ import { BLACK_CIRCLE } from 'src/constants/figures.js'
import { getModeColor } from 'src/utils/permissions/PermissionMode.js'
import { z } from 'zod/v4'
import { Box, Text } from '@anthropic/ink'
import type { Tool } from '../../Tool.js'
import { buildTool, type ToolDef } from '../../Tool.js'
import { lazySchema } from '../../utils/lazySchema.js'
import type { Tool } from 'src/Tool.js'
import { buildTool, type ToolDef } from 'src/Tool.js'
import { lazySchema } from 'src/utils/lazySchema.js'
import {
ASK_USER_QUESTION_TOOL_CHIP_WIDTH,
ASK_USER_QUESTION_TOOL_NAME,

View File

@@ -10,70 +10,70 @@ import * as React from 'react'
import type { CanUseToolFn } from 'src/hooks/useCanUseTool.js'
import type { AppState } from 'src/state/AppState.js'
import { z } from 'zod/v4'
import { getKairosActive } from '../../bootstrap/state.js'
import { TOOL_SUMMARY_MAX_LENGTH } from '../../constants/toolLimits.js'
import { getKairosActive } from 'src/bootstrap/state.js'
import { TOOL_SUMMARY_MAX_LENGTH } from 'src/constants/toolLimits.js'
import {
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
logEvent,
} from '../../services/analytics/index.js'
import { notifyVscodeFileUpdated } from '../../services/mcp/vscodeSdkMcp.js'
} from 'src/services/analytics/index.js'
import { notifyVscodeFileUpdated } from 'src/services/mcp/vscodeSdkMcp.js'
import type {
SetToolJSXFn,
ToolCallProgress,
ToolUseContext,
ValidationResult,
} from '../../Tool.js'
import { buildTool, type ToolDef } from '../../Tool.js'
} from 'src/Tool.js'
import { buildTool, type ToolDef } from 'src/Tool.js'
import {
backgroundExistingForegroundTask,
markTaskNotified,
registerForeground,
spawnShellTask,
unregisterForeground,
} from '../../tasks/LocalShellTask/LocalShellTask.js'
import type { AgentId } from '../../types/ids.js'
import type { AssistantMessage } from '../../types/message.js'
import { parseForSecurity } from '../../utils/bash/ast.js'
} from 'src/tasks/LocalShellTask/LocalShellTask.js'
import type { AgentId } from 'src/types/ids.js'
import type { AssistantMessage } from 'src/types/message.js'
import { parseForSecurity } from 'src/utils/bash/ast.js'
import {
splitCommand_DEPRECATED,
splitCommandWithOperators,
} from '../../utils/bash/commands.js'
import { extractClaudeCodeHints } from '../../utils/claudeCodeHints.js'
import { detectCodeIndexingFromCommand } from '../../utils/codeIndexing.js'
import { isEnvTruthy } from '../../utils/envUtils.js'
import { isENOENT, ShellError } from '../../utils/errors.js'
} from 'src/utils/bash/commands.js'
import { extractClaudeCodeHints } from 'src/utils/claudeCodeHints.js'
import { detectCodeIndexingFromCommand } from 'src/utils/codeIndexing.js'
import { isEnvTruthy } from 'src/utils/envUtils.js'
import { isENOENT, ShellError } from 'src/utils/errors.js'
import {
detectFileEncoding,
detectLineEndings,
getFileModificationTime,
writeTextContent,
} from '../../utils/file.js'
} from 'src/utils/file.js'
import {
fileHistoryEnabled,
fileHistoryTrackEdit,
} from '../../utils/fileHistory.js'
import { truncate } from '../../utils/format.js'
import { getFsImplementation } from '../../utils/fsOperations.js'
import { lazySchema } from '../../utils/lazySchema.js'
import { expandPath } from '../../utils/path.js'
import type { PermissionResult } from '../../utils/permissions/PermissionResult.js'
import { maybeRecordPluginHint } from '../../utils/plugins/hintRecommendation.js'
import { exec } from '../../utils/Shell.js'
import type { ExecResult } from '../../utils/ShellCommand.js'
import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js'
import { semanticBoolean } from '../../utils/semanticBoolean.js'
import { semanticNumber } from '../../utils/semanticNumber.js'
import { EndTruncatingAccumulator } from '../../utils/stringUtils.js'
import { getTaskOutputPath } from '../../utils/task/diskOutput.js'
import { TaskOutput } from '../../utils/task/TaskOutput.js'
import { isOutputLineTruncated } from '../../utils/terminal.js'
} from 'src/utils/fileHistory.js'
import { truncate } from 'src/utils/format.js'
import { getFsImplementation } from 'src/utils/fsOperations.js'
import { lazySchema } from 'src/utils/lazySchema.js'
import { expandPath } from 'src/utils/path.js'
import type { PermissionResult } from 'src/utils/permissions/PermissionResult.js'
import { maybeRecordPluginHint } from 'src/utils/plugins/hintRecommendation.js'
import { exec } from 'src/utils/Shell.js'
import type { ExecResult } from 'src/utils/ShellCommand.js'
import { SandboxManager } from 'src/utils/sandbox/sandbox-adapter.js'
import { semanticBoolean } from 'src/utils/semanticBoolean.js'
import { semanticNumber } from 'src/utils/semanticNumber.js'
import { EndTruncatingAccumulator } from 'src/utils/stringUtils.js'
import { getTaskOutputPath } from 'src/utils/task/diskOutput.js'
import { TaskOutput } from 'src/utils/task/TaskOutput.js'
import { isOutputLineTruncated } from 'src/utils/terminal.js'
import {
buildLargeToolResultMessage,
ensureToolResultsDir,
generatePreview,
getToolResultPath,
PREVIEW_SIZE_BYTES,
} from '../../utils/toolResultStorage.js'
} from 'src/utils/toolResultStorage.js'
import { userFacingName as fileEditUserFacingName } from '../FileEditTool/UI.js'
import { trackGitOperations } from '../shared/gitOperationTracking.js'
import {
@@ -506,9 +506,9 @@ type OutputSchema = ReturnType<typeof outputSchema>
export type Out = z.infer<OutputSchema>
// Re-export BashProgress from centralized types to break import cycles
export type { BashProgress } from '../../types/tools.js'
export type { BashProgress } from 'src/types/tools.js'
import type { BashProgress } from '../../types/tools.js'
import type { BashProgress } from 'src/types/tools.js'
/**
* Checks if a command is allowed to be automatically backgrounded

View File

@@ -1,9 +1,9 @@
import React from 'react'
import { removeSandboxViolationTags } from 'src/utils/sandbox/sandbox-ui-utils.js'
import { KeyboardShortcutHint } from '@anthropic/ink'
import { MessageResponse } from '../../components/MessageResponse.js'
import { OutputLine } from '../../components/shell/OutputLine.js'
import { ShellTimeDisplay } from '../../components/shell/ShellTimeDisplay.js'
import { MessageResponse } from 'src/components/MessageResponse.js'
import { OutputLine } from 'src/components/shell/OutputLine.js'
import { ShellTimeDisplay } from 'src/components/shell/ShellTimeDisplay.js'
import { Box, Text } from '@anthropic/ink'
import type { Out as BashOut } from './BashTool.js'

View File

@@ -1,21 +1,21 @@
import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
import * as React from 'react'
import { KeyboardShortcutHint } from '@anthropic/ink'
import { FallbackToolUseErrorMessage } from '../../components/FallbackToolUseErrorMessage.js'
import { MessageResponse } from '../../components/MessageResponse.js'
import { ShellProgressMessage } from '../../components/shell/ShellProgressMessage.js'
import { FallbackToolUseErrorMessage } from 'src/components/FallbackToolUseErrorMessage.js'
import { MessageResponse } from 'src/components/MessageResponse.js'
import { ShellProgressMessage } from 'src/components/shell/ShellProgressMessage.js'
import { Box, Text } from '@anthropic/ink'
import { useKeybinding } from '../../keybindings/useKeybinding.js'
import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js'
import { useAppStateStore, useSetAppState } from '../../state/AppState.js'
import type { Tool } from '../../Tool.js'
import { backgroundAll } from '../../tasks/LocalShellTask/LocalShellTask.js'
import type { ProgressMessage } from '../../types/message.js'
import { env } from '../../utils/env.js'
import { isEnvTruthy } from '../../utils/envUtils.js'
import { getDisplayPath } from '../../utils/file.js'
import { isFullscreenEnvEnabled } from '../../utils/fullscreen.js'
import type { ThemeName } from '../../utils/theme.js'
import { useKeybinding } from 'src/keybindings/useKeybinding.js'
import { useShortcutDisplay } from 'src/keybindings/useShortcutDisplay.js'
import { useAppStateStore, useSetAppState } from 'src/state/AppState.js'
import type { Tool } from 'src/Tool.js'
import { backgroundAll } from 'src/tasks/LocalShellTask/LocalShellTask.js'
import type { ProgressMessage } from 'src/types/message.js'
import { env } from 'src/utils/env.js'
import { isEnvTruthy } from 'src/utils/envUtils.js'
import { getDisplayPath } from 'src/utils/file.js'
import { isFullscreenEnvEnabled } from 'src/utils/fullscreen.js'
import type { ThemeName } from 'src/utils/theme.js'
import type { BashProgress, BashToolInput, Out } from './BashTool.js'
import BashToolResultMessage from './BashToolResultMessage.js'
import { extractBashCommentLabel } from './commentLabel.js'

View File

@@ -2,16 +2,16 @@ import type { z } from 'zod/v4'
import {
isUnsafeCompoundCommand_DEPRECATED,
splitCommand_DEPRECATED,
} from '../../utils/bash/commands.js'
} from 'src/utils/bash/commands.js'
import {
buildParsedCommandFromRoot,
type IParsedCommand,
ParsedCommand,
} from '../../utils/bash/ParsedCommand.js'
import { type Node, PARSE_ABORTED } from '../../utils/bash/parser.js'
import type { PermissionResult } from '../../utils/permissions/PermissionResult.js'
import type { PermissionUpdate } from '../../utils/permissions/PermissionUpdateSchema.js'
import { createPermissionRequestMessage } from '../../utils/permissions/permissions.js'
} from 'src/utils/bash/ParsedCommand.js'
import { type Node, PARSE_ABORTED } from 'src/utils/bash/parser.js'
import type { PermissionResult } from 'src/utils/permissions/PermissionResult.js'
import type { PermissionUpdate } from 'src/utils/permissions/PermissionUpdateSchema.js'
import { createPermissionRequestMessage } from 'src/utils/permissions/permissions.js'
import { BashTool } from './BashTool.js'
import { bashCommandIsSafeAsync_DEPRECATED } from './bashSecurity.js'

View File

@@ -1,14 +1,14 @@
import { feature } from 'bun:bundle'
import { APIUserAbortError } from '@anthropic-ai/sdk'
import type { z } from 'zod/v4'
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from 'src/services/analytics/growthbook.js'
import {
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
logEvent,
} from '../../services/analytics/index.js'
import type { ToolPermissionContext, ToolUseContext } from '../../Tool.js'
import type { PendingClassifierCheck } from '../../types/permissions.js'
import { count } from '../../utils/array.js'
} from 'src/services/analytics/index.js'
import type { ToolPermissionContext, ToolUseContext } from 'src/Tool.js'
import type { PendingClassifierCheck } from 'src/types/permissions.js'
import { count } from 'src/utils/array.js'
import {
checkSemantics,
nodeTypeId,
@@ -16,45 +16,45 @@ import {
parseForSecurityFromAst,
type Redirect,
type SimpleCommand,
} from '../../utils/bash/ast.js'
} from 'src/utils/bash/ast.js'
import {
type CommandPrefixResult,
extractOutputRedirections,
getCommandSubcommandPrefix,
splitCommand_DEPRECATED,
} from '../../utils/bash/commands.js'
import { parseCommandRaw } from '../../utils/bash/parser.js'
import { tryParseShellCommand } from '../../utils/bash/shellQuote.js'
import { getCwd } from '../../utils/cwd.js'
import { logForDebugging } from '../../utils/debug.js'
import { isEnvTruthy } from '../../utils/envUtils.js'
import { AbortError } from '../../utils/errors.js'
} from 'src/utils/bash/commands.js'
import { parseCommandRaw } from 'src/utils/bash/parser.js'
import { tryParseShellCommand } from 'src/utils/bash/shellQuote.js'
import { getCwd } from 'src/utils/cwd.js'
import { logForDebugging } from 'src/utils/debug.js'
import { isEnvTruthy } from 'src/utils/envUtils.js'
import { AbortError } from 'src/utils/errors.js'
import type {
ClassifierBehavior,
ClassifierResult,
} from '../../utils/permissions/bashClassifier.js'
} from 'src/utils/permissions/bashClassifier.js'
import {
classifyBashCommand,
getBashPromptAllowDescriptions,
getBashPromptAskDescriptions,
getBashPromptDenyDescriptions,
isClassifierPermissionsEnabled,
} from '../../utils/permissions/bashClassifier.js'
} from 'src/utils/permissions/bashClassifier.js'
import type {
PermissionDecisionReason,
PermissionResult,
} from '../../utils/permissions/PermissionResult.js'
} from 'src/utils/permissions/PermissionResult.js'
import type {
PermissionRule,
PermissionRuleValue,
} from '../../utils/permissions/PermissionRule.js'
import { extractRules } from '../../utils/permissions/PermissionUpdate.js'
import type { PermissionUpdate } from '../../utils/permissions/PermissionUpdateSchema.js'
import { permissionRuleValueToString } from '../../utils/permissions/permissionRuleParser.js'
} from 'src/utils/permissions/PermissionRule.js'
import { extractRules } from 'src/utils/permissions/PermissionUpdate.js'
import type { PermissionUpdate } from 'src/utils/permissions/PermissionUpdateSchema.js'
import { permissionRuleValueToString } from 'src/utils/permissions/permissionRuleParser.js'
import {
createPermissionRequestMessage,
getRuleByContentsForTool,
} from '../../utils/permissions/permissions.js'
} from 'src/utils/permissions/permissions.js'
import {
parsePermissionRule,
type ShellPermissionRule,
@@ -62,11 +62,11 @@ import {
permissionRuleExtractPrefix as sharedPermissionRuleExtractPrefix,
suggestionForExactCommand as sharedSuggestionForExactCommand,
suggestionForPrefix as sharedSuggestionForPrefix,
} from '../../utils/permissions/shellRuleMatching.js'
import { getPlatform } from '../../utils/platform.js'
import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js'
import { jsonStringify } from '../../utils/slowOperations.js'
import { windowsPathToPosixPath } from '../../utils/windowsPaths.js'
} from 'src/utils/permissions/shellRuleMatching.js'
import { getPlatform } from 'src/utils/platform.js'
import { SandboxManager } from 'src/utils/sandbox/sandbox-adapter.js'
import { jsonStringify } from 'src/utils/slowOperations.js'
import { windowsPathToPosixPath } from 'src/utils/windowsPaths.js'
import { BashTool } from './BashTool.js'
import { checkCommandOperatorPermissions } from './bashCommandHelpers.js'
import {

Some files were not shown because too many files have changed in this diff Show More