From 70c59eb71d4e8245f12708420b3c439d4dc33a1a Mon Sep 17 00:00:00 2001 From: root Date: Fri, 27 Feb 2026 20:19:00 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=B0=86=20.claude/=20=E5=92=8C=20CLA?= =?UTF-8?q?UDE.local.md=20=E4=BB=8E=20git=20=E8=B7=9F=E8=B8=AA=E4=B8=AD?= =?UTF-8?q?=E6=8E=92=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/commands/throttled-task.md | 30 -- .claude/plans/compiled-discovering-sunset.md | 407 ------------------- .claude/remove-root-check.sh | 172 -------- .claude/settings.local.json | 24 -- .claude/show-status.mjs | 91 ----- .gitignore | 4 + CLAUDE.local.md | 261 ------------ 7 files changed, 4 insertions(+), 985 deletions(-) delete mode 100644 .claude/commands/throttled-task.md delete mode 100644 .claude/plans/compiled-discovering-sunset.md delete mode 100644 .claude/remove-root-check.sh delete mode 100644 .claude/settings.local.json delete mode 100644 .claude/show-status.mjs delete mode 100644 CLAUDE.local.md diff --git a/.claude/commands/throttled-task.md b/.claude/commands/throttled-task.md deleted file mode 100644 index 72ba1a5..0000000 --- a/.claude/commands/throttled-task.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -description: 执行带有请求节流的任务,可以控制工具调用的频率 ---- - -# 节流任务执行 - -你现在进入了节流模式。在这个模式下,你需要: - -## 节流规则 - -1. **工具调用间隔**:每次调用工具后,等待至少 2 秒再调用下一个工具 -2. **并行限制**:同时最多只能并行调用 2 个工具(原本可能更多) -3. **批处理优化**:优先将相关的操作合并到一个工具调用中 -4. **进度提示**:每次等待时向用户说明正在节流等待 - -## 执行方式 - -- 对于读取操作(Read, Glob, Grep),可以适当放宽并行限制 -- 对于写入操作(Write, Edit),严格遵守间隔要求 -- 对于 API 调用(Bash 中的 API 请求),必须串行执行 - -## 用户任务 - -请按照上述节流规则执行以下任务: - -{{PROMPT}} - ---- - -**注意**:完成任务后,你将自动退出节流模式,恢复正常的工具调用频率。 diff --git a/.claude/plans/compiled-discovering-sunset.md b/.claude/plans/compiled-discovering-sunset.md deleted file mode 100644 index 6769b63..0000000 --- a/.claude/plans/compiled-discovering-sunset.md +++ /dev/null @@ -1,407 +0,0 @@ -# 心跳配置功能重构计划 - -## 概述 - -将心跳配置从"选择curl命令"模式重构为"基于URL的可配置心跳"模式。 - -**核心变化**: -- ✅ 支持多个URL同时发送心跳 -- ✅ 每个URL独立配置间隔时间 -- ✅ curl命令作为导入模板,可解析为URL配置 -- ✅ 完全替换旧的基于curl选择的方式 - -## 架构设计 - -### 数据模型 - -**新表:heartbeat_url_configs** -```typescript -{ - id: number; - name: string; // URL配置名称 - url: string; // 目标URL - method: string; // HTTP方法(GET/POST/PUT/DELETE) - headers: Record; // 请求头(JSONB) - body: string | null; // 请求体 - intervalSeconds: number; // 独立的心跳间隔(10-3600秒) - isEnabled: boolean; // 是否启用此配置 - lastSuccessAt: Date | null; // 统计:上次成功时间 - lastErrorAt: Date | null; // 统计:上次失败时间 - lastErrorMessage: string | null; // 统计:上次错误信息 - successCount: number; // 统计:成功次数 - failureCount: number; // 统计:失败次数 - providerId: number | null; // 关联的供应商ID(可选) - model: string | null; // 模型名称(展示用) - endpoint: string | null; // 端点路径(展示用) - createdAt: Date; - updatedAt: Date; -} -``` - -**修改表:heartbeat_settings** -```typescript -{ - id: number; - enabled: boolean; // 全局开关(保留) - // 删除:intervalSeconds, savedCurls, selectedCurlIndex - createdAt: Date; - updatedAt: Date; -} -``` - -### 心跳执行逻辑 - -**ProviderHeartbeat类重构**: -```typescript -class ProviderHeartbeat { - // 多定时器管理:Map - private static timers: Map = new Map(); - - // 启动:为每个启用的URL配置创建独立定时器 - static async start() { - const configs = await findEnabledHeartbeatUrlConfigs(); - for (const config of configs) { - this.startConfigTimer(config); - } - } - - // 停止:清除所有定时器 - static stop() { - for (const timer of this.timers.values()) { - clearInterval(timer); - } - this.timers.clear(); - } - - // 单个配置的定时器 - private static startConfigTimer(config: HeartbeatUrlConfig) { - const interval = setInterval(() => { - this.sendHeartbeat(config); - }, config.intervalSeconds * 1000); - this.timers.set(config.id, interval); - } - - // 发送心跳并记录成功/失败 - private static async sendHeartbeat(config: HeartbeatUrlConfig) { - const response = await fetch(config.url, { - method: config.method, - headers: config.headers, - body: config.body, - signal: AbortSignal.timeout(10000), - }); - - if (response.ok) { - await recordHeartbeatSuccess(config.id); - } else { - await recordHeartbeatFailure(config.id, errorMessage); - } - } -} -``` - -### 前端UI设计 - -**页面布局**: -``` -/settings/heartbeat/page.tsx -├── GlobalSettingsCard(全局开关) -├── CurlHistorySection(curl历史记录 + 导入按钮) -└── UrlConfigsSection(URL配置列表 + 新建/编辑/删除) -``` - -**组件拆分**: -- `global-settings-card.tsx` - 全局开关卡片 -- `curl-history-section.tsx` - curl历史记录区域 -- `curl-history-card.tsx` - 单个curl历史卡片 -- `url-configs-section.tsx` - URL配置列表区域 -- `url-config-card.tsx` - 单个URL配置卡片 -- `url-config-dialog.tsx` - 新建/编辑对话框 -- `_lib/hooks.ts` - 自定义hooks(useHeartbeatPageData) - -**curl导入流程**: -1. 用户点击curl历史卡片上的"导入"按钮 -2. 使用`parseCurlCommand()`解析curl命令 -3. 自动打开新建对话框,表单预填充解析后的数据 -4. 用户修改后保存,创建URL配置 - -## 实施步骤 - -### 阶段1:数据库和Repository层 - -1. **修改schema.ts** - - 添加`heartbeatUrlConfigs`表定义 - - 修改`heartbeatSettings`表定义(删除3个字段) - -2. **生成和审查迁移** - ```bash - bun run db:generate - # 检查生成的 drizzle/0061_*.sql - # 确保数据迁移逻辑正确(将选中的curl转为第一个URL配置) - ``` - -3. **创建repository/heartbeat-url-configs.ts** - - 接口:`HeartbeatUrlConfig`、`CreateHeartbeatUrlConfigInput`、`UpdateHeartbeatUrlConfigInput` - - 函数: - - `findAllHeartbeatUrlConfigs()` - 获取所有配置 - - `findEnabledHeartbeatUrlConfigs()` - 获取启用的配置 - - `findHeartbeatUrlConfigById(id)` - 根据ID获取 - - `createHeartbeatUrlConfig(input)` - 创建配置 - - `updateHeartbeatUrlConfig(id, input)` - 更新配置 - - `deleteHeartbeatUrlConfig(id)` - 删除配置 - - `recordHeartbeatSuccess(id)` - 记录成功 - - `recordHeartbeatFailure(id, errorMessage)` - 记录失败 - -4. **修改repository/heartbeat-settings.ts** - - 简化为只管理全局开关 - - 删除`savedCurls`和`selectedCurlIndex`相关逻辑 - - 保持`getHeartbeatSettings()`和`updateHeartbeatSettings()`接口 - -5. **运行迁移** - ```bash - bun run db:migrate - ``` - -### 阶段2:Action层 - -6. **创建actions/heartbeat-url-configs.ts** - - `fetchHeartbeatUrlConfigs()` - 获取所有配置 - - `createHeartbeatUrlConfigAction(input)` - 创建配置 - - `updateHeartbeatUrlConfigAction(id, input)` - 更新配置 - - `deleteHeartbeatUrlConfigAction(id)` - 删除配置 - - 验证规则: - - 名称不能为空 - - URL不能为空 - - 间隔时间范围:10-3600秒 - - 权限检查:仅admin可操作 - - 副作用:修改配置后重启心跳任务 - -7. **修改actions/heartbeat-settings.ts** - - 简化为只管理全局开关 - - 保持`fetchHeartbeatSettings()`和`saveHeartbeatSettings()` - - 开关变化时重启心跳任务 - -### 阶段3:心跳执行逻辑 - -8. **重构lib/provider-heartbeat.ts** - - 添加`timers: Map` - - 修改`start()`:为每个启用的配置创建定时器 - - 修改`stop()`:清除所有定时器 - - 新增`startConfigTimer(config)`:创建单个配置的定时器 - - 新增`stopConfigTimer(configId)`:停止单个配置的定时器 - - 修改`sendHeartbeat(config)`:发送请求并记录结果 - - 删除curl解析逻辑(不再需要) - -9. **修改app/v1/_lib/proxy/forwarder.ts** - - 删除或注释掉`addSuccessfulCurl()`调用(第357-367行) - - curl历史功能迁移到独立模块(可选) - -### 阶段4:i18n文案 - -10. **更新翻译文件** - - `messages/zh-CN/settings/heartbeat.json` - - `messages/zh-TW/settings/heartbeat.json` - - `messages/en/settings/heartbeat.json` - - `messages/ja/settings/heartbeat.json` - - `messages/ru/settings/heartbeat.json` - - 新增key: - - `section.global.*` - 全局设置区域 - - `section.curlHistory.*` - curl历史区域 - - `section.urlConfigs.*` - URL配置区域 - - `form.name.*` - 配置名称字段 - - `form.url.*` - URL字段 - - `form.method.*` - HTTP方法字段 - - `form.headers.*` - 请求头字段 - - `form.body.*` - 请求体字段 - - `form.isEnabled.*` - 启用开关 - - `form.stats.*` - 统计信息 - - `form.createButton`、`importButton`等 - -### 阶段5:前端UI - -11. **创建组件** - - `app/[locale]/settings/heartbeat/_components/global-settings-card.tsx` - - Switch组件:全局开关 - - 说明文字 - - - `app/[locale]/settings/heartbeat/_components/curl-history-section.tsx` - - 区域标题和描述 - - curl历史卡片列表 - - 空状态提示 - - - `app/[locale]/settings/heartbeat/_components/curl-history-card.tsx` - - 显示:供应商名、端点、模型、时间 - - 导入按钮 - - - `app/[locale]/settings/heartbeat/_components/url-configs-section.tsx` - - 区域标题和描述 - - 新建按钮 - - URL配置卡片列表 - - 空状态提示 - - - `app/[locale]/settings/heartbeat/_components/url-config-card.tsx` - - 显示:名称、URL、方法、间隔、启用状态 - - 统计信息:成功次数、失败次数、最后成功/失败时间 - - 编辑按钮、删除按钮 - - Switch组件:快速启用/禁用 - - - `app/[locale]/settings/heartbeat/_components/url-config-dialog.tsx` - - Dialog表单:名称、URL、方法、headers、body、间隔 - - 支持新建和编辑模式 - - headers使用Textarea(JSON格式) - - body使用Textarea(可选) - - 验证和错误提示 - - - `app/[locale]/settings/heartbeat/_components/heartbeat-skeleton.tsx` - - 骨架屏加载状态 - -12. **创建hooks** - - `app/[locale]/settings/heartbeat/_lib/hooks.ts` - - `useHeartbeatPageData()`: - - 加载settings、configs、savedCurls - - 提供CRUD操作函数 - - 提供importFromCurl函数 - - 统一错误处理和toast提示 - -13. **重写page.tsx** - - 使用`useHeartbeatPageData()` - - 组合所有子组件 - - 加载状态和错误处理 - -### 阶段6:测试和验证 - -14. **类型检查和格式化** - ```bash - bun run typecheck - bun run lint:fix - ``` - -15. **手动测试流程** - - [ ] 访问 `/settings/heartbeat` 页面 - - [ ] 创建新的URL配置 - - [ ] 从curl历史导入配置 - - [ ] 编辑配置(修改URL、间隔等) - - [ ] 启用/禁用单个配置 - - [ ] 启用/禁用全局开关 - - [ ] 删除配置 - - [ ] 检查多个URL同时发送心跳 - - [ ] 检查失败记录和统计信息 - - [ ] 检查国际化(切换语言) - -16. **日志验证** - ```bash - # 检查心跳日志 - tail -f logs/app.log | grep "ProviderHeartbeat" - - # 应该看到: - # - "Timer started" - 定时器启动 - # - "Heartbeat sent successfully" - 成功日志 - # - "Heartbeat failed" - 失败日志 - ``` - -17. **数据库验证** - ```bash - bun run db:studio - # 检查 heartbeat_url_configs 表 - # 确认配置已保存 - # 确认成功/失败统计更新 - ``` - -## 关键文件清单 - -### 新建文件 -- `src/repository/heartbeat-url-configs.ts` - URL配置Repository -- `src/actions/heartbeat-url-configs.ts` - URL配置Actions -- `src/app/[locale]/settings/heartbeat/_components/global-settings-card.tsx` -- `src/app/[locale]/settings/heartbeat/_components/curl-history-section.tsx` -- `src/app/[locale]/settings/heartbeat/_components/curl-history-card.tsx` -- `src/app/[locale]/settings/heartbeat/_components/url-configs-section.tsx` -- `src/app/[locale]/settings/heartbeat/_components/url-config-card.tsx` -- `src/app/[locale]/settings/heartbeat/_components/url-config-dialog.tsx` -- `src/app/[locale]/settings/heartbeat/_lib/hooks.ts` -- `drizzle/0061_*.sql` - 数据库迁移文件(自动生成) - -### 修改文件 -- `src/drizzle/schema.ts` - 添加新表,修改旧表 -- `src/repository/heartbeat-settings.ts` - 简化逻辑 -- `src/actions/heartbeat-settings.ts` - 简化Action -- `src/lib/provider-heartbeat.ts` - 重构心跳执行逻辑 -- `src/app/v1/_lib/proxy/forwarder.ts` - 删除curl保存逻辑 -- `src/app/[locale]/settings/heartbeat/page.tsx` - 重写UI -- `messages/*/settings/heartbeat.json` - 更新翻译(5种语言) - -### 删除文件 -- `src/app/[locale]/settings/heartbeat/_components/heartbeat-form.tsx` - 旧表单组件 - -## 数据迁移策略 - -**迁移逻辑(在0061_*.sql中)**: -```sql --- 创建新表 -CREATE TABLE heartbeat_url_configs (...); - --- 迁移现有数据 -DO $$ -DECLARE - settings_row RECORD; - selected_curl JSONB; -BEGIN - SELECT * INTO settings_row FROM heartbeat_settings LIMIT 1; - - IF settings_row.selected_curl_index IS NOT NULL THEN - selected_curl := settings_row.saved_curls->settings_row.selected_curl_index; - - INSERT INTO heartbeat_url_configs ( - name, url, interval_seconds, is_enabled, ... - ) VALUES ( - selected_curl->>'providerName', - selected_curl->>'url', - settings_row.interval_seconds, - settings_row.enabled, - ... - ); - END IF; -END $$; - --- 删除旧字段 -ALTER TABLE heartbeat_settings - DROP COLUMN interval_seconds, - DROP COLUMN saved_curls, - DROP COLUMN selected_curl_index; -``` - -**回滚能力**:保留旧数据在迁移文件中,可以通过反向迁移恢复。 - -## 风险和缓解 - -| 风险 | 缓解措施 | -|------|----------| -| 数据迁移失败 | 1. 迁移前备份数据库
2. 在测试环境验证
3. 编写回滚脚本 | -| curl解析不完整 | 1. 复用现有`parseCurlCommand`
2. 添加解析错误提示
3. 允许手动编辑 | -| 多定时器性能问题 | 1. 限制最大配置数量(如20个)
2. 添加禁用功能
3. 监控日志 | -| 心跳发送失败 | 1. 记录失败日志
2. UI显示失败状态
3. 支持手动禁用 | - -## 验证清单 - -- [ ] 数据库迁移成功,旧数据已转移 -- [ ] 类型检查通过 (`bun run typecheck`) -- [ ] Lint检查通过 (`bun run lint`) -- [ ] 构建成功 (`bun run build`) -- [ ] 可以创建URL配置 -- [ ] 可以从curl导入配置 -- [ ] 可以编辑和删除配置 -- [ ] 全局开关控制所有心跳 -- [ ] 多个URL同时发送心跳(检查日志) -- [ ] 失败统计正确记录 -- [ ] 所有5种语言显示正常 -- [ ] 页面加载和交互流畅 - -## 预估工作量 - -- 数据库和Repository层:1-2小时 -- Action层:30分钟 -- 心跳执行逻辑:1小时 -- i18n文案:30分钟 -- 前端UI:2-3小时 -- 测试和验证:1小时 -- **总计:6-8小时** diff --git a/.claude/remove-root-check.sh b/.claude/remove-root-check.sh deleted file mode 100644 index 6df65a0..0000000 --- a/.claude/remove-root-check.sh +++ /dev/null @@ -1,172 +0,0 @@ -#!/bin/bash - -echo "==========================================" -echo "Claude Code Root Check 移除工具" -echo "==========================================" -echo "" - -# 通过 which 命令找到 claude 可执行文件 -echo "正在查找 claude 命令..." -CLAUDE_PATH=$(which claude) - -if [ -z "$CLAUDE_PATH" ]; then - echo "❌ 错误: 未找到 claude 命令" - exit 1 -fi - -echo "找到 claude 位置: $CLAUDE_PATH" - -# 如果是软链接,获取实际文件路径 -if [ -L "$CLAUDE_PATH" ]; then - REAL_PATH=$(readlink -f "$CLAUDE_PATH") - echo "这是一个软链接,实际路径: $REAL_PATH" -else - REAL_PATH="$CLAUDE_PATH" -fi - -# 获取 claude 所在的目录 -CLAUDE_DIR=$(dirname "$CLAUDE_PATH") -echo "claude 目录: $CLAUDE_DIR" -echo "" - -# 检查是否已经是包装脚本 -if grep -q "Claude Code Wrapper" "$CLAUDE_PATH" 2>/dev/null; then - echo "✓ 检测到已安装包装脚本" - echo "正在更新包装脚本..." -else - echo "正在创建包装脚本..." -fi - -# 创建 claude-wrapper.sh -WRAPPER_PATH="$CLAUDE_DIR/claude-wrapper.sh" - -cat > "$WRAPPER_PATH" << 'EOF' -#!/bin/bash - -# Claude Code Wrapper - 自动删除 root check 限制 -# 此脚本会在每次执行 claude 前绕过 root 用户限制 -# -# 新版本 (2.1.x+) 支持通过环境变量绕过检查: -# - IS_SANDBOX=1 -# - CLAUDE_CODE_BUBBLEWRAP=1 -# -# 旧版本需要修改 cli.js 文件删除检查代码 - -# 获取当前脚本的真实路径 -SCRIPT_PATH="$(readlink -f "$0")" -SCRIPT_DIR="$(dirname "$SCRIPT_PATH")" - -# 查找同目录下的 claude.bak(原始软链接) -CLAUDE_BAK="$SCRIPT_DIR/claude.bak" - -# 如果 claude.bak 不存在,尝试通过 which 和目录搜索找到真实路径 -if [ ! -L "$CLAUDE_BAK" ] && [ ! -f "$CLAUDE_BAK" ]; then - # 在当前目录查找指向 claude-code 的软链接或文件 - for file in "$SCRIPT_DIR"/*; do - if [ -L "$file" ] || [ -f "$file" ]; then - target=$(readlink -f "$file" 2>/dev/null) - if [[ "$target" == *"@anthropic-ai/claude-code/cli.js" ]]; then - CLAUDE_REAL_PATH="$target" - break - fi - fi - done - - # 如果还是没找到,尝试常见路径 - if [ -z "$CLAUDE_REAL_PATH" ]; then - for path in \ - "$SCRIPT_DIR/../lib/node_modules/@anthropic-ai/claude-code/cli.js" \ - "/usr/local/lib/node_modules/@anthropic-ai/claude-code/cli.js" \ - "/usr/lib/node_modules/@anthropic-ai/claude-code/cli.js"; do - if [ -f "$path" ]; then - CLAUDE_REAL_PATH="$path" - break - fi - done - fi -else - # 通过 claude.bak 获取真实的 cli.js 路径 - CLAUDE_REAL_PATH="$(readlink -f "$CLAUDE_BAK")" -fi - -if [ -z "$CLAUDE_REAL_PATH" ] || [ ! -f "$CLAUDE_REAL_PATH" ]; then - echo "错误: 未找到真实的 claude cli.js 文件" >&2 - echo "请确保 claude 已正确安装" >&2 - exit 1 -fi - -# 获取 claude 版本号(用于提示信息) -CLAUDE_VERSION=$(node "$CLAUDE_REAL_PATH" --version 2>/dev/null | head -1 || echo "unknown") - -# 新版本 (2.1.x+) 直接使用环境变量绕过 root check -# 设置 IS_SANDBOX=1 或 CLAUDE_CODE_BUBBLEWRAP=1 即可 -export IS_SANDBOX=1 -export CLAUDE_CODE_BUBBLEWRAP=1 - -# 执行原始 claude 命令,传递所有参数 -exec node "$CLAUDE_REAL_PATH" "$@" -EOF - -# 给包装脚本添加执行权限 -chmod +x "$WRAPPER_PATH" -echo "✓ 已创建包装脚本: $WRAPPER_PATH" -echo "" - -# 备份原 claude 命令(如果尚未备份) -CLAUDE_BAK="$CLAUDE_DIR/claude.bak" -if [ ! -e "$CLAUDE_BAK" ]; then - if [ -L "$CLAUDE_PATH" ]; then - # 如果是软链接,复制软链接本身 - cp -P "$CLAUDE_PATH" "$CLAUDE_BAK" - echo "✓ 已备份原 claude 软链接为: $CLAUDE_BAK" - else - # 如果是普通文件,复制文件 - cp "$CLAUDE_PATH" "$CLAUDE_BAK" - echo "✓ 已备份原 claude 文件为: $CLAUDE_BAK" - fi -else - echo "✓ 检测到已存在备份: $CLAUDE_BAK" -fi - -# 替换 claude 命令为包装脚本 -echo "" -echo "正在替换 claude 命令..." - -# 删除原有的 claude(如果是软链接或文件) -rm -f "$CLAUDE_PATH" - -# 创建新的软链接指向包装脚本 -ln -s "$WRAPPER_PATH" "$CLAUDE_PATH" - -echo "✓ 已将 claude 命令替换为包装脚本" -echo "" - -# 验证安装 -echo "==========================================" -echo "验证安装..." -echo "" - -if [ -L "$CLAUDE_PATH" ]; then - TARGET_PATH=$(readlink "$CLAUDE_PATH") - echo "✓ claude 现在指向: $TARGET_PATH" -fi - -if [ -e "$CLAUDE_BAK" ]; then - echo "✓ 原始 claude 已备份为: $CLAUDE_BAK" -fi - -if [ -x "$WRAPPER_PATH" ]; then - echo "✓ 包装脚本具有执行权限" -fi - -echo "" -echo "==========================================" -echo "✓ 安装完成!" -echo "" -echo "现在你可以在 root 用户下使用:" -echo " claude --dangerously-skip-permissions" -echo "" -echo "如需恢复原始 claude 命令:" -echo " rm $CLAUDE_PATH" -echo " mv $CLAUDE_BAK $CLAUDE_PATH" -echo "==========================================" diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 24e6832..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "env": { - "ANTHROPIC_API_KEY": "sk-d87ce5b80978df466c81378d798ca39f", - "ANTHROPIC_BASE_URL": "https://cc.ronghuaxueleng.top", - "CLAUDE_CODE_ATTRIBUTION_HEADER": "0", - "DISABLE_AUTOUPDATER": 1, - "DISABLE_BUG_COMMAND": 1, - "DISABLE_ERROR_REPORTING": 1, - "DISABLE_TELEMETRY": 1, - "IS_SANDBOX": "1", - "USER_NAME": "腾讯云" - }, - "permissions": { - "allow": [ - "*" - ], - "defaultMode": "bypassPermissions" - }, - "statusLine": { - "command": "node \".claude/show-status.mjs\"", - "padding": 0, - "type": "command" - } -} \ No newline at end of file diff --git a/.claude/show-status.mjs b/.claude/show-status.mjs deleted file mode 100644 index 772ab85..0000000 --- a/.claude/show-status.mjs +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env node -/** - * Claude Code 积分状态栏脚本 - * 用途: 在状态栏显示配置信息 - */ - -import fs from 'fs'; -import path from 'path'; -import os from 'os'; - -// 禁用SSL证书验证警告 -process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - -function getDisplayUrl() { - const baseUrl = process.env.ANTHROPIC_BASE_URL || ''; - if (baseUrl) { - const match = baseUrl.match(/https?:\/\/([^\/]+)/); - if (match) { - return match[1]; - } - } - return ''; -} - -function getCurrentModel() { - // 优先使用环境变量 - let model = process.env.ANTHROPIC_MODEL || ''; - - // 如果环境变量没有,检查settings.json - if (!model) { - try { - const settingsFile = path.join(os.homedir(), '.claude', 'settings.json'); - if (fs.existsSync(settingsFile)) { - const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8')); - model = settings.model || ''; - } - } catch (error) { - // 忽略错误 - } - } - - if (model) { - if (model.toLowerCase().includes('claude-3')) { - if (model.toLowerCase().includes('haiku')) { - return 'Claude 3 Haiku'; - } else if (model.toLowerCase().includes('sonnet')) { - return 'Claude 3 Sonnet'; - } else if (model.toLowerCase().includes('opus')) { - return 'Claude 3 Opus'; - } - } else if (model.toLowerCase().includes('claude-4') || model.toLowerCase().includes('sonnet-4')) { - return 'Claude 4 Sonnet'; - } else if (model.toLowerCase().includes('opus-4')) { - return 'Claude 4 Opus'; - } else if (model.length > 20) { - return model.substring(0, 20) + '...'; - } - return model; - } - - return 'Claude (Auto)'; -} - -async function main() { - try { - const currentUrl = getDisplayUrl(); - const currentModel = getCurrentModel(); - const userName = process.env.USER_NAME || ''; - - const parts = []; - if (userName) parts.push(`👤 ${userName}`); - parts.push(currentModel); - parts.push(currentUrl); - - console.log(parts.join(' | ')); - - } catch (error) { - // 即使出错也显示基本信息 - const currentUrl = getDisplayUrl(); - const currentModel = getCurrentModel(); - const userName = process.env.USER_NAME || ''; - const parts = ['🔴 错误']; - if (userName) parts.push(`👤 ${userName}`); - parts.push(currentModel); - parts.push(currentUrl); - console.log(parts.join(' | ')); - } -} - -// ES Module 中直接执行 -main(); diff --git a/.gitignore b/.gitignore index c1146eb..2221ddd 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,7 @@ ds2api-tests # Misc .git/ Thumbs.db + +# Claude Code +.claude/ +CLAUDE.local.md diff --git a/CLAUDE.local.md b/CLAUDE.local.md deleted file mode 100644 index 4fd51c0..0000000 --- a/CLAUDE.local.md +++ /dev/null @@ -1,261 +0,0 @@ -# Claude Code 行为准则 - -> 本文件定义 Claude Code 在本项目中的强制执行规则。所有规则均为**必须执行**,不可跳过。 - ---- - -## 一、核心原则(九荣九耻) - -| 耻 | 荣 | -|---|---| -| ❌ 瞎猜接口 | ✅ 认真查询源码 | -| ❌ 模糊执行 | ✅ 寻求用户确认 | -| ❌ 臆想业务 | ✅ 复用现有实现 | -| ❌ 创造接口 | ✅ 主动测试验证 | -| ❌ 跳过验证 | ✅ 等待人类确认 | -| ❌ 破坏架构 | ✅ 遵循项目规范 | -| ❌ 假装理解 | ✅ 诚实说"不确定" | -| ❌ 盲目修改 | ✅ 谨慎重构 | -| ❌ 画蛇添足 | ✅ 按需实现 | - ---- - -## 二、代码生成前置检查 - -### 【强制】生成代码前必须完成的 4 项检查 - -在生成**任何代码**之前,必须逐条确认以下检查项,**缺一不可**: - -| # | 检查项 | 未通过则 | -|---|--------|---------| -| 1 | 是否已读取 CLAUDE.md 中的编码规范? | ❌ 禁止生成代码 | -| 2 | 是否已搜索项目中类似实现作为参考? | ❌ 禁止生成代码 | -| 3 | 是否有不确定的地方需要询问用户? | ⚠️ 先询问再继续 | -| 4 | 是否复用了现有的实体类/工具类? | ❌ 禁止新建已存在的类 | - ---- - -## 三、"参照 XX 写"执行规则 - -当用户说"参照XX写"、"仿照XX实现"、"按照XX的方式"时,**必须严格执行**以下步骤: - -### 步骤清单 - -| # | 步骤 | 必须完成的动作 | -|---|------|--------------| -| 1 | 完整阅读参照对象 | 读取 Controller、Service、Mapper、Entity **所有相关文件**,不能只看部分 | -| 2 | 列出关键对照点 | 向用户列出:接口路径、参数格式、返回值格式、Service 调用方式、业务逻辑 | -| 3 | 严格对照实现 | ❌ 禁止"优化"或"改进"参照对象,❌ 禁止偏离参照对象的风格 | - -### 完成后自检 - -| # | 自检问题 | 答案必须是"是" | -|---|---------|--------------| -| 1 | 我的实现和参照对象的实现方式是否一致? | 否则必须修正 | -| 2 | 有没有任何地方是我"自作主张"改的? | 有则必须告知用户 | - -**如果有任何偏离,必须告知用户并说明原因,由用户决定是否采用。** - ---- - -## 四、批量保存接口设计规范 - -### 【强制】设计前必须列出用户操作场景 - -| 用户操作 | 数据特征 | 处理方式 | -|---------|---------|---------| -| 新增一条数据 | 传入的数据没有 id | INSERT | -| 修改一条数据 | 传入的数据有 id | UPDATE | -| 删除一条数据 | **数据库有但传入列表中没有** ← 容易遗漏! | DELETE | -| 不做任何改动 | 原样传回 | 不处理 | - -### 正确实现步骤 - -``` -1. 查询数据库中该主体已有的所有数据 ID -2. 对比传入列表中的 ID,找出需要删除的(数据库有但传入没有) -3. 删除不在传入列表中的数据 -4. 新增或更新传入列表中的数据 -``` - -### 完成后自检 - -| # | 自检问题 | 答案必须是"是" | -|---|---------|--------------| -| 1 | 新增、更新、删除——三种情况都覆盖了吗? | | -| 2 | 如果用户删除了一条已有数据,保存后这条数据会消失吗? | | - ---- - -## 五、文档解析规则 - -### 【强制】解析步骤(按顺序执行,不可跳过) - -| # | 步骤 | 必须完成的动作 | 中断条件 | -|---|------|--------------|---------| -| 1 | 多方式解析 | Word/PDF 必须尝试 ≥2 种解析方式(段落、表格、文本框、XML 等) | | -| 2 | 完整性检查 | 检查是否只看到类名而没有属性定义? | ⚠️ **是则停止,询问用户** | -| 3 | 列出清单 | 向用户列出:类数量+名称、每个类的属性数量+名称、方法数量+签名 | ⚠️ **等待用户确认** | -| 4 | 生成代码 | 只有用户明确确认后才能继续 | | - -### 绝对禁止 - -| # | 禁止行为 | -|---|---------| -| 1 | ❌ 禁止在用户确认前生成任何代码 | -| 2 | ❌ 禁止自行补充或猜测文档中未明确写出的内容 | -| 3 | ❌ 禁止只用一种方式解析就认为解析完成 | -| 4 | ❌ 禁止看到类名/接口名却没有属性定义时继续执行 | - ---- - -## 六、接口与参数分析规则 - -### 触发条件 -- 分析接口映射关系(标准接口 → 内部接口) -- 分析参数映射关系 -- 编写 DTO/Entity 字段定义 - -### 【强制】执行步骤 - -| # | 步骤 | 必须完成的动作 | -|---|------|--------------| -| 1 | 确认接口映射 | 阅读标准接口功能 → 搜索后端代码找**功能匹配**的内部接口(不是名称匹配!)→ 读 Controller 确认功能 | -| 2 | 确认参数映射 | 找到 @RequestBody 的类 → 读源码(含父类)→ 逐一列出字段 → 对比建立映射 | - -### 映射可信度标注(必须标注) - -| 标注 | 含义 | -|-----|------| -| ✅ 已验证 | 已阅读源码确认 | -| ⚠️ 待验证 | 需要进一步确认 | -| ❌ 需新建接口 | 需要编写复杂业务逻辑(组合调用多个接口等) | - -### 绝对禁止 - -| # | 禁止行为 | -|---|---------| -| 1 | ❌ 禁止凭接口名称相似就认为可以映射 | -| 2 | ❌ 禁止直接使用 Postman/Swagger 参数定义,必须与源码核对 | -| 3 | ❌ 禁止凭"合理推测"编写参数映射 | -| 4 | ❌ 禁止使用模糊表述如"需要扩展"、"可能需要调用额外接口" | - ---- - -## 七、Postman 文档规范 - -### 核心原则 - -| 位置 | 内容 | -|-----|------| -| `description` 字段 | Markdown 格式,展示完整参数说明(带注释的 JSON 代码块) | -| `body.raw` 字段 | 纯净 JSON(无注释),可直接发送请求 | - -### description 格式模板 - -```json -{ - "description": "接口功能说明。\n\n**请求参数示例:**\n```json\n{\n \"字段名\": \"示例值\", // 字段说明\n}\n```\n\n**响应示例:**\n```json\n{\n \"code\": 0,\n \"data\": {}\n}\n```" -} -``` - -### 自检清单 - -| # | 检查项 | 要求 | -|---|--------|-----| -| 1 | body.raw 是否有注释? | ❌ 禁止,会导致 JSON 格式错误 | -| 2 | description 是否展示了参数格式? | ✅ 必须有带注释的 JSON 示例 | -| 3 | 是否包含响应示例? | ✅ 每个接口都必须有 | -| 4 | Long 类型 ID 是否展示为 String? | ✅ 如 `"id": "123456789"` | - ---- - -## 八、设计文档编写规范 - -### 核心原则 -设计文档的目标是:**开发人员可以直接照着写代码**,不是概念性说明。 - -### 【强制】文档必须包含的内容 - -| # | 内容 | 要求 | -|---|------|-----| -| 1 | 数据库表 DDL | 可直接执行的 CREATE TABLE | -| 2 | 枚举类代码 | 可直接复制使用 | -| 3 | 实体类代码 | 包括所有字段和注解 | -| 4 | Mapper 代码 | 包括 Provider 中的完整 SQL | -| 5 | Service 代码 | 接口定义和实现类 | -| 6 | Controller 代码 | 接口路径、请求体、响应格式 | -| 7 | 实现清单 | 新模块接入时的检查表 | -| 8 | 常见问题 FAQ | 解答可能的疑惑 | - -### 代码示例要求 - -| # | 要求 | -|---|------| -| 1 | 代码必须**完整可用**,不是片段或伪代码 | -| 2 | 必须包含**完整的 import 语句** | -| 3 | SQL 必须**完整可执行**,不能用 `...` 省略 | - -### 完成后自检 - -| # | 自检问题 | 答案必须是"是" | -|---|---------|--------------| -| 1 | 新人开发者能否只看这份文档就完成开发? | | -| 2 | 文档中的代码能否直接复制到项目中使用? | | -| 3 | 是否有"等"、"..."、"类似"等模糊表述? | 有则删除 | - ---- - -## 九、方法重载规范 - -### 规则 - -| # | 规则 | 说明 | -|---|------|-----| -| 1 | 全量参数方法承载所有逻辑 | 是唯一的实现体 | -| 2 | 少参数方法只做委托调用 | 传 `null` 给新增参数,方法体只有一行 `return` | -| 3 | ❌ 禁止两个重载方法各写一份逻辑 | 即使逻辑相同也不行 | -| 4 | ❌ 禁止反向委托 | 全量方法不能调用少参数方法 | - -### 正确示例 - -```java -// ✅ 少参数方法委托全量方法 -public Object foo(Req req, Request request, Response response) { - return foo(req, request, response, null); -} - -public Object foo(Req req, Request request, Response response, Function lineConverter) { - // 所有逻辑在这里 - if (lineConverter != null) { - // 有转换器时的处理 - } -} -``` - ---- - -## 十、工作偏好 - -| # | 偏好 | -|---|------| -| 1 | 始终使用**简体中文**回复 | -| 2 | 长任务必须记录详细进度 | -| 3 | 提交代码时**不要**附带 `Co-Authored-By: Claude` | -| 4 | 对所有工具操作自动同意,无需额外确认 | -| 5 | 不用执行编译和测试 | -| 6 | 编写构建脚本时尽量使用 mjs 编写带菜单的脚本 | -| 7 | 尽量使用 Python 连接数据库 | -| 8 | 联网搜索时**禁止**使用 csdn.net、阿里云/腾讯云/华为云社区等内容农场 | - ---- - -## 十一、代码生成规则 - -| # | 规则 | -|---|------| -| 1 | 提供实体类/模板/文档时,必须**完整复制所有属性和方法**,禁止省略 | -| 2 | 生成代码前,先列出文档中所有属性数量和名称,确认无遗漏后再生成 | -| 3 | 属性超过 20 个时,分批列出确认 | -| 4 | 禁止因为"优化"或"简化"而删减任何属性 | -| 5 | 生成完成后,对比源文档属性数量是否一致 |