代码标签扫描与任务创建技能Skill skill-learn

这个技能是一个直接执行的工具,用于扫描代码库中的FIX、NOTE、TODO标签,通过交互式提示向用户展示扫描结果,并创建用户选择的结构化任务。它支持多种文件类型,包括Lua、LaTeX、Markdown、Python、Shell、YAML等,并可以创建修复任务、学习任务和TODO任务,适用于软件开发项目管理和自动化流程。关键词:代码扫描,任务创建,FIX标签,NOTE标签,TODO标签,交互式选择,软件开发,DevOps。

DevOps 0 次安装 0 次浏览 更新于 3/22/2026

名称: 技能学习 描述: 扫描代码库中的 FIX:/NOTE:/TODO: 标签并创建结构化任务,通过交互式选择。为 /learn 命令调用。 允许的工具: Bash, Grep, Read, Write, Edit, AskUserQuestion

学习技能(直接执行)

直接执行技能,用于扫描文件、交互式呈现发现并创建用户选择的任务。取代之前的基于委托的方法,采用同步执行和 AskUserQuestion 提示。

关键行为: 用户总是在任何任务创建之前看到标签扫描结果。用户通过交互式提示选择要创建的任务类型。

上下文参考

参考(不要急切加载):

  • 路径: @specs/TODO.md - 当前任务列表
  • 路径: @specs/state.json - 机器状态

执行

步骤 1: 解析参数

从命令输入提取路径:

# 从命令输入解析
paths="$ARGUMENTS"

# 如果未指定路径,默认项目根目录
if [ -z "$paths" ]; then
  paths="."
fi

注意: --dry-run 标志不再支持。交互式流程本质上是“先预览”——用户总是在任务创建前看到发现。

步骤 2: 生成会话 ID

生成用于跟踪的会话 ID:

session_id="sess_$(date +%s)_$(od -An -N3 -tx1 /dev/urandom | tr -d ' ')"

步骤 3: 执行标签提取

使用文件类型特定模式扫描标签。使用 Bash 和 grep 实现一致输出解析。

3.1: 提取 FIX: 标签

Lua 文件(Neovim 配置):

grep -rn --include="*.lua" "-- FIX:" $paths 2>/dev/null || true

LaTeX 文件:

grep -rn --include="*.tex" "% FIX:" $paths 2>/dev/null || true

Markdown 文件:

grep -rn --include="*.md" "<!-- FIX:" $paths 2>/dev/null || true

Python/Shell/YAML 文件:

grep -rn --include="*.py" --include="*.sh" --include="*.yaml" --include="*.yml" "# FIX:" $paths 2>/dev/null || true

3.2: 提取 NOTE: 标签

与上面相同模式,将 FIX: 替换为 NOTE:

3.3: 提取 TODO: 标签

与上面相同模式,将 FIX: 替换为 TODO:

3.4: 解析结果

对于每个 grep 匹配,提取:

  • 文件路径
  • 行号
  • 标签类型 (FIX, NOTE, TODO)
  • 标签内容(标签后的文本)

示例原始输出:

nvim/lua/plugins/telescope.lua:67:-- TODO: 为 git 工作树添加自定义选择器
docs/KEYMAPS.md:89:<!-- FIX: 使用新绑定更新键映射表 -->

分类为三个数组:

  • fix_tags[] - 所有 FIX: 标签
  • note_tags[] - 所有 NOTE: 标签
  • todo_tags[] - 所有 TODO: 标签

步骤 4: 显示标签摘要

在用户选择之前呈现发现:

## 标签扫描结果

**扫描的文件**: {paths}
**发现的标签**: {total_count}

### FIX: 标签 ({count})
- `{file}:{line}` - {content}
- ...

### NOTE: 标签 ({count})
- `{file}:{line}` - {content}
- ...

### TODO: 标签 ({count})
- `{file}:{line}` - {content}
- ...

步骤 5: 处理边缘情况

未发现标签

如果未发现标签:

## 未发现标签

扫描的文件在: {paths}
未检测到 FIX:、NOTE: 或 TODO: 标签。

无需创建。

优雅退出,无提示。

仅某些标签类型

仅显示存在的标签类型的任务类型选项:

  • 存在 FIX: 标签 -> 提供“修复任务”
  • 存在 NOTE: 标签 -> 提供“修复任务”和“学习任务”
  • 存在 TODO: 标签 -> 提供“TODO 任务”

步骤 6: 任务类型选择

如果发现标签,提示用户选择任务类型:

{
  "question": "应创建哪些任务类型?",
  "header": "任务类型",
  "multiSelect": true,
  "options": [
    {
      "label": "修复任务",
      "description": "将 {N} 个 FIX:/NOTE: 标签合并为单个任务"
    },
    {
      "label": "学习任务",
      "description": "从 {N} 个 NOTE: 标签更新上下文"
    },
    {
      "label": "TODO 任务",
      "description": "为 {N} 个 TODO: 项创建任务"
    }
  ]
}

重要: 仅包含标签类型存在的选项:

  • 仅当 FIX: 或 NOTE: 标签存在时包含“修复任务”
  • 仅当 NOTE: 标签存在时包含“学习任务”
  • 仅当 TODO: 标签存在时包含“TODO 任务”

如果用户未选择任何选项,优雅退出:

未选择任务类型。未创建任务。

步骤 7: 单个 TODO 选择

如果选择了“TODO 任务”且存在 TODO: 标签:

标准情况(<=20 个 TODO)

{
  "question": "选择要创建为任务的 TODO 项:",
  "header": "TODO 选择",
  "multiSelect": true,
  "options": [
    {
      "label": "{内容截断至 50 字符}",
      "description": "{file}:{line}"
    },
    ...
  ]
}

大量 TODO(>20)

在顶部添加“全选”选项:

{
  "question": "选择要创建为任务的 TODO 项:",
  "header": "TODO 选择(多项)",
  "multiSelect": true,
  "options": [
    {
      "label": "全选({N} 项)",
      "description": "为每个 TODO 标签创建任务"
    },
    {
      "label": "{内容截断至 50 字符}",
      "description": "{file}:{line}"
    },
    ...
  ]
}

如果选择“全选”,包含所有 TODO。否则,仅包含选定项。

步骤 7.5: TODO 项的主题分组

条件: 用户选择了“TODO 任务”且选择了多于 1 个 TODO 项

如果仅选择 1 个 TODO 项,跳至步骤 8(无分组益处)。

7.5.1: 提取主题指示器

对于每个选定 TODO 项,提取主题指示器:

关键术语: 从 TODO 内容提取重要单词(名词、动词)。忽略停用词(the, a, is, to, for 等)。

文件部分: 按文件路径前缀分组(例如,Logos/Layer1/Logos/Shared/)。

操作类型: 识别常见操作模式:

  • “添加/实现/创建” → 实现任务
  • “修复/处理/纠正” → 修复任务
  • “文档/更新文档” → 文档任务
  • “测试/验证” → 测试任务
  • “重构/优化” → 改进任务

示例提取:

TODO: "为工作树添加自定义选择器" 在 nvim/lua/plugins/telescope.lua:67
  → 关键术语: ["选择器", "工作树", "望远镜"]
  → 文件部分: "nvim/lua/plugins/"
  → 操作类型: "实现"

TODO: "为工作树添加预览窗口" 在 nvim/lua/plugins/telescope.lua:89
  → 关键术语: ["预览", "工作树", "望远镜"]
  → 文件部分: "nvim/lua/plugins/"
  → 操作类型: "实现"

TODO: "优化懒加载" 在 nvim/lua/config/lazy.lua:23
  → 关键术语: ["优化", "懒", "加载"]
  → 文件部分: "nvim/lua/config/"
  → 操作类型: "改进"

7.5.2: 按共享术语聚类 TODO

分组共享 2 个或更多重要术语 或共享 文件部分 + 操作类型 的 TODO。

聚类算法:

  1. 从第一个 TODO 开始作为初始组
  2. 对于每个剩余 TODO:
    • 如果与现有组共享 2+ 关键术语 → 添加到组
    • 如果与现有组共享文件部分 AND 操作类型 → 添加到组
    • 否则 → 开始新组
  3. 从组中最常见的共享术语生成主题标签

示例聚类:

组 1: "望远镜工作树"(共享: 工作树, 望远镜, nvim/lua/plugins/, 实现)
  - 为工作树添加自定义选择器
  - 为工作树添加预览窗口

组 2: "配置优化"(共享: nvim/lua/config/, 改进)
  - 优化懒加载

单项目组: 如果一个 TODO 未与其他项聚类,它成为自己的单项目组。

7.5.3: 存储分组主题

存储主题组以供步骤 7.5.4 使用:

topic_groups = [
  {
    label: "望远镜工作树",
    items: [
      {file: "nvim/lua/plugins/telescope.lua", line: 67, content: "为工作树添加自定义选择器"},
      {file: "nvim/lua/plugins/telescope.lua", line: 89, content: "为工作树添加预览窗口"}
    ],
    shared_terms: ["工作树", "望远镜"],
    action_type: "实现"
  },
  {
    label: "配置优化",
    items: [
      {file: "nvim/lua/config/lazy.lua", line: 23, content: "优化懒加载"}
    ],
    shared_terms: [],
    action_type: "改进"
  }
]

步骤 7.5.4: 主题组确认

条件: topic_groups 包含至少一个具有 2+ 项的组

如果所有组仅 1 项,跳至步骤 8(无分组益处)。

通过 AskUserQuestion 呈现主题组:

{
  "question": "如何将 TODO 项分组为任务?",
  "header": "TODO 主题分组",
  "multiSelect": false,
  "options": [
    {
      "label": "接受建议的主题组",
      "description": "创建 {N} 个分组任务: {group_summaries}"
    },
    {
      "label": "保持为单独任务",
      "description": "创建 {M} 个单独任务(每 TODO 项一个)"
    },
    {
      "label": "创建单个组合任务",
      "description": "创建 1 个任务包含所有 {M} 个 TODO 项"
    }
  ]
}

其中:

  • {N} = 主题组数量
  • {M} = 选定 TODO 项总数
  • {group_summaries} = 逗号分隔列表,如“S5 定理(2 项),效用优化(1 项)”

存储用户选择: grouping_mode = "分组" | "单独" | "组合"

步骤 8: 创建选定任务

对于每个选定任务类型,创建任务。重要: 当 NOTE: 标签存在且同时选定修复和学习任务时,首先创建学习任务,以便修复任务可以依赖于它。

8.1: 获取下一个任务编号

next_num=$(jq -r '.next_project_number' specs/state.json)

8.2: 依赖感知任务创建顺序

检查 NOTE: 依赖条件:

has_note_dependency = (NOTE: 标签存在) AND (用户同时选定“修复任务”和“学习任务”)

如果 has_note_dependency 为 TRUE:

  • 首先创建学习任务(步骤 8.2a)
  • 存储学习任务编号为 learn_it_task_num
  • 第二创建修复任务并带有依赖(步骤 8.2b)

如果 has_note_dependency 为 FALSE:

  • 首先创建修复任务(如果选定)
  • 第二创建学习任务(如果选定)
  • 无依赖关系

8.2a: 学习任务(当为依赖首先创建时)

条件: has_note_dependency 为 TRUE

{
  "title": "从 NOTE: 标签更新上下文文件",
  "description": "基于学习更新 {N} 个上下文文件:

{按目标上下文分组}",
  "language": "元",
  "effort": "1-2 小时"
}

存储任务编号: learn_it_task_num = next_num 递增: next_num = next_num + 1

8.2b: 修复任务(当 has_note_dependency 时带有依赖)

条件: 用户选定“修复任务”且 (FIX: 或 NOTE: 标签存在)

当 has_note_dependency 为 TRUE 时:

{
  "title": "修复来自 FIX:/NOTE: 标签的问题",
  "description": "处理来自嵌入标签的 {N} 个项:

{具有文件:行引用的项列表}

**重要**: 进行更改时,从源文件中移除 FIX: 和 NOTE: 标签。保留 TODO: 标签不变(它们创建单独任务)。",
  "language": "{源文件中主要语言}",
  "effort": "2-4 小时",
  "dependencies": [learn_it_task_num]
}

当 has_note_dependency 为 FALSE 时:

{
  "title": "修复来自 FIX:/NOTE: 标签的问题",
  "description": "处理来自嵌入标签的 {N} 个项:

{具有文件:行引用的项列表}

**重要**: 进行更改时,从源文件中移除 FIX: 和 NOTE: 标签。保留 TODO: 标签不变(它们创建单独任务)。",
  "language": "{源文件中主要语言}",
  "effort": "2-4 小时"
}

语言检测:

如果大多数标签来自 .lean 文件 -> "lean"
否则如果大多数来自 .tex 文件 -> "latex"
否则如果大多数来自 .opencode/ 文件 -> "元"
否则 -> "通用"

8.3: 学习任务(当无依赖创建时)

条件: 用户选定“学习任务”且 NOTE: 标签存在且 has_note_dependency 为 FALSE

{
  "title": "从 NOTE: 标签更新上下文文件",
  "description": "基于学习更新 {N} 个上下文文件:

{按目标上下文分组}",
  "language": "元",
  "effort": "1-2 小时"
}

8.4: Todo 任务(如果选定)

条件: 用户选定“TODO 任务”且用户选定特定 TODO 项

检查 grouping_mode(来自步骤 7.5.4,如果步骤 7.5.4 被跳过,默认“单独”):

8.4.1: 分组模式 (grouping_mode == “分组”)

对于 topic_groups 中的每个主题组:

{
  "title": "{topic_label}: {item_count} 个 TODO 项",
  "description": "处理与 {topic_label} 相关的 TODO 项:

{item_list}

---

共享上下文: {shared_terms_description}",
  "language": "{从组中主要文件类型检测}",
  "effort": "{缩放努力}"
}

其中:

  • {topic_label} = 生成标签(例如,“望远镜工作树”)
  • {item_count} = 组中项数
  • {item_list} = 格式化项列表:
    - [ ] {content} (`{file}:{line}`)
    - [ ] {content} (`{file}:{line}`)
    
  • {shared_terms_description} = 简要描述为何项分组(例如,“与望远镜工作树功能相关”)

努力缩放公式:

基础努力 = 1 小时
缩放努力 = 基础努力 + (30 分钟 * (item_count - 1))

示例:
  1 项 → 1 小时
  2 项 → 1.5 小时 (1h + 30min)
  3 项 → 2 小时 (1h + 60min)
  4 项 → 2.5 小时 (1h + 90min)
8.4.2: 组合模式 (grouping_mode == “组合”)

创建单个任务包含所有选定 TODO 项:

{
  "title": "处理 {item_count} 个 TODO 项",
  "description": "从扫描中组合的 TODO 项:

{all_items_list}

---

文件: {unique_files_list}",
  "language": "{从主要文件类型检测}",
  "effort": "{缩放努力}"
}

其中:

  • {item_count} = 选定 TODO 项总数
  • {all_items_list} = 格式化所有项列表带复选框
  • {unique_files_list} = 涉及唯一文件的逗号分隔列表

努力缩放: 与分组模式相同公式。

8.4.3: 单独模式 (grouping_mode == “单独” 或默认)

对于每个选定 TODO 项单独:

{
  "title": "{标签内容,截断至 60 字符}",
  "description": "{完整标签内容}

来源: {file}:{line}",
  "language": "{从文件类型检测}",
  "effort": "1 小时"
}

Todo 任务语言检测(所有模式):

.lua (nvim/) -> "neovim"
.tex  -> "latex"
.md   -> "markdown"
.py/.sh -> "通用"
.opencode/* -> "元"

步骤 9: 更新状态文件

对于每个创建的任务:

9.1: 更新 state.json

读取当前状态,添加新任务条目,递增 next_project_number:

# 从标题创建 slug
slug=$(echo "$title" | tr '[:upper:]' '[:lower:]' | tr ' ' '_' | tr -cd 'a-z0-9_' | cut -c1-50)

# 读取当前状态
current=$(cat specs/state.json)

# 使用 jq 添加任务(使用两步模式避免转义问题)
# 步骤 1: 将任务数据写入临时文件
# 步骤 2: 使用 jq 和 slurpfile

对于当 has_note_dependency 为 TRUE 时的修复任务,包括依赖数组:

{
  "project_number": {N},
  "project_name": "{slug}",
  "status": "未开始",
  "language": "{language}",
  "dependencies": [learn_it_task_num]
}

对于所有其他任务,无需依赖字段。

9.2: 更新 TODO.md

## 任务 部分前置新任务条目(新任务在顶部):

标准格式(无依赖):

### {N}. {标题}
- **努力**: {估计}
- **状态**: [未开始]
- **语言**: {语言}
- **开始**: {时间戳}

**描述**: {描述}

---

当 has_note_dependency 为 TRUE 时的修复任务格式:

### {N}. {标题}
- **努力**: {估计}
- **状态**: [未开始]
- **语言**: {语言}
- **依赖**: {learn_it_task_num}
- **开始**: {时间戳}

**描述**: {描述}

---

步骤 10: 显示结果

显示创建的任务摘要:

## 从标签创建的任务

**处理的标签**: {N} 跨扫描文件

### 创建的任务

| # | 类型 | 标题 | 语言 |
|---|------|-------|----------|
| {N} | 修复 | 修复来自 FIX:/NOTE: 标签的问题 | {lang} |
| {N+1} | 学习 | 从 NOTE: 标签更新上下文文件 | 元 |
| {N+2} | todo | {title} | {lang} |

---

**后续步骤**:
1. 在 TODO.md 中审阅任务
2. 运行 `/research {first_task}` 开始
3. 通过 /research -> /plan -> /implement 循环推进

步骤 11: Git 提交(后处理)

如果创建了任务,提交更改:

task_count={创建的任务数}
git add specs/TODO.md specs/state.json
git commit -m "learn: 从标签创建 $task_count 个任务

会话: $session_id

共同作者: Claude Opus 4.5 <noreply@anthropic.com>"

错误处理

路径访问错误

当路径不存在或无法访问时:

  1. 为每个无效路径记录警告
  2. 继续处理有效路径
  3. 如果无有效路径剩余,报告并退出

未发现标签

这不是错误条件:

  • 信息性报告
  • 退出无提示

state.json 更新失败

如果 jq 失败:

  1. 记录错误带命令和输出
  2. 尝试两步 jq 模式
  3. 如果仍然失败,报告部分成功(发现标签但未创建任务)

TODO.md 解析错误

如果 TODO.md 格式损坏:

  1. 记录错误
  2. 跳过 TODO.md 更新
  3. state.json 更新可能仍成功
  4. 报告部分成功

Git 提交失败

非阻塞:

  1. 记录失败
  2. 任务仍成功创建
  3. 报告提交失败但任务存在