# PRD:dsh-plugin-todo-scanner TODO代码扫描插件 > 在 DeepSeek-Harness 会话中提供项目代码 TODO/FIXME 标记扫描与管理能力,插件扫描本地项目目录,提取代码中的待办标记,生成结构化清单,支持侧边面板展示、筛选、状态管理、导出 Markdown 任务清单,帮助开发者不遗漏任何待办事项。 元信息 - 作者:自定义 - 版本:V1.0 - 状态:开发中 - 更新日期:2026-09-06 --- ## 1. 背景与痛点 ### 1.1 用户痛点 - 项目代码中散落大量 `// TODO`、`// FIXME`、`// HACK`、`// NOTE` 标记,时间一长就忘了,没人跟踪。 - 手动 grep 搜索 TODO 效率低,结果是纯文本,无法按文件、优先级、状态分类管理。 - Agent 读文件时经常漏掉 TODO 标记,无法主动提醒开发者还有未完成的工作。 - 多人协作项目中,TODO 标记没有统一格式,有的写 `// TODO: xxx`,有的写 `// todo xxx`,有的写 `# TODO`,扫描不完整。 - 无法区分哪些是自己的待办、哪些是别人留下的、哪些已经过期。 - 缺少将 TODO 清单导出为可执行任务列表(Markdown / 飞书任务)的能力。 - 前端项目 node_modules、构建产物目录里也有大量 TODO,扫描结果噪声大。 ### 1.2 目标用户 所有在 Harness 中处理代码项目的开发者、需要管理技术债务的团队、做代码审计的人员。 ### 1.3 插件定位 一句话定位:Harness 项目 TODO 雷达插件,**插件负责本地文件扫描与标记提取**;大模型负责对 TODO 内容做分类、优先级评估、生成处理建议;支持多语言注释格式识别、忽略目录配置、侧边面板管理、Markdown 导出。 --- ## 2. 范围界定 ### 2.1 ✅ V1.0 必须实现(P0) - [ ] 扫描指定目录下所有代码文件,提取 TODO / FIXME / HACK / NOTE / XXX / BUG 标记 - [ ] 支持多语言注释格式:`//`(JS/TS/Java/C++)、`#`(Python/Ruby/Shell)、`/* */`(块注释)、``(HTML/XML) - [ ] 标记解析:提取标记类型、标记后文本、所在文件路径、行号、代码上下文(前后各 2 行) - [ ] 可配置扫描根目录白名单,禁止扫描系统目录 - [ ] 可配置忽略目录(默认忽略 node_modules、dist、build、.git、.next、vendor、target) - [ ] 可配置忽略文件扩展名(默认忽略 .min.js、.map、.lock、二进制文件) - [ ] 标记去重:同一文件同一行同一内容不重复记录 - [ ] 侧边 UI 面板:展示 TODO 清单,支持按类型/文件筛选,点击跳转到对应文件行 - [ ] 提供扫描工具:`todo_scan()` 触发扫描,返回结构化结果 - [ ] 提供清单查询工具:`todo_list()` 获取当前 TODO 清单 - [ ] 提供状态管理:可将 TODO 标记为 待处理 / 处理中 / 已完成 / 已忽略 - [ ] 提供导出工具:`todo_export()` 导出为 Markdown 任务清单(带复选框) - [ ] 统计信息:各类型标记数量、各文件标记数量、未处理数量 - [ ] 插件卸载时清空扫描结果与状态,无残留 - [ ] 扫描结果按会话隔离,每个会话独立维护 ### 2.2 ⭕ V1.1 后续迭代(P1,本期不做) - [ ] 自动优先级评估:大模型根据 TODO 内容评估高/中/低优先级 - [ ] 自动分类:功能 / 重构 / 修复 / 优化 / 文档 - [ ] 负责人识别:解析 `// TODO @张三: xxx` 格式中的负责人 - [ ] 过期检测:结合 git blame 判断 TODO 存在时间,超过 N 天标记为"过期" - [ ] 增量扫描:仅扫描变更文件,不全量重扫 - [ ] 文件监听:文件保存时自动重新扫描该文件 - [ ] 导出到飞书多维表格 / GitHub Issues - [ ] TODO 趋势图:随时间变化的 TODO 数量趋势 - [ ] 代码修复建议:大模型针对每个 FIXME 给出修复方案 ### 2.3 ❌ 不在本版本做(明确边界) - ❌ 不做代码语义分析(仅做注释行文本匹配,不理解代码逻辑) - ❌ 不做自动修复 TODO(仅扫描与管理,不自动修改代码) - ❌ 不做 git 集成(V1.1 再考虑 git blame) - ❌ 不做跨项目聚合(仅扫描单个指定目录) --- ## 3. 功能详细需求 ### 3.1 用户触发方式 - 自然语言触发:`扫描这个项目的TODO`、`看看还有哪些待办`、`列出所有FIXME`、`导出TODO清单` - 工具调用触发:`todo_scan()`、`todo_list()`、`todo_update_status()`、`todo_export()` - 侧边 UI 面板:展示清单 + 扫描按钮 + 筛选器 + 导出按钮 - 事件自动触发:无(纯工具型,用户主动触发扫描) ### 3.2 全部功能列表 #### 功能1:多语言标记扫描 - 递归遍历指定目录下的所有文本文件 - 对每个文件逐行扫描,匹配标记正则: - `// TODO:`、`// TODO`、`//todo:`(大小写不敏感) - `# TODO:`、`# TODO` - `/* TODO: */`、`/* TODO */` - ``、`` - 支持的标记类型:TODO、FIXME、HACK、NOTE、XXX、BUG、OPTIMIZE、REVIEW - 标记类型可在配置中扩展 - 提取标记后的文本内容(如 `// TODO: 修复登录页样式` → 内容为"修复登录页样式") - 记录文件路径(相对路径)、行号、所在行完整代码 - 提取代码上下文:标记行前后各 2 行代码(用于大模型理解上下文) #### 功能2:忽略规则 - 目录忽略(默认):node_modules、dist、build、.git、.next、.nuxt、vendor、target、out、.cache、.idea、.vscode - 文件扩展名忽略(默认):.min.js、.min.css、.map、.lock、.png、.jpg、.jpeg、.gif、.svg、.ico、.woff、.woff2、.ttf、.eot、.pdf、.zip、.tar、.gz - 二进制文件自动检测跳过(非 UTF-8 文本文件) - 可在配置中扩展忽略目录和文件类型 - 支持 `.gitignore` 规则自动识别(如果项目根目录有 .gitignore,自动应用其规则) #### 功能3:标记去重与合并 - 同一文件同一行的同一标记只记录一次 - 块注释中跨行的 TODO 合并为一条 - 内容完全相同且在同一文件的多条标记合并,记录出现次数 #### 功能4:结构化数据模型 ``` TodoItem { id: 唯一ID type: TODO / FIXME / HACK / NOTE / XXX / BUG / OPTIMIZE / REVIEW content: 标记文本内容 filePath: 相对路径 lineNumber: 行号 codeLine: 所在行完整代码 context: 前后各2行代码(数组) language: 文件语言(根据扩展名推断) status: pending / in_progress / done / ignored createdAt: 扫描发现时间戳 updatedAt: 状态更新时间戳 } ``` #### 功能5:状态管理 - 每个 TODO 项可设置状态: - pending(待处理,默认) - in_progress(处理中) - done(已完成) - ignored(已忽略,不再显示在主清单中) - 状态变更记录到审计日志 - 可批量更新状态(如"全部标记为已完成") - 状态保存在内存,按会话隔离 #### 功能6:侧边 UI 面板 - 顶部统计卡片:总标记数、待处理数、处理中数、已完成数 - 标记类型分布:各类型数量条形图 - TODO 清单列表: - 每行显示:标记类型标签(彩色)、内容摘要、文件路径:行号、状态标签 - 点击展开查看代码上下文 - 点击文件路径可在 Harness 中打开该文件 - 状态下拉切换 - 筛选器:按标记类型筛选、按状态筛选、按文件筛选、按关键词搜索 - 排序:按文件路径排序 / 按标记类型排序 / 按行号排序 - 按钮:【重新扫描】【导出 Markdown】【全部标记已完成】【清空清单】 - 不污染主聊天流 #### 功能7:Markdown 导出 - 导出为 Markdown 任务清单,格式: ```markdown # 项目 TODO 清单 > 扫描时间:2026-09-06 10:30:00 > 扫描目录:/path/to/project > 总计:15 项(待处理 10 / 处理中 2 / 已完成 3) ## FIXME(3) - [ ] 修复登录页样式错位 — src/pages/login.tsx:42 - [ ] 修复内存泄漏 — src/utils/cache.ts:128 ## TODO(8) - [ ] 添加单元测试 — src/services/user.ts:56 - [x] 更新依赖版本 — package.json:12 ## 按文件分组 ### src/pages/login.tsx - [ ] FIXME: 修复登录页样式错位 — 第42行 ``` - 支持按标记类型分组导出 / 按文件分组导出两种格式 - 已完成项显示为 `[x]`,待处理显示为 `[ ]` - 导出结果输出到聊天流(代码块),同时提供复制按钮 #### 功能8:统计与报告 - 标记类型分布统计 - 文件分布统计(哪些文件 TODO 最多) - 语言分布统计(哪些语言的文件 TODO 最多) - 状态分布统计 - 可生成简短的扫描报告文本(供大模型解读) ### 3.3 配置项(对应 cordis.patch.yml config 段 / src/config.ts Config schema) | 配置key | 类型 | 默认值 | 说明 | |---|---|---|---| | enable | boolean | true | 插件总开关 | | scan.allowedRoots | string[] | [] | 允许扫描的根目录白名单,为空则允许当前工作目录 | | scan.ignoreDirs | string[] | [内置列表] | 忽略的目录名列表 | | scan.ignoreExtensions | string[] | [内置列表] | 忽略的文件扩展名列表 | | scan.useGitignore | boolean | true | 是否自动识别并应用 .gitignore 规则 | | scan.maxFileSize | number | 1048576 | 单文件最大扫描字节数(默认 1MB),超出跳过 | | scan.maxFiles | number | 5000 | 单次扫描最大文件数,超出提示范围过大 | | markers.types | string[] | ["TODO","FIXME","HACK","NOTE","XXX","BUG","OPTIMIZE","REVIEW"] | 要扫描的标记类型列表 | | markers.caseSensitive | boolean | false | 标记匹配是否大小写敏感 | | export.defaultFormat | string | "by_type" | 默认导出格式:by_type(按类型分组)/ by_file(按文件分组) | | ui.showIgnored | boolean | false | 侧边面板是否显示已忽略项 | ### 3.4 UI 表现 - 侧边面板:统计卡片 + 类型分布图 + 可筛选可排序的 TODO 清单 + 操作按钮 - 扫描进行中显示进度条(已扫描文件数 / 总文件数) - 扫描完成后在聊天流输出简短摘要:"扫描完成,共发现 15 个标记:FIXME 3、TODO 8、NOTE 4" - TODO 项的标记类型用彩色标签区分(TODO=蓝色、FIXME=红色、HACK=橙色、NOTE=灰色、BUG=深红) - 不修改模型主回答内容 --- ## 4. 状态与数据设计 ### 4.1 内存状态结构 ```typescript type MarkerType = "TODO" | "FIXME" | "HACK" | "NOTE" | "XXX" | "BUG" | "OPTIMIZE" | "REVIEW"; type TodoStatus = "pending" | "in_progress" | "done" | "ignored"; interface TodoItem { id: string; type: MarkerType; content: string; filePath: string; // 相对扫描根目录的路径 lineNumber: number; codeLine: string; context: string[]; // 前后各2行代码 language: string; // 根据扩展名推断,如 "typescript"、"python" status: TodoStatus; createdAt: number; updatedAt: number; } interface ScanStats { totalFiles: number; scannedFiles: number; skippedFiles: number; // 被忽略规则跳过的 totalMarkers: number; byType: Record; byFile: Record; byStatus: Record; scanDurationMs: number; } interface SessionState { items: TodoItem[]; scanRoot: string; // 上次扫描的根目录 lastScanTime: number; stats: ScanStats | null; scanInProgress: boolean; } ``` - 会话隔离:✅ 每个 session 独立扫描结果与状态 - 持久化:仅内存保存,插件卸载 / Harness 重启后清空 ### 4.2 生命周期行为 1. **插件加载 apply(ctx)** - 注册扫描与管理工具 - 注册侧边 UI 面板 - 初始化配置与忽略规则 2. **插件卸载 plugin:unload** - 取消所有进行中的扫描(设置取消标志) - 清空所有会话的扫描结果与状态 - 移除 UI 面板 - 卸载后无任何残留 ### 4.3 自动休眠逻辑 - 本插件为纯工具型,不监听会话事件,无需自动休眠 - 扫描过程中如果用户发起新的扫描请求,取消上一次扫描(避免重复 IO) - 扫描大目录时在后台线程执行,不阻塞 Harness 主流程 --- ## 5. 工具与事件清单 ### 5.1 注册给大模型调用的 Tool 列表 | tool名称 | 入参 | 返回 | 用途 | |---|---|---|---| | todo_scan | rootPath?:string, types?:MarkerType[] | {items:TodoItem[], stats:ScanStats} | 扫描指定目录(默认当前工作目录),提取所有标记 | | todo_list | filter?: {type?:MarkerType, status?:TodoStatus, filePath?:string, keyword?:string}, sortBy?:"file"\|"type"\|"line" | TodoItem[] | 获取当前 TODO 清单,支持筛选与排序 | | todo_get | id:string | TodoItem | 获取单条 TODO 详情(含代码上下文) | | todo_update_status | id:string, status:TodoStatus | {success:boolean, item:TodoItem} | 更新单条 TODO 的状态 | | todo_batch_update | ids:string[], status:TodoStatus | {success:boolean, updatedCount:number} | 批量更新 TODO 状态 | | todo_export | format?:"by_type"\|"by_file", includeDone?:boolean | {content:string} | 导出为 Markdown 任务清单 | | todo_stats | 无入参 | ScanStats | 获取扫描统计信息 | | todo_clear | 无入参 | {success:boolean} | 清空当前会话的所有扫描结果 | ### 5.2 监听 Harness 事件列表 | 事件名 | 用途 | |---|---| | `plugin:unload` | 资源清理,取消进行中的扫描,清空全部会话状态 | > 本插件为纯工具型,不监听 `user:message`、`turn:before/after`、`tool:before` 等会话事件。 --- ## 6. 安全约束 & 异常处理 ### 6.1 安全规则 - 扫描根目录必须在 `scan.allowedRoots` 白名单内(白名单为空时仅允许当前工作目录),禁止扫描系统目录(/etc、/usr、C:\Windows 等) - 扫描为**只读操作**,不修改任何文件内容 - 不执行任何 shell 命令,纯文件系统读取 - 单文件大小上限保护,防止读取超大文件导致内存溢出 - 总文件数上限保护,防止扫描超大目录导致 Harness 卡死 - 忽略规则默认排除 node_modules 等第三方代码目录,减少噪声与 IO - 扫描结果中的代码内容仅在内存与 UI 中展示,不自动外发 ### 6.2 异常场景处理 1. **扫描目录不存在 / 无权限**:返回友好错误提示,不崩溃 2. **文件读取失败(编码异常/权限不足)**:跳过该文件,记录到 skippedFiles 统计,不中断整体扫描 3. **扫描文件数超上限**:停止扫描,返回已扫描结果 + 提示"文件数超过上限,仅扫描前 N 个文件,建议缩小扫描范围" 4. **单文件超大**:跳过该文件,记录到 skippedFiles 5. **二进制文件**:自动检测并跳过 6. **正则匹配异常**:跳过该行,记录警告,继续扫描 7. **扫描被取消(用户发起新扫描)**:设置取消标志,当前文件处理完后停止,返回已扫描的部分结果 8. **标记内容为空(如 `// TODO` 后面没有文字)**:内容标记为"(无描述)",仍记录在清单中 --- ## 7. 手工测试用例 - [ ] 用例1:扫描一个含多种语言的测试项目,验证 TODO/FIXME/HACK/NOTE 都能被正确识别 - [ ] 用例2:验证 `// TODO:`、`// TODO`、`# TODO`、`/* TODO */`、`` 五种注释格式都能匹配 - [ ] 用例3:验证大小写不敏感匹配(`todo`、`Todo`、`TODO` 都能识别) - [ ] 用例4:验证 node_modules、dist 目录被自动忽略 - [ ] 用例5:验证 .min.js、.map、图片文件被自动忽略 - [ ] 用例6:验证 .gitignore 规则被自动应用 - [ ] 用例7:扫描超出 maxFiles 的目录,验证停止并提示 - [ ] 用例8:扫描超出 maxFileSize 的文件,验证跳过 - [ ] 用例9:更新 TODO 状态为已完成,验证清单中状态变化、导出时显示 [x] - [ ] 用例10:批量更新状态,验证全部更新成功 - [ ] 用例11:按类型筛选清单,验证只显示指定类型 - [ ] 用例12:按关键词搜索,验证内容匹配的项被筛选出来 - [ ] 用例13:导出 Markdown(按类型分组),验证格式正确、复选框状态正确 - [ ] 用例14:导出 Markdown(按文件分组),验证按文件路径分组 - [ ] 用例15:侧边面板显示统计卡片与清单,点击展开查看代码上下文 - [ ] 用例16:尝试扫描系统目录(如 C:\Windows),验证被白名单拒绝 - [ ] 用例17:插件卸载,验证扫描结果清空、侧边面板消失 --- ## 8. 风险与待解决问题 | 风险 | 缓解方案 | |---|---| | 扫描大项目 IO 耗时过长,阻塞 Harness | 后台异步扫描 + 文件数上限 + 单文件大小上限 + 可取消 | | 标记正则误匹配(如字符串中出现 "TODO" 但不是注释) | 优先匹配注释符号前缀;无法 100% 准确时允许用户标记为 ignored | | 不同语言注释格式多样,无法全覆盖 | 内置主流语言格式;可配置扩展标记类型;V1.1 考虑用 tree-sitter 做精确注释解析 | | 扫描结果包含敏感代码(如硬编码密钥) | 扫描为本地只读操作,结果不自动外发;导出时用户自行确认 | | 增量更新困难(文件修改后 TODO 行号变化) | V1.0 每次全量重扫;V1.1 实现增量扫描 | | Windows 路径与 Linux 路径差异 | 使用 path 模块统一处理,相对路径存储时统一用正斜杠 | --- ## 9. 交付物清单 - cordis.patch.yml(bundle 补丁,默认配置内联) - src/index.ts(插件装配入口:生命周期、HTTP 服务装配、runScan 仲裁) - src/config.ts(配置 schema Config) - src/tools.ts(8 个工具注册) - src/scanner.ts(扫描引擎核心,目录遍历与文件读取) - src/matcher.ts(标记匹配器,多语言注释正则) - src/ignore-rules.ts(忽略规则管理器,含 .gitignore 解析) - src/todo-store.ts(TODO 项状态管理与查询) - src/exporter.ts(Markdown 导出器) - src/stats.ts(统计计算器) - src/safety.ts(扫描范围安全校验) - src/server.ts(面板数据 HTTP 服务) - src/language-map.ts(文件扩展名到语言的映射表) - src/types.ts(类型定义) - src/client/(Web 面板:index.tsx / Panel.tsx / locales.ts / types.ts) - tests/(smoke 冒烟 + matcher/ignore-rules/safety 单测) - package.json / tsconfig.json / tsconfig.client.json - docs/PRD.md(本文档) - docs/api.md(接口文档) - README.md 用户文档(含支持的标记类型、忽略规则配置) - CHANGELOG.md - LICENSE