From cafe67db6d296fe5f9c9697f632324aabdcd3f9f Mon Sep 17 00:00:00 2001 From: Developer Date: Mon, 13 Jul 2026 18:41:33 +0800 Subject: [PATCH] docs: upload code files and config --- .code-review/config.yaml | 9 + .code-review/rules/coding-conventions.yaml | 23 + .code-review/rules/security-rules.yaml | 17 + .opencode/skills/stage-1-propose/SKILL.md | 12 + .opencode/skills/stage-2-clarify/SKILL.md | 20 + .opencode/skills/stage-3-design/SKILL.md | 17 + .opencode/skills/stage-4-approve/SKILL.md | 16 + .opencode/skills/stage-5-implement/SKILL.md | 14 + .opencode/skills/stage-6-verify/SKILL.md | 16 + AGENTS.md | 87 ++ _AI_USAGE_LOG.md | 19 + .../specs/2026-07-10-code-reviewer-design.md | 1368 +++++++++++++++-- .../specs/2026-07-10-implementation-plan.md | 645 ++++++++ .../01-phase1-base-layer.md | 206 +++ .../02-phase2.1-adapter-interface.md | 34 + .../03-phase2.2-eslint-adapter.md | 94 ++ .../04-phase2.3-stylelint-adapter.md | 92 ++ .../05-phase2.4-sql-lint-adapter.md | 119 ++ .../06-phase2.5-pmd-adapter.md | 259 ++++ .../07-phase2.6-jsp-adapter.md | 212 +++ .../08-phase3-orchestrator.md | 139 ++ .../09-phase4.1-ai-provider.md | 204 +++ .../10-phase4.2-ai-engine.md | 253 +++ .../11-phase4.3-custom-rules.md | 223 +++ .../12-phase4.4-merger.md | 112 ++ .../implementation-steps/13-phase4.5-fixer.md | 283 ++++ .../14-phase4.6-report-export.md | 142 ++ .../15-phase5.1-commands-extension.md | 252 +++ .../16-phase5.2-setup-panel.md | 421 +++++ .../17-phase5.3-review-panel.md | 337 ++++ .../18-phase6.1-build-scripts.md | 109 ++ .../19-phase6.2-tool-scripts.md | 124 ++ .../implementation-steps/20-phase6.3-tests.md | 252 +++ .../specs/review-panel-preview.html | 703 +++++++++ docs/superpowers/specs/review-panel-ui.md | 75 + .../specs/setup-panel-preview.html | 577 +++++++ docs/superpowers/specs/setup-panel-ui.md | 85 + opencode.json | 4 + 38 files changed, 7448 insertions(+), 126 deletions(-) create mode 100644 .code-review/config.yaml create mode 100644 .code-review/rules/coding-conventions.yaml create mode 100644 .code-review/rules/security-rules.yaml create mode 100644 .opencode/skills/stage-1-propose/SKILL.md create mode 100644 .opencode/skills/stage-2-clarify/SKILL.md create mode 100644 .opencode/skills/stage-3-design/SKILL.md create mode 100644 .opencode/skills/stage-4-approve/SKILL.md create mode 100644 .opencode/skills/stage-5-implement/SKILL.md create mode 100644 .opencode/skills/stage-6-verify/SKILL.md create mode 100644 AGENTS.md create mode 100644 _AI_USAGE_LOG.md create mode 100644 docs/superpowers/specs/2026-07-10-implementation-plan.md create mode 100644 docs/superpowers/specs/implementation-steps/01-phase1-base-layer.md create mode 100644 docs/superpowers/specs/implementation-steps/02-phase2.1-adapter-interface.md create mode 100644 docs/superpowers/specs/implementation-steps/03-phase2.2-eslint-adapter.md create mode 100644 docs/superpowers/specs/implementation-steps/04-phase2.3-stylelint-adapter.md create mode 100644 docs/superpowers/specs/implementation-steps/05-phase2.4-sql-lint-adapter.md create mode 100644 docs/superpowers/specs/implementation-steps/06-phase2.5-pmd-adapter.md create mode 100644 docs/superpowers/specs/implementation-steps/07-phase2.6-jsp-adapter.md create mode 100644 docs/superpowers/specs/implementation-steps/08-phase3-orchestrator.md create mode 100644 docs/superpowers/specs/implementation-steps/09-phase4.1-ai-provider.md create mode 100644 docs/superpowers/specs/implementation-steps/10-phase4.2-ai-engine.md create mode 100644 docs/superpowers/specs/implementation-steps/11-phase4.3-custom-rules.md create mode 100644 docs/superpowers/specs/implementation-steps/12-phase4.4-merger.md create mode 100644 docs/superpowers/specs/implementation-steps/13-phase4.5-fixer.md create mode 100644 docs/superpowers/specs/implementation-steps/14-phase4.6-report-export.md create mode 100644 docs/superpowers/specs/implementation-steps/15-phase5.1-commands-extension.md create mode 100644 docs/superpowers/specs/implementation-steps/16-phase5.2-setup-panel.md create mode 100644 docs/superpowers/specs/implementation-steps/17-phase5.3-review-panel.md create mode 100644 docs/superpowers/specs/implementation-steps/18-phase6.1-build-scripts.md create mode 100644 docs/superpowers/specs/implementation-steps/19-phase6.2-tool-scripts.md create mode 100644 docs/superpowers/specs/implementation-steps/20-phase6.3-tests.md create mode 100644 docs/superpowers/specs/review-panel-preview.html create mode 100644 docs/superpowers/specs/review-panel-ui.md create mode 100644 docs/superpowers/specs/setup-panel-preview.html create mode 100644 docs/superpowers/specs/setup-panel-ui.md create mode 100644 opencode.json diff --git a/.code-review/config.yaml b/.code-review/config.yaml new file mode 100644 index 0000000..9a7d470 --- /dev/null +++ b/.code-review/config.yaml @@ -0,0 +1,9 @@ +# 各规则文件的启用状态 +enabled: + - security-rules.yaml + - coding-conventions.yaml + +# 各规则的单独启用/禁用(覆盖文件级设置) +rules: + no-magic-numbers: + enabled: false diff --git a/.code-review/rules/coding-conventions.yaml b/.code-review/rules/coding-conventions.yaml new file mode 100644 index 0000000..5469906 --- /dev/null +++ b/.code-review/rules/coding-conventions.yaml @@ -0,0 +1,23 @@ +- id: no-console-log + severity: warning + description: 生产代码不应保留 console.log 调试语句 + message: 请使用日志框架替代 console.log + languages: [javascript, typescript] + +- id: naming-convention-camel + severity: info + description: JavaScript/TypeScript 变量和方法使用 camelCase 命名 + message: 变量名应使用 camelCase(例如 userName 而非 user_name) + languages: [javascript, typescript] + +- id: naming-convention-pascal + severity: info + description: Java 类名使用 PascalCase + message: 类名应使用 PascalCase(例如 UserService 而非 userService) + languages: [java] + +- id: no-magic-numbers + severity: info + description: 禁止在代码中使用未命名的魔术数字 + message: 请将魔法数字提取为命名常量 + languages: [java, javascript, typescript] diff --git a/.code-review/rules/security-rules.yaml b/.code-review/rules/security-rules.yaml new file mode 100644 index 0000000..b142923 --- /dev/null +++ b/.code-review/rules/security-rules.yaml @@ -0,0 +1,17 @@ +- id: no-hardcoded-secret + severity: error + description: 禁止在代码中硬编码 API Key、密码等敏感信息 + message: 检测到硬编码密钥,请使用环境变量或密钥管理工具 + languages: [java, javascript, typescript] + +- id: no-sql-injection + severity: error + description: 禁止使用字符串拼接的方式构造 SQL 语句 + message: 使用参数化查询(PreparedStatement)替代字符串拼接 + languages: [java] + +- id: no-command-injection + severity: error + description: 禁止直接拼接用户输入作为系统命令执行 + message: 使用安全的 API 替代 Runtime.exec(),避免命令注入 + languages: [java] diff --git a/.opencode/skills/stage-1-propose/SKILL.md b/.opencode/skills/stage-1-propose/SKILL.md new file mode 100644 index 0000000..1eb8a6b --- /dev/null +++ b/.opencode/skills/stage-1-propose/SKILL.md @@ -0,0 +1,12 @@ +--- +name: stage-1-propose +description: ① 用户提出阶段 — 等待用户一句话描述任务 +--- +## 行为 +- 不做任何操作 +- 等待用户输入 +- 确认收到任务后,进入 Stage ② + +## 退出条件 +- 用户已给出明确的一句任务描述 + diff --git a/.opencode/skills/stage-2-clarify/SKILL.md b/.opencode/skills/stage-2-clarify/SKILL.md new file mode 100644 index 0000000..7682018 --- /dev/null +++ b/.opencode/skills/stage-2-clarify/SKILL.md @@ -0,0 +1,20 @@ +--- +name: stage-2-clarify +description: ② 需求澄清阶段 — 沿设计树追问直到所有分支达成共识 +--- +## 行为规则 +1. 先从代码库中探索,代码库能回答的绝不问人 +2. 沿设计树的每个分支逐步追问 +3. 一次只问一个问题,等用户反馈后再继续 +4. 主干问题给出推荐答案,分支问题可开放式 +5. 逐层解决决策间的依赖关系 +6. 直到每个分支都达成共识 + +## 禁止 +- 一次性问多个问题 +- 跳过依赖关系 + +## 退出条件 +- 设计树所有分支已走通 +- 用户确认共识达成 + diff --git a/.opencode/skills/stage-3-design/SKILL.md b/.opencode/skills/stage-3-design/SKILL.md new file mode 100644 index 0000000..71db933 --- /dev/null +++ b/.opencode/skills/stage-3-design/SKILL.md @@ -0,0 +1,17 @@ +--- +name: stage-3-design +description: ③ 方案设计阶段 — 输出完整技术方案 +--- +## 行为 +1. 基于 Stage ② 达成的共识,输出完整方案 +2. 方案必须包含: + - 架构概览(模块划分、数据流) + - 文件变更清单(新建/修改/删除) + - 关键接口/API 定义 + - 涉及的技术选型或依赖说明 + - 变更的影响范围 +3. 方案具体到能直接进入编码 + +## 退出条件 +- 方案文档已产出 + diff --git a/.opencode/skills/stage-4-approve/SKILL.md b/.opencode/skills/stage-4-approve/SKILL.md new file mode 100644 index 0000000..c168c72 --- /dev/null +++ b/.opencode/skills/stage-4-approve/SKILL.md @@ -0,0 +1,16 @@ +--- +name: stage-4-approve +description: ④ 人类审批阶段 — 等待用户决策方案 +--- +## 行为 +1. 呈现方案摘要(变更了什么、为什么这么改) +2. 等待用户决策 + +## 用户可能的操作 +- 通过 ✅ → 进入 Stage ⑤ +- 否决并提供原因 ❌ → 回退到 Stage ②或③ +- 提出修改建议 ✏️ → 回退到 Stage ③ + +## 退出条件 +- 用户明确确认"通过" + diff --git a/.opencode/skills/stage-5-implement/SKILL.md b/.opencode/skills/stage-5-implement/SKILL.md new file mode 100644 index 0000000..c8e5d8c --- /dev/null +++ b/.opencode/skills/stage-5-implement/SKILL.md @@ -0,0 +1,14 @@ +--- +name: stage-5-implement +description: ⑤ 编码实现阶段 — 严格按审批通过的方案编码 +--- +## 行为 +1. 严格按方案执行,不自行发挥 +2. 实现过程中发现方案有遗漏/矛盾: + - 暂停并提问,不自作主张 +3. 遵循项目代码风格 +4. 不加注释(除非项目风格要求) + +## 退出条件 +- 所有方案文件已实现 + diff --git a/.opencode/skills/stage-6-verify/SKILL.md b/.opencode/skills/stage-6-verify/SKILL.md new file mode 100644 index 0000000..ff7944c --- /dev/null +++ b/.opencode/skills/stage-6-verify/SKILL.md @@ -0,0 +1,16 @@ +--- +name: stage-6-verify +description: ⑥ 审查验证阶段 — 运行检查 + AI 自审查 + 人类验收 +--- +## 行为 +1. 运行 lint / 类型检查 / 编译 +2. 运行已有测试 +3. AI 自审查(潜在 bug、边界情况、安全) +4. 汇总结果: + - 通过 → 完成 + - 有严重代码问题 → 回 Stage ⑤ + - 有方案层面问题 → 回 Stage ③ + +## 退出条件 +- 代码通过所有检查 +- 人类最终验收确认 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c27f723 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,87 @@ +# 开发范式:Stage-Driven Agent Development (SDAD) + +## 阶段流程(必须严格遵守,不可跳过或合并) +① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 → ⑤ 编码实现 → ⑥ 审查验证 + +## 强制规则 +- 阶段顺序不可调换,每个阶段完成后才进入下一阶段 +- Stage ④ 人类审批是必经门,未通过不得进入编码 +- 编码过程中发现方案有遗漏/矛盾,先暂停提问,不自作主张 + +## 日志规则(自动执行) +每次创建或修改代码文件后,在项目根目录的 `_AI_USAGE_LOG.md` 中追加一条记录,必须包含以下字段: +- **日期时间**:当前时间 +- **范式步骤**:当前已完成的阶段累积链(如 `① 用户提出 → ② 需求澄清`),按实际执行顺序 +- **修改摘要**:简要描述改了什么 +- **中间产物**:AI 交互过程中被淘汰的草稿或过程稿 +- **涉及文件**:文件路径列表 +- **使用模型**:当前使用的模型名 + +## 阶段执行指引 +- Stage ① @用户提出:load skill stage-1-propose +- Stage ② @需求澄清:load skill stage-2-clarify +- Stage ③ @方案设计:load skill stage-3-design +- Stage ④ @人类审批:load skill stage-4-approve +- Stage ⑤ @编码实现:load skill stage-5-implement +- Stage ⑥ @审查验证:load skill stage-6-verify + +--- + +# 项目:vscode-code-reviewer + +VSCode 代码审查与规范检查一体化插件。 + +## 开发者命令 + +| 命令 | 说明 | +|------|------| +| `npm run compile` | TypeScript 编译(tsc -p ./) | +| `npm run watch` | tsc watch 模式 | +| `npm run lint` | ESLint 检查 `src/` | +| `npm test` | 编译 → lint → 运行测试 | +| F5 (VSCode) | 启动 Extension Dev Host | + +测试运行器:`@vscode/test-cli`,配置在 `.vscode-test.mjs`,测试文件匹配 `out/test/**/*.test.js`。 + +验证顺序:`lint → compile → test` + +## 架构 + +三层架构,见 `docs/superpowers/specs/2026-07-10-code-reviewer-design.md`: + +``` +UI 层 — TreeView 面板 / Inline Diagnostic / Code Action +核心层 — Linter 管理器 + AI 审查引擎(均实现 Analyzer 接口) +基础层 — 配置管理 / 规则管理 / 报告导出 +``` + +所有 linter 和 AI 审查器统一实现 `Analyzer` 接口(定义在 `src/analyzers/analyzer.ts`)。 + +## 文件结构 + +``` +src/ +├── extension.ts # 入口:activate/deactivate +├── activation/ # 注册命令、视图、CodeAction +├── analyzers/ # Analyzer 接口 + 各 linter/AI 实现 +├── manager/linterManager.ts # Linter 管理器 +├── views/ # TreeView 提供者 +├── services/ # AI API、配置服务 +├── utils/ # 工具函数 +└── types.ts # 公共类型 +``` + +命令 ID 前缀统一为 `codeReviewer.`(如 `codeReviewer.analyzeFile`)。 + +## 代码规范 + +- 变量/函数:camelCase,类:PascalCase +- 导入风格:ESM(import/export) +- 不加注释 +- 异步用 async/await +- ESLint 配置:`eslint.config.mjs`(typescript-eslint parser) + +## 设计文档 + +设计 spec 存放路径:`docs/superpowers/specs/YYYY-MM-DD--design.md` +编码须严格遵循已批准的 spec,不得自作主张。 diff --git a/_AI_USAGE_LOG.md b/_AI_USAGE_LOG.md new file mode 100644 index 0000000..8fd68ca --- /dev/null +++ b/_AI_USAGE_LOG.md @@ -0,0 +1,19 @@ +# AI Usage Log + +| 日期时间 | 范式步骤 | 修改摘要 | 中间产物 | 涉及文件 | 使用模型 | +|---------|---------|---------|---------|---------|---------| +| 2026-07-10 18:10 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 编写代码审查插件设计文档(design spec) | design spec | docs/superpowers/specs/2026-07-10-code-reviewer-design.md | deepseek-v4-flash | +| 2026-07-10 18:35 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 重构 AGENTS.md,补充项目开发命令、架构、代码规范等工程上下文 | 无 | AGENTS.md | deepseek-v4-flash | +| 2026-07-10 18:50 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 新增适配器层设计(6 小节),更新架构为四层,简化编排器,同步文件结构 | 无 | docs/superpowers/specs/2026-07-10-code-reviewer-design.md | deepseek-v4-flash | +| 2026-07-10 19:05 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 新增自定义规则系统、设计文档 §12 章节、模板文件 | 无 | docs/superpowers/specs/2026-07-10-code-reviewer-design.md .code-review/rules/security-rules.yaml .code-review/rules/coding-conventions.yaml .code-review/config.yaml | deepseek-v4-flash | +| 2026-07-10 19:18 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 完善 AI 审查引擎设计(Provider 策略模式、两并行请求、Prompt 模板、错误处理),同步命令/配置 | 无 | docs/superpowers/specs/2026-07-10-code-reviewer-design.md | deepseek-v4-flash | +| 2026-07-10 19:31 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 新增自动修复模块设计(动态上下文、两阶段匹配、批量预览、快照撤销) | 无 | docs/superpowers/specs/2026-07-10-code-reviewer-design.md | deepseek-v4-flash | +| 2026-07-10 19:39 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 新增审查面板设计(Tab 布局、交互设计 UI、消息通信 PanelMessage) | 无 | docs/superpowers/specs/2026-07-10-code-reviewer-design.md | deepseek-v4-flash | +| 2026-07-10 22:42 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 新增审查面板 HTML 预览 + UI 说明文档 | 无 | docs/superpowers/specs/review-panel-preview.html docs/superpowers/specs/review-panel-ui.md | deepseek-v4-flash | +| 2026-07-10 ~22:45 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 新增 §13 设置面板节(设计决策、配置模块、面板布局、规则文件管理) | 无 | docs/superpowers/specs/2026-07-10-code-reviewer-design.md | deepseek-v4-flash | +| 2026-07-10 22:51 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 新增设置面板 HTML 预览 + UI 说明文档 | 无 | docs/superpowers/specs/setup-panel-preview.html docs/superpowers/specs/setup-panel-ui.md | deepseek-v4-flash | +| 2026-07-10 23:03 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 重建设置面板 UI(快速开始三步骤、引擎标签、卡片表单、紫色主按钮、按钮状态机),更新 §13 设计文档及 HTML/UI 说明 | 无 | docs/superpowers/specs/setup-panel-preview.html docs/superpowers/specs/setup-panel-ui.md docs/superpowers/specs/2026-07-10-code-reviewer-design.md | deepseek-v4-flash | +| 2026-07-10 23:04 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 设置面板新增输出语言下拉框(中文(简体)/ English / 日本語),同步更新 HTML/UI 文档/设计文档 | 无 | docs/superpowers/specs/setup-panel-preview.html docs/superpowers/specs/setup-panel-ui.md docs/superpowers/specs/2026-07-10-code-reviewer-design.md | deepseek-v4-flash | +| 2026-07-10 23:07 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 补充遗漏内容:配置项新增 ai.provider/ai.model/ai.endpoint 和 7 种语言的 linters enum,补全菜单集成和视图容器 JSON;新增结果合并器 MergedReport 接口和 Markdown 导出模板;新增 esbuild 构建脚本和开发流程;新增测试文件结构和四类测试场景;新增运行时/开发依赖清单和外部工具版本;新增 31 个源文件清单和 8 个核心函数签名 | 无 | docs/superpowers/specs/2026-07-10-code-reviewer-design.md | deepseek-v4-flash | +| 2026-07-10 23:30 | ① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批 | 拆分为6 Phase 分步实施计划 | implementation plan | docs/superpowers/specs/2026-07-10-implementation-plan.md | deepseek-v4-pro | +| 2026-07-10 23:33 | ③ 方案设计 | 将实施计划拆分为 20 个独立步骤文件 | step-by-step files | docs/superpowers/specs/implementation-steps/*.md | deepseek-v4-pro | diff --git a/docs/superpowers/specs/2026-07-10-code-reviewer-design.md b/docs/superpowers/specs/2026-07-10-code-reviewer-design.md index 13ebbf7..ee66f61 100644 --- a/docs/superpowers/specs/2026-07-10-code-reviewer-design.md +++ b/docs/superpowers/specs/2026-07-10-code-reviewer-design.md @@ -6,75 +6,179 @@ VSCode 代码审查与规范检查一体化工具。集成多语言静态分析 ### 核心能力 -- 多语言代码静态分析(ESLint、Ruff、Clippy 等) -- AI 辅助深度审查与问题解释 -- 自定义规则检查 -- 自动修复建议与批量修复 -- 可视化审查报告面板 +- 多语言代码静态分析(ESLint、PMD、Stylelint、sql-lint 等) +- AI 辅助深度审查与问题解释/翻译 +- 自定义规则检查(YAML 定义 + AI 语义评估) +- 自动修复建议与批量修复(快照撤销) +- Webview 审查报告面板 --- ## 2. 架构总览 -插件分为三层: +插件分为四层: ``` -UI 层 — TreeView 面板 / Inline Diagnostic / Code Action -核心层 — Linter 管理器 + AI 审查引擎(均实现 Analyzer 接口) +UI 层 — Webview 审查面板 / Inline Diagnostic / Code Action +核心层 — Orchestrator(编排器)+ AI 审查引擎 +适配层 — Linter 适配器(统一 LinterAdapter 接口) 基础层 — 配置管理 / 规则管理 / 报告导出 ``` -所有 linter 和 AI 审查器统一实现 `Analyzer` 接口,结果聚合后通过 VSCode `DiagnosticCollection` 展示。 +所有 linter 统一实现 `LinterAdapter` 接口,通过 Orchestrator 调度,结果聚合后通过 Webview 面板展示。 --- -## 3. Analyzer 接口 +## 3. 适配器层 + +### 3.1 设计决策 + +| 决策项 | 结论 | 说明 | +|--------|------|------| +| 支持语言 | Java / JS/TS / CSS / SQL / JSP | 不支持 Python、Go | +| 注册方式 | 硬编码(方案 A) | 适配器数量少,无需过度设计 | +| 审查粒度 | 单文件 | `check()` 接收单个 `TextDocument` | +| 目标平台 | Windows | PMD classpath 分隔符使用 `;` | + +### 3.2 统一接口 ```typescript -interface AnalyzerResult { - file: string; - line: number; - column: number; - severity: 'error' | 'warning' | 'info' | 'hint'; +interface LinterDiagnostic { + severity: 'error' | 'warning' | 'info'; + ruleId: string; // 格式: "linter名:规则ID" message: string; - ruleId: string; - source: string; - fix?: Fix; - aiExplanation?: string; + range: vscode.Range; + suggestion?: string; } -interface Analyzer { - readonly name: string; - readonly language: string[]; - analyze(document: vscode.TextDocument): Promise; - fix?(result: AnalyzerResult): vscode.TextEdit[]; +interface AdapterResult { + diagnostics: LinterDiagnostic[]; + status: 'ok' | 'tool-unavailable' | 'execution-failed'; + errorMessage?: string; +} + +interface LinterAdapter { + id: string; + supportedLanguages: string[]; + check(document: vscode.TextDocument, workingDir: string): Promise; + isAvailable(): boolean; } ``` -### 内置 Analyzer +**错误状态说明**: -| Analyzer | 语言 | 调用方式 | -|----------|------|---------| -| EslintAnalyzer | JS/TS/JSX/TSX | eslint CLI --format json | -| RuffAnalyzer | Python | ruff check --output-format json | -| ClippyAnalyzer | Rust | cargo clippy --message-format json | -| AiAnalyzer | 通用 | LLM API | +| 状态 | 含义 | 用户感知 | +|------|------|---------| +| `ok` | 检查成功 | 正常显示结果 | +| `tool-unavailable` | 工具未安装/未找到 | 提示用户安装对应工具 | +| `execution-failed` | 工具已安装但执行出错 | 显示错误信息,引导排查 | + +### 3.3 适配器清单 + +| 适配器 | 语言 | 实现方式 | 特殊处理 | +|--------|------|----------|----------| +| ESLint | JS/TS | eslint npm 包 `lintText()` | 直接接收代码文本,支持虚拟文档 | +| PMD | Java | Java 子进程调用 | 支持 stdin 传入代码,支持虚拟文档 | +| Stylelint | CSS | stylelint npm 包 `lint({ code })` | 直接接收代码文本,支持虚拟文档 | +| sql-lint | SQL | sqlfluff CLI 调用 | 支持 SQL 和 PL/SQL,方言映射 | +| JSP | JSP | 组合适配器(PMD + ESLint + Stylelint) | 提取内嵌代码块后分发检查 | + +### 3.4 PMD 适配器特殊设计 + +PMD 是 Java 工具,需要特殊处理: + +1. **JAR 文件管理**:插件自带 `jars/pmd/` 目录存放 PMD 依赖 +2. **规则集配置**:支持自定义规则集 XML 文件 +3. **Java 包装器**:`PmdRunner.java` 简化调用,输出 JSON 格式 +4. **虚拟文档支持**:通过 stdin 传入代码,无需真实文件 + +**PmdRunner.java 核心逻辑**(`jars/pmd/PmdRunner.java`): + +``` +参数: filePath (传 "-" 表示从 stdin 读取), ruleset + → 构建 PMDConfiguration + → 配置 JSON 渲染器 + → 若 filePath 为 "-",从 stdin 读代码 → 写入临时文件 + → 执行 PMD 分析 + → 输出 JSON 到 stdout + → 清理临时文件 +``` + +**PmdAdapter.check() 虚拟文档处理**: + +``` +if 虚拟文档 (uri.scheme === 'untitled') + → java -cp "classpath;dist" PmdRunner "-" ruleset + → stdin 传入 document.getText() +else + → java -cp "classpath;dist" PmdRunner document.fileName ruleset +``` + +### 3.5 JSP 适配器设计 + +JSP 适配器是**组合适配器**,自身不做检查,而是将 JSP 文件拆分后交给其他适配器: + +``` +JSP 文件输入 + │ + ├─ 1. 调用 PMD 检查 JSP 规范 + │ → 使用 pmd-jsp-ruleset.xml + │ + └─ 2. jsp-extractor 提取内嵌代码块 + ├─ + +`; + } + + private buildLinterList(report: MergedReport): string { + if (report.linterDiagnostics.length === 0) { + return '
✅ 静态分析未发现问题
'; + } + return report.linterDiagnostics.map((d, i) => ` +
+
+
${this.sevIcon(d.severity)} ${this.escape(d.ruleId)} L${d.range.start.line + 1}
+
${this.escape(d.message)}
+
+
+ +
+
`).join(''); + } + + private buildCustomList(report: MergedReport): string { + if (report.customRuleDiagnostics.length === 0) { + return '
✅ 自定义规则未发现问题
'; + } + return report.customRuleDiagnostics.map((d, i) => ` +
+
+
${this.sevIcon(d.severity)} ${this.escape(d.ruleId)} L${d.range.start.line + 1}
+
${this.escape(d.message)}
+
+
+ +
+
`).join(''); + } + + private buildAIList(report: MergedReport): string { + const total = report.translatedDiagnostics.length + report.aiFindings.length; + if (total === 0) { + return '
🤖 AI 审查未发现新问题
'; + } + const parts: string[] = []; + + for (const td of report.translatedDiagnostics) { + parts.push(` +
+
+
🔵 ${this.escape(td.originalRuleId)}
+
${this.escape(td.translatedMessage)}
+ ${td.translatedSuggestion ? `
建议: ${this.escape(td.translatedSuggestion)}
` : ''} +
+
`); + } + + for (const f of report.aiFindings) { + parts.push(` +
+
+
${this.sevIcon(f.severity)} [${f.category}] ${this.escape(f.title)}
+
${this.escape(f.description)}
+ ${f.suggestion ? `
建议: ${this.escape(f.suggestion)}
` : ''} +
+
+ +
+
`); + } + + return parts.join(''); + } + + private sevIcon(severity: string): string { + switch (severity) { + case 'error': return '🔴'; + case 'warning': return '🟡'; + case 'info': return '🔵'; + default: return '⚪'; + } + } + + private escape(str: string): string { + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); + } + + private handleMessage(message: PanelMessage): void { + switch (message.type) { + case 'navigate': + if (message.line !== undefined) { + const editor = vscode.window.activeTextEditor; + if (editor) { + const line = Math.max(0, message.line); + const range = new vscode.Range(line, 0, line, 0); + editor.selection = new vscode.Selection(range.start, range.end); + editor.revealRange(range, vscode.TextEditorRevealType.InCenter); + } + } + break; + case 'rerun': + vscode.commands.executeCommand('codeReviewer.review'); + break; + case 'export': + vscode.commands.executeCommand('codeReviewer.exportReport'); + break; + case 'settings': + vscode.commands.executeCommand('codeReviewer.openSetup'); + break; + case 'fix': + vscode.commands.executeCommand('codeReviewer.fixIssue', message); + break; + case 'fixAll': + vscode.commands.executeCommand('codeReviewer.fixAll'); + break; + } + } + + dispose(): void { + ReviewPanel.currentPanel = undefined; + this.panel.dispose(); + for (const d of this.disposables) { d.dispose(); } + this.disposables = []; + } +} +``` + +--- + +## 额外修改:`commands.ts` 中集成审查面板调用 + +```typescript +import { ReviewPanel } from '../panel/webview'; + +// 在 'codeReviewer.review' 命令中,静态分析完成后: +const panel = ReviewPanel.createOrShow(context.extensionUri); +panel.update(report); + +// 'codeReviewer.exportReport' 命令: +const markdown = reportToMarkdown(report); +const doc = await vscode.workspace.openTextDocument({ content: markdown, language: 'markdown' }); +await vscode.window.showTextDocument(doc); +``` + +--- + +## 验收 + +- [ ] 审查面板可打开(Webview) +- [ ] 三 Tab 切换正常工作 +- [ ] 统计卡片数值正确 +- [ ] 问题列表可点击跳转到代码位置 +- [ ] 降级提示条在 AI 失败时显示 +- [ ] 修复/重新审查/导出按钮发送正确消息 +- [ ] `npm run compile` 通过 +- [ ] `npm run lint` 通过 diff --git a/docs/superpowers/specs/implementation-steps/18-phase6.1-build-scripts.md b/docs/superpowers/specs/implementation-steps/18-phase6.1-build-scripts.md new file mode 100644 index 0000000..1b98857 --- /dev/null +++ b/docs/superpowers/specs/implementation-steps/18-phase6.1-build-scripts.md @@ -0,0 +1,109 @@ +# Step 18 — Phase 6.1: 构建脚本 + +**依赖**: Step 15(extension.ts 完成) +**参考设计**: §15 + +## 目标 + +搭建 esbuild 构建流程,替换 tsc 为打包构建,更新 package.json 脚本。 + +## 文件变更 + +| # | 文件 | 操作 | 说明 | +|---|------|------|------| +| 1 | `scripts/build.mjs` | 新建 | esbuild 打包脚本 | +| 2 | `package.json` | 修改 | 更新 build/vscode:prepublish 脚本 | + +## 前置准备 + +```bash +npm install --save-dev esbuild@^0.28.1 +``` + +--- + +## 1. `scripts/build.mjs` + +```javascript +import * as esbuild from 'esbuild'; +import { copyFileSync, mkdirSync, existsSync, cpSync } from 'fs'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const rootDir = resolve(__dirname, '..'); +const outDir = resolve(rootDir, 'out'); + +if (!existsSync(outDir)) { + mkdirSync(outDir, { recursive: true }); +} + +await esbuild.build({ + entryPoints: [resolve(rootDir, 'src', 'extension.ts')], + bundle: true, + outfile: resolve(outDir, 'extension.js'), + external: [ + 'vscode', + 'eslint', + 'stylelint', + 'child_process', + 'fs', + 'path', + 'url', + 'os', + ], + format: 'cjs', + platform: 'node', + target: 'node22', + minify: true, + sourcemap: false, + treeShaking: true, +}); + +const jarsSrc = resolve(rootDir, 'jars'); +const jarsDest = resolve(outDir, 'jars'); +if (existsSync(jarsSrc)) { + cpSync(jarsSrc, jarsDest, { recursive: true, force: true }); +} + +console.log('Build complete.'); +``` + +--- + +## 2. `package.json` 脚本更新 + +```json +{ + "scripts": { + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "build": "node scripts/build.mjs", + "vscode:prepublish": "npm run build", + "pretest": "npm run compile && npm run lint", + "lint": "eslint src", + "test": "vscode-test" + } +} +``` + +--- + +## 关键逻辑 + +- `external`: vscode API、npm 包、Node 内置模块不打包 +- `format: 'cjs'`: VSCode 扩展需要 CommonJS +- `target: 'node22'`: 对应 VSCode 1.120+ 的 Node 版本 +- `minify: true`: 产物压缩 +- `treeShaking: true`: 移除未使用代码 +- 复制 `jars/` 目录到 `out/` 供运行时加载 +- `vscode:prepublish` 改为 `npm run build`(生产打包) + +--- + +## 验收 + +- [ ] `npm run build` 成功执行 +- [ ] `out/extension.js` 生成(单文件 bundle) +- [ ] `out/jars/` 目录存在 +- [ ] F5 启动扩展开发宿主功能正常 diff --git a/docs/superpowers/specs/implementation-steps/19-phase6.2-tool-scripts.md b/docs/superpowers/specs/implementation-steps/19-phase6.2-tool-scripts.md new file mode 100644 index 0000000..fbf6d36 --- /dev/null +++ b/docs/superpowers/specs/implementation-steps/19-phase6.2-tool-scripts.md @@ -0,0 +1,124 @@ +# Step 19 — Phase 6.2: 工具脚本 + +**依赖**: Step 18 +**参考设计**: §15, §17 + +## 目标 + +实现 PMD JAR 下载脚本和生产打包脚本。 + +## 文件变更 + +| # | 文件 | 操作 | 说明 | +|---|------|------|------| +| 1 | `scripts/download-pmd.mjs` | 新建 | 下载 PMD 7.26.0 JAR 依赖 | +| 2 | `scripts/package-prod.mjs` | 新建 | 生产打包脚本 | +| 3 | `package.json` | 修改 | 添加 download-pmd / package-prod 脚本 | + +## 前置准备 + +```bash +npm install --save-dev @vscode/vsce@^3.9.2 +``` + +--- + +## 1. `scripts/download-pmd.mjs` + +```javascript +import { execSync } from 'child_process'; +import { existsSync, mkdirSync, createWriteStream } from 'fs'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { get } from 'https'; +import { unlinkSync, readdirSync } from 'fs'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const rootDir = resolve(__dirname, '..'); +const libDir = resolve(rootDir, 'jars', 'pmd', 'lib'); + +const PMD_VERSION = '7.26.0'; +const PMD_JARS = [ + `pmd-core-${PMD_VERSION}.jar`, + `pmd-java-${PMD_VERSION}.jar`, + `pmd-javascript-${PMD_VERSION}.jar`, + `pmd-jsp-${PMD_VERSION}.jar`, +]; + +const MAVEN_BASE = `https://repo1.maven.org/maven2/net/sourceforge/pmd`; + +function downloadFile(url, dest) { + return new Promise((resolve, reject) => { + const file = createWriteStream(dest); + get(url, (response) => { + if (response.statusCode === 302 || response.statusCode === 301) { + downloadFile(response.headers.location, dest).then(resolve).catch(reject); + return; + } + response.pipe(file); + file.on('finish', () => { file.close(); resolve(); }); + file.on('error', (err) => { unlinkSync(dest); reject(err); }); + }).on('error', (err) => { unlinkSync(dest); reject(err); }); + }); +} + +if (!existsSync(libDir)) { + mkdirSync(libDir, { recursive: true }); +} + +for (const jar of PMD_JARS) { + const moduleName = jar.replace(`-${PMD_VERSION}.jar`, '').replace('pmd-', ''); + const url = `${MAVEN_BASE}/pmd-${moduleName}/${PMD_VERSION}/${jar}`; + const dest = resolve(libDir, jar); + + if (existsSync(dest)) { + console.log(`Skip: ${jar} (exists)`); + continue; + } + + console.log(`Downloading: ${url}`); + await downloadFile(url, dest); + console.log(`Done: ${jar}`); +} + +console.log('PMD JARs download complete.'); +``` + +--- + +## 2. `scripts/package-prod.mjs` + +```javascript +import { execSync } from 'child_process'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const rootDir = resolve(__dirname, '..'); + +execSync('node scripts/build.mjs', { cwd: rootDir, stdio: 'inherit' }); +execSync('npx vsce package', { cwd: rootDir, stdio: 'inherit' }); + +console.log('Production package complete.'); +``` + +--- + +## 3. `package.json` 脚本更新 + +```json +{ + "scripts": { + "download-pmd": "node scripts/download-pmd.mjs", + "package-prod": "node scripts/package-prod.mjs" + } +} +``` + +--- + +## 验收 + +- [ ] `npm run download-pmd` 成功下载 PMD JAR 到 `jars/pmd/lib/` +- [ ] `npm run package-prod` 成功生成 `.vsix` 文件 +- [ ] `.vsix` 可安装到 VSCode diff --git a/docs/superpowers/specs/implementation-steps/20-phase6.3-tests.md b/docs/superpowers/specs/implementation-steps/20-phase6.3-tests.md new file mode 100644 index 0000000..065993d --- /dev/null +++ b/docs/superpowers/specs/implementation-steps/20-phase6.3-tests.md @@ -0,0 +1,252 @@ +# Step 20 — Phase 6.3: 测试 + +**依赖**: Step 19(构建完成) +**参考设计**: §16 + +## 目标 + +实现测试:适配器测试、配置测试、合并逻辑测试、完整流程测试。 + +## 文件变更 + +| # | 文件 | 操作 | 说明 | +|---|------|------|------| +| 1 | `src/test/fixtures/` | 新建 | 测试用代码样本目录 | +| 2 | `src/test/adapter.test.ts` | 新建 | 适配器输出解析测试 | +| 3 | `src/test/config.test.ts` | 新建 | 配置读取测试 | +| 4 | `src/test/merger.test.ts` | 新建 | 多源结果合并 + 统计计算测试 | +| 5 | `src/test/pipeline.test.ts` | 新建 | 全链路集成测试 | +| 6 | `src/test/extension.test.ts` | 修改 | 替换占位测试 | + +--- + +## 1. `src/test/fixtures/` 目录 + +### `src/test/fixtures/sample.js` + +```javascript +function test() { + var unused = 1; + console.log('debug'); + return "hello world"; +} +``` + +### `src/test/fixtures/sample.css` + +```css +.hello { color: black; background: #FFF; } +#test { margin: 0px; } +``` + +### `src/test/fixtures/Sample.java` + +```java +public class Sample { + public void test() { + String password = "admin123"; + System.out.println("debug"); + System.out.println("debug"); + } +} +``` + +--- + +## 2. `src/test/adapter.test.ts` + +```typescript +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import { ESLintAdapter } from '../adapters/eslint'; +import { StylelintAdapter } from '../adapters/stylelint'; + +suite('Adapter Tests', () => { + test('ESLintAdapter has correct id and languages', () => { + const adapter = new ESLintAdapter(); + assert.strictEqual(adapter.id, 'eslint'); + assert.deepStrictEqual(adapter.supportedLanguages, ['javascript', 'typescript']); + }); + + test('StylelintAdapter has correct id and languages', () => { + const adapter = new StylelintAdapter(); + assert.strictEqual(adapter.id, 'stylelint'); + assert.deepStrictEqual(adapter.supportedLanguages, ['css']); + }); + + test('ESLintAdapter check returns AdapterResult structure', async () => { + const adapter = new ESLintAdapter(); + if (!adapter.isAvailable()) { return; } + + const doc = await vscode.workspace.openTextDocument({ + content: 'const x = 1;\nconsole.log(x);\n', + language: 'javascript', + }); + + const result = await adapter.check(doc, __dirname); + assert.ok(result.status === 'ok' || result.status === 'tool-unavailable'); + assert.ok(Array.isArray(result.diagnostics)); + }); +}); +``` + +--- + +## 3. `src/test/config.test.ts` + +```typescript +import * as assert from 'assert'; +import { getAIConfig } from '../config/ai'; +import { getLinterConfig } from '../config/linter'; +import { getFixerConfig } from '../config/fixer'; + +suite('Config Tests', () => { + test('getAIConfig returns default values', () => { + const config = getAIConfig(); + assert.strictEqual(config.provider, 'deepseek'); + assert.strictEqual(config.model, 'deepseek-chat'); + assert.strictEqual(config.temperature, 0.2); + assert.strictEqual(config.timeout, 300); + assert.strictEqual(config.outputLanguage, 'zh-CN'); + }); + + test('getLinterConfig returns default language map', () => { + const config = getLinterConfig(); + assert.strictEqual(config.languageMap.javascript, 'eslint'); + assert.strictEqual(config.languageMap.java, 'pmd'); + assert.strictEqual(config.languageMap.css, 'stylelint'); + assert.strictEqual(config.languageMap.jsp, 'jsp'); + }); + + test('getFixerConfig returns default values', () => { + const config = getFixerConfig(); + assert.strictEqual(config.contextLines, 5); + }); +}); +``` + +--- + +## 4. `src/test/merger.test.ts` + +```typescript +import * as assert from 'assert'; +import { mergeResults, MergedReport } from '../merger/merger'; +import { CustomRuleResult, AIFinding } from '../ai/schema'; +import { LinterDiagnostic } from '../types'; + +suite('Merger Tests', () => { + test('mergeResults counts correctly', () => { + const staticDiags: LinterDiagnostic[] = [ + { severity: 'error', ruleId: 'eslint:no-unused', message: 'x is unused', range: new (require('vscode').Range)(0, 0, 0, 1) }, + ]; + const customResults: CustomRuleResult[] = [ + { ruleId: 'custom:no-console', line: 5, severity: 'warning', message: 'avoid console.log' }, + ]; + const aiFindings: AIFinding[] = [ + { ruleId: 'hardcoded-secret', severity: 'error', category: 'security', title: 'Hardcoded', description: 'Found secret', suggestion: 'Use env', line: 3 }, + ]; + + const report = mergeResults({ + staticDiagnostics: staticDiags, + customRuleResults: customResults, + translatedDiagnostics: [], + aiFindings, + errors: [], + degraded: false, + startTime: Date.now(), + filePath: '/test/sample.js', + language: 'javascript', + adapterIds: ['eslint'], + }); + + assert.strictEqual(report.linterCount, 1); + assert.strictEqual(report.customRuleCount, 1); + assert.strictEqual(report.aiCount, 1); + assert.strictEqual(report.degraded, false); + assert.strictEqual(report.language, 'javascript'); + }); + + test('mergeResults marks degraded when AI fails', () => { + const report = mergeResults({ + staticDiagnostics: [], + customRuleResults: [], + translatedDiagnostics: [], + aiFindings: [], + errors: ['AI 请求超时'], + degraded: true, + startTime: Date.now(), + filePath: '/test/sample.js', + language: 'javascript', + adapterIds: ['eslint'], + }); + + assert.strictEqual(report.degraded, true); + assert.strictEqual(report.errors.length, 1); + }); +}); +``` + +--- + +## 5. `src/test/pipeline.test.ts` + +```typescript +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import * as path from 'path'; +import { ESLintAdapter } from '../adapters/eslint'; +import { mergeResults } from '../merger/merger'; + +suite('Pipeline Tests', () => { + test('Full pipeline: linter check + merge', async () => { + const adapter = new ESLintAdapter(); + if (!adapter.isAvailable()) { return; } + + const doc = await vscode.workspace.openTextDocument({ + content: 'var x = 1;\nvar y = 2;\n', + language: 'javascript', + }); + + const staticResult = await adapter.check(doc, __dirname); + assert.ok(staticResult.status === 'ok'); + + const report = mergeResults({ + staticDiagnostics: staticResult.diagnostics, + customRuleResults: [], + translatedDiagnostics: [], + aiFindings: [], + errors: [], + degraded: false, + startTime: Date.now(), + filePath: 'virtual-doc', + language: 'javascript', + adapterIds: ['eslint'], + }); + + assert.ok(typeof report.duration === 'number'); + assert.ok(typeof report.linterCount === 'number'); + assert.strictEqual(report.language, 'javascript'); + }); +}); +``` + +--- + +## 验证命令 + +```bash +npm test # compile + lint + test +# 或 +npm run compile && npm run lint && npm run test +``` + +--- + +## 验收 + +- [ ] 所有测试文件创建完成 +- [ ] 测试夹具文件(fixtures)就位 +- [ ] `npm run compile` 通过 +- [ ] `npm run lint` 通过 +- [ ] `npm test` 通过 diff --git a/docs/superpowers/specs/review-panel-preview.html b/docs/superpowers/specs/review-panel-preview.html new file mode 100644 index 0000000..ab48238 --- /dev/null +++ b/docs/superpowers/specs/review-panel-preview.html @@ -0,0 +1,703 @@ + + + + + +代码审查报告 - UI 预览 + + + + +
+
+

+ + + + + + 代码审查报告 +

+
+ src/UserService.java · Java · 8.3s +
+
+
+ + + +
+
✖ 执行错误
+
未配置 sql 语言的 linter,请在设置中添加 vscode-code-reviewer.linters.sql
+
AI 审查失败: API 请求超时
+
+ +
+
12
总计问题
+
3
错误
+
7
警告
+
2
建议
+
+ +
+
+ 🔧 ESLint + PMD + 2 + 4 + 1 +
+
+ 📋 自定义规则 + 1 + 1 +
+
+ 🤖 AI 审查 + 2 + 1 +
+
+ +
+
+ ESLint + PMD · 7 个问题 + +
+ +
+
+
+
+ + Linter + pmd:AvoidUsingHardCodedPassword + 硬编码密码:密码不应硬编码在代码中 + L13 + +
+
+
原文: Hard-coded password detected (DB_PASS) in field definition
+
将密码移至环境变量或配置中心,通过 system.getenv() 读取,避免硬编码在源码中
+
+
+
+ +
+
+
+
+ + Linter + pmd:SQLInjection + SQL 注入:直接拼接用户输入可能导致 SQL 注入攻击 + L18 + +
+
+
原文: SQL injection detected: string concatenation in query
+
改用 PreparedStatement 参数化查询,避免字符串拼接
+
+
+
+ +
+
+
+
+ + Linter + pmd:CloseResource + 资源未关闭:Statement/ResultSet 未在 finally 中关闭 + L19 + +
+
+
原文: Ensure that resources like Statement and ResultSet are closed
+
使用 try-with-resources 自动关闭 Statement 和 ResultSet,或确保在 finally 块中关闭它们
+
+
+
+ +
+
+
+
+ + Linter + pmd:MethodNamingConventions + 方法命名不规范:FetchUserById 应以小写字母开头 + L37 +
+
+
原文: Method name 'FetchUserById' does not follow camelCase naming convention
+
将方法名改为 fetchUserById,遵循 Java camelCase 命名规范
+
+
+
+ +
+
+
+
+ + Linter + pmd:FieldNamingConventions + 字段命名:api_key 应使用大写常量命名 + L44 +
+
+
原文: Field name 'api_key' does not follow naming convention
+
对于 static final 常量,应将名称改为 API_KEY 并使用下划线分隔
+
+
+
+ +
+
+
+
+ + Linter + eslint:no-console + 生产代码不应保留 console 语句 + L57 +
+
+
原文: Unexpected console statement (no-console)
+
移除 console.log 调试语句,或使用专门的日志框架替代
+
+
+
+ +
+
+
+
+ + Linter + pmd:UnusedLocalVariable + 未使用的局部变量:unusedVar 已声明但未使用 + L56 +
+
+
原文: Avoid unused local variables such as 'unusedVar'
+
删除未使用的变量声明,或确认是否需要该变量
+
+
+
+
+ +
+
+ 自定义规则 · 2 个问题 +
+ +
+
+
+
+ + 自定义 + no-hardcoded-credentials + 禁止在代码中硬编码数据库密码和 API Key + L13 +
+
+
规则 no-hardcoded-credentials 触发: 禁止在代码中硬编码数据库密码和 API Key
+
+
+
+ +
+
+
+
+ + 自定义 + avoid-console-log + 生产代码不应使用 console.log 调试 + L57 +
+
+
规则 avoid-console-log 触发: 生产代码不应使用 console.log 调试
+
+
+
+
+ +
+
+ AI 审查建议 · 3 条 +
+ +
+
+
+
+ + AI + sql-injection + SQL注入风险:用户状态参数直接拼接 SQL 查询 + L18 +
+
+
getActiveUsers 方法中,status 参数直接拼接到 SQL 查询字符串中,攻击者可传入恶意值导致 SQL 注入。同时缺少对 Connection 为 null 的防御性检查。
+ 🎯 安全漏洞 +
使用 PreparedStatement 参数化查询;添加 Connection 非空校验;使用 try-with-resources 管理资源
+
- String query = "SELECT username FROM users WHERE status = '" + status + "'"; ++ String query = "SELECT username FROM users WHERE status = ?"; ++ PreparedStatement ps = conn.prepareStatement(query); ++ ps.setString(1, status); ++ ResultSet rs = ps.executeQuery();
+
+
+
+ +
+
+
+
+ + AI + hardcoded-secret + 硬编码密钥:数据库密码和 API Key 直接写在源码中 + L13 +
+
+
DB_PASS 和 api_key 都是敏感凭据,硬编码在类中会导致密钥泄露。任何有权访问代码的人(包括通过反编译 .class 文件)都可获取这些凭据。
+ 🎯 安全漏洞 +
使用环境变量或密钥管理服务(如 Vault)存储敏感信息,在运行时注入
+
- private static final String DB_PASS = "admin123"; ++ private static final String DB_PASS = System.getenv("DB_PASS");
+
+
+
+ +
+
+
+
+ + AI + unused-method + 未使用的公共方法:unusedMethod 疑似死代码 + L55 +
+
+
unusedMethod 方法仅在 main 中未被调用,且其内部存在 DEBUG 打印和未使用的局部变量,可能是开发阶段遗留的测试代码,建议确认后删除。
+ 🎯 代码设计 +
+
+
+
+ +
+ + + +
+ + + + diff --git a/docs/superpowers/specs/review-panel-ui.md b/docs/superpowers/specs/review-panel-ui.md new file mode 100644 index 0000000..1e694f8 --- /dev/null +++ b/docs/superpowers/specs/review-panel-ui.md @@ -0,0 +1,75 @@ +# 审查结果面板 UI 说明 + +## 顶部区域 +| 元素 | 描述 | +|------|------| +| 标题 | SVG 图标 + "代码审查报告" | +| 元信息 | 文件路径 · 语言 · 耗时(秒) | + +## 降级横幅(AI 失败时显示) +``` +⚠ AI 审查未完成,报告仅包含静态分析结果 +``` + +## 执行错误框 +列出 linter 未配置/未安装、适配器异常等信息。 + +## 统计卡片 +| 卡片 | 颜色 | 说明 | +|------|------|------| +| 总计问题 | 白色 | 所有问题数量之和 | +| 错误 | 红色 `#f48771` | error 级别数量 | +| 警告 | 黄色 `#d29922` | warning 级别数量 | +| 建议 | 蓝色 `#58a6ff` | info 级别数量 | + +## 三栏标签页 +| 标签 | 来源 | 说明 | +|------|------|------| +| `🔧 {linter名称}` | ESLint / PMD / SqlLint / Stylelint | 静态分析检测到的问题 | +| `📋 自定义规则` | `.code-review-rules.yaml` → AI 评估 | 团队自定义规则违规 | +| `🤖 AI 审查` | DeepSeek API | AI 深度审查发现的问题 | + +每个标签右侧有药丸计数(红/黄/蓝分别对应错误/警告/建议数量)。 + +## 问题条目 +每一条问题卡片包含: + +``` +┌──────────────────────────────────────────────────┐ +│▌ ● BADGE ruleId 消息文本... L行号 │ ← 点击展开详情 +│▌ 🔧 │ ← 修复按钮 +└──────────────────────────────────────────────────┘ +``` + +| 元素 | 说明 | +|------|------| +| 左侧色条 `▌` | 红色=错误, 黄色=警告, 蓝色=建议(5px 宽圆角条) | +| 彩色圆点 `●` | 同色圆点辅助标识 | +| Badge | `Linter`(灰色)/ `自定义`(紫色)/ `AI`(青色) | +| ruleId | 规则标识,如 `pmd:SQLInjection` | +| 消息 | 中文问题描述(单行省略) | +| 行号 | `L18` 格式,灰色背景 | +| 🔧 修复 | 仅 fixable 条目显示,点击后变 `⏳...` 并禁用 | + +## 展开详情(点击条目切换) +- **原文对照**:原始英文问题描述(linter 条目) +- **💡 修复建议**:蓝色背景框,给出具体修复方向 +- **diff 代码对比**:深色背景 + `del`(红色) / `ins`(绿色) 显示修改前后 +- **🎯 AI 分类标签**:如"安全漏洞"、"代码设计"等(仅 AI 条目) + +## 底部操作栏 +| 按钮 | 功能 | +|------|------| +| 🔄 重新审查 | 重新运行审查流程 | +| 📄 导出报告 | 复制到剪贴板 / 保存为 Markdown | +| ⚙️ 设置 | 打开 VS Code 设置(vscode-code-reviewer) | + +## 交互行为 +- 点击条目 ← 展开/收起详情 +- 点击条目空白区 ← 跳转到编辑器对应行 +- 点击 🔧 修复 ← 调 AI 生成修复并应用,完成后自动重新审查 +- 标签页切换 ← 三栏独立切换(保留展开状态 via `retainContextWhenHidden`) + +## 空状态 +- 某栏无问题:居中斜体提示,如"未发现任何问题"、"无 AI 审查建议" +- Linter 未启用:提示"请检查上方错误信息" diff --git a/docs/superpowers/specs/setup-panel-preview.html b/docs/superpowers/specs/setup-panel-preview.html new file mode 100644 index 0000000..6a24b0b --- /dev/null +++ b/docs/superpowers/specs/setup-panel-preview.html @@ -0,0 +1,577 @@ + + + + + +设置面板 - UI 预览 + + + + +
+ +
+ + + + + 代码审查 · 设置 +
+ + +
+
快速开始
+
+
+
+
配置 AI 模型及 API Key
+
+
+
+
+
+
启用自定义规则
+
+
+
+
+
+
保存并测试连接
+
快捷键 Ctrl+Shift+R
+
+
+
+ + +
+
审核引擎
+
+
+ +
+
共通规则
+
Linter 静态分析
+
+
+
+ +
+
自定义规则
+
团队编码规范
+
+
+
+ +
+
AI 审核
+
深度代码审查
+
+
+
+
+ + +
+
AI 模型配置
+
+
+ 模型提供商 + 已配置 +
+
+ +
+
+ + +
+
建议使用支持结构化输出的模型。
+
+
+ + +
+
API Key
+
+
+ API Key + 已配置 +
+
+ +
+
+ + +
+
Key 仅存储在本地 VS Code 安全存储中。
+
+
+ + +
+
输出语言
+
+ + +
+
+ + +
+
自定义规则
+
+
+ 规则列表 + 3 条启用 +
+ +
+ + no-console-in-production + +
+
+ + max-function-lines: 80 + +
+
+ + require-javadoc-public + +
+
+ + no-any-type + +
+ +
+
+ + +
+
+
+
+ + +
+ + +
+ + +
+
+ + + + \ No newline at end of file diff --git a/docs/superpowers/specs/setup-panel-ui.md b/docs/superpowers/specs/setup-panel-ui.md new file mode 100644 index 0000000..8ababa1 --- /dev/null +++ b/docs/superpowers/specs/setup-panel-ui.md @@ -0,0 +1,85 @@ +# 设置面板 UI 说明 + +## 内容区 + +### 1. 快速开始 + +三步引导卡片: + +| 步骤 | 描述 | 完成状态 | +|------|------|---------| +| ① | 配置 AI 模型及 API Key | 圆形序号变紫色(`done`) | +| ② | 启用自定义规则 | 同上 | +| ③ | 保存并测试连接,含快捷键提示 `Ctrl+Shift+R` | 同上 | + +`Ctrl+Shift+R` 为 `vscode-code-reviewer.review` 快捷键。 + +### 2. 审核引擎 + +三个模块标签: + +| 标签 | 圆点颜色 | 描述 | +|------|---------|------| +| 🔵 共通规则 | 蓝色 `#58a6ff` | Linter 静态分析 | +| 🟡 自定义规则 | 琥珀色 `#d29922` | 团队编码规范 | +| 🟢 AI 审核 | 绿色 `#3fb950` | 深度代码审查 | + +### 3. AI 模型配置 + +卡片式表单: + +| 字段 | 控件 | 说明 | +|------|------|------| +| 模型提供商 | 下拉选择 | OpenAI / Azure OpenAI / DeepSeek / 腾讯混元 / Ollama | +| 模型名称 | 下拉选择 | 按提供商动态切换可选模型列表 | +| 状态标签 | `已配置` / `未配置` | 右侧药丸标签(绿/灰) | + +底部提示:建议使用支持结构化输出的模型。 + +### 4. API Key + +卡片式表单: + +| 字段 | 控件 | 说明 | +|------|------|------| +| API Key | 密码输入框 | `sk-...` 格式,已输入显示为圆点掩码 | +| Base URL | 文本输入框 | 可选,按提供商自动填充默认地址 | +| 状态标签 | `已配置` / `未配置` | 右侧药丸标签 | + +底部提示:Key 仅存储在本地 VS Code 安全存储中。 + +### 5a. 输出语言 + +| 字段 | 控件 | 说明 | +|------|------|------| +| AI 审查结果输出语言 | 下拉选择 | 中文(简体)/ English / 日本語 | + +配置项 `ai.outputLanguage`,默认 `zh-CN`。 + +### 5b. 自定义规则 + +| 元素 | 说明 | +|------|------| +| 状态标签 | 显示已启用数,如 `3 条启用` | +| 规则行 | 开关滑块 + 规则名(等宽字体)+ 删除按钮 `×` | +| 添加行 | 文本框 + 紫色 `+ 添加` 按钮 | + +示例规则:`no-console-in-production`、`max-function-lines: 80`、`require-javadoc-public`、`no-any-type` + +## 底部操作栏 + +| 按钮 | 样式 | 功能 | +|------|------|------| +| `重置` | 次级按钮(暗色边框) | 恢复默认设置 | +| `保存并测试连接` | 主按钮(紫色 `#7c3aed`) | 保存配置 → 调用 API 测试 → 显示结果 | +| 测试中 | 按钮禁用 + spinner 动画 | — | +| 测试成功 | 按钮变为 `✓ 已连接` | Toast 绿色提示,5 秒消失 | +| 测试失败 | 按钮变为 `✗ 重试` | Toast 红色提示,5 秒消失 | + +## 交互行为 + +- 输入 API Key → 步骤①自动标记完成(圆形变紫色) +- 开启任意规则 → 步骤②自动标记完成 +- 步骤③取决于前两步完成 + 测试连接成功 +- 规则开关即时切换,可删除单条规则 +- 测试结果 Toast 5 秒自动消失 diff --git a/opencode.json b/opencode.json new file mode 100644 index 0000000..6376bc7 --- /dev/null +++ b/opencode.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://opencode.ai/config.json", + "instructions": ["AGENTS.md"] +}