Compare commits

...
10 Commits
95 changed files with 1464 additions and 2385 deletions
+3
View File
@@ -0,0 +1,3 @@
# Local CraftKit data
/local/
/cache/
+13
View File
@@ -0,0 +1,13 @@
# CraftKit 项目资料
本目录保存当前项目可供 Agent 使用的配置、规范、知识和交接内容。
- `project.json`:项目类型、技术栈、代码边界、依赖标识和命令。
- `agents/`:项目对 Agent 的补充指令。
- `standards/`:项目自身的开发、测试、文档与 Git 规范。
- `knowledge/`:经过验证的技术决策和可复用经验。
- `handoff/`:用户明确选择共享的任务交接。
- `local/`:当前工作副本的本地上下文,不进入 Git。
- `cache/`:可重新生成的缓存,不进入 Git。
共享资料不得包含凭据、个人机器绝对路径或无必要的业务数据。
+3
View File
@@ -0,0 +1,3 @@
# Agent 补充说明索引
当前没有项目专属 Agent 补充说明。新增说明时记录适用目录、触发条件和文件路径。
+7
View File
@@ -0,0 +1,7 @@
# 项目知识索引
当前没有已验证的项目知识。只收录有证据、适用范围明确且能够复用的技术决策与经验。
- 问题经验按需建立在 `pitfalls/`,不预建空分类。
- 重要技术决策按需建立在 `decisions/`。
- 其他长期知识应优先并入已有主题,避免同义平行文档。
+60
View File
@@ -0,0 +1,60 @@
{
"schemaVersion": 1,
"initialization": {
"mode": "existing",
"references": []
},
"project": {
"name": "CraftKit",
"description": "面向 Codex 插件市场的通用插件工具集,提供可独立安装和维护的 Skill。",
"type": "codex-plugin-collection"
},
"technology": {
"languages": [
"Markdown",
"JSON",
"YAML",
"Python"
],
"frameworks": [],
"buildTools": [],
"databases": []
},
"code": {
"sourceRoots": [
"plugins"
],
"packageRoots": [],
"modules": [
"plugins/dev",
"plugins/doc",
"plugins/git",
"plugins/knowledge",
"plugins/skill"
]
},
"dependencies": {
"internal": [],
"public": []
},
"frontend": {
"framework": "",
"componentLibraries": [],
"componentRoots": [],
"documentation": []
},
"commands": {
"build": [],
"test": [],
"check": []
},
"documents": {
"archiveRoot": "docs/archive",
"archiveRules": []
},
"guidance": {
"agentIndex": ".craftkit/agents/index.md",
"standardsIndex": ".craftkit/standards/index.md",
"knowledgeIndex": ".craftkit/knowledge/index.md"
}
}
+3
View File
@@ -0,0 +1,3 @@
# 项目规范索引
当前没有项目专属规范。新增规范时记录主题、适用范围、规则文件和优先级;未覆盖主题可由 `guidance` 查询中性公共基线。
+46 -121
View File
@@ -4,7 +4,14 @@
## 项目目标 ## 项目目标
CraftKit 面向 Codex 插件市场,提供简洁、中性、可独立安装的 Skill。所有能力必须能够脱离来源项目独立理解、验证和维护。 CraftKit 面向 Codex 插件市场,提供简洁、中性、可独立安装和维护的 Skill。每项能力都应具备清晰边界,并能够独立理解与验证。
## 项目资料
- 结构化项目配置:`.craftkit/project.json`
- Agent 补充说明:`.craftkit/agents/index.md`
- 项目规范索引:`.craftkit/standards/index.md`
- 项目知识索引:`.craftkit/knowledge/index.md`
## 沟通与改动 ## 沟通与改动
@@ -23,8 +30,7 @@ CraftKit 面向 Codex 插件市场,提供简洁、中性、可独立安装的
| `plugins/doc/` | 文档转换、整理与写作 Skill | | `plugins/doc/` | 文档转换、整理与写作 Skill |
| `plugins/git/` | Git 操作与交付 Skill | | `plugins/git/` | Git 操作与交付 Skill |
| `plugins/knowledge/` | 交接、复盘与知识维护 Skill | | `plugins/knowledge/` | 交接、复盘与知识维护 Skill |
| `plugins/skill/` | Skill 创建、迁移、检查与同步工具 | | `plugins/skill/` | Skill 与项目规范辅助工具 |
| `migration/` | 来源指纹、迁移计划和状态追踪,不进入插件发布内容 |
## Skill 设计规则 ## Skill 设计规则
@@ -44,143 +50,51 @@ plugins/<plugin>/skills/<skill>/
├─ SKILL.md ├─ SKILL.md
├─ agents/openai.yaml # 有 UI 元数据时使用 ├─ agents/openai.yaml # 有 UI 元数据时使用
├─ scripts/ # 有确定性自动化时使用 ├─ scripts/ # 有确定性自动化时使用
├─ references/ # 有条件加载的详细资料时使用 ├─ references/ # 有条件加载的详细资料
└─ assets/ # 生成产物需要的素材 └─ assets/ # 生成产物需要的素材
``` ```
## 维护原则
- 以当前 Codex 能力、项目实际情况、公开标准和用户需求为依据设计 Skill。
- 技术规则应注明适用版本;依赖外部资料时记录资料入口和验证日期。
- 新增内容应具备明确用途,避免重复能力、过期说明和仅用于记录开发过程的文档。
## 新增与修改流程
1. 阅读目标插件、相邻 Skill、适用的 `AGENTS.md` 和当前 Git 状态,确认职责边界与已有改动。
2. 明确用户目标、输入、输出、触发条件、副作用、权限和不包含事项。
3. 检查是否应复用或扩展已有 Skill,避免名称不同但职责重复的实现。
4. 修改前向用户说明目标文件、实现方式、风险和验证方法;高风险或范围不明确时等待确认。
5. 按最小必要结构实现,优先使用项目证据和官方资料,不猜测接口、版本或运行行为。
6. 完成结构、引用、插件清单及必要行为验证,并分别记录通过项和未验证边界。
7. 展示实际变更、验证结果、剩余风险和建议提交信息;只有用户明确授权后才执行本地提交。
## CraftKit 项目目录 ## CraftKit 项目目录
- `.craftkit/` 是项目级 Agent 配置、规范、知识和运行状态的统一目录,目录名固定为全小写;不能把整个目录视为本地缓存或整体排除。 - `.craftkit/` 是项目级 Agent 配置、规范、知识和运行状态的统一目录,目录名固定为全小写;不能把整个目录视为本地缓存或整体排除。
- 可共享内容包括 `project.json`、`agents/`、`standards/`、`knowledge/` 和 `handoff/`,可以根据用户确认正常提交和同步。 - 可共享内容包括 `project.json`、`agents/`、`standards/`、`knowledge/` 和 `handoff/`,可以根据用户确认正常提交和同步。
- `project.json` 记录项目类型、技术栈、源码与包边界、内部依赖标识、常用命令和规范入口;不得记录凭据、完整连接信息或个人机器绝对路径。 - `project.json` 记录项目类型、技术栈、源码与包边界、内部依赖标识、常用命令和规范入口;不得记录凭据、完整连接信息或个人机器绝对路径。
- 项目初始化区分成熟项目与空项目:成熟项目优先延续当前项目已有且有充分证据的风格;空项目根据需求、用户选择和已授权的参考项目建立最小上下文,不自行猜测框架、包名或内部依赖。 - 成熟项目优先延续已有且有充分证据的风格;空项目根据需求、用户选择和已授权参考建立最小上下文,不自行猜测框架、包名或内部依赖。
- 引用其他项目时先确认参考范围,只提炼结构、依赖、命名、测试、规范或工具约定;共享配置优先记录相对路径,不能共享的本地位置放入 `local/`。 - 引用其他项目时先确认参考范围,只提炼结构、依赖、命名、测试、规范或工具约定;共享配置优先记录相对路径,不能共享的本地位置放入 `local/`。
- 公开框架版本差异由 CraftKit 公共 profile 基于官方资料独立维护;项目只在 `project.json` 选择实际版本和 profile,并在 `standards/` 保存内部框架与覆盖规则。普通任务不得跨 profile 混读,升级或版本比较除外。 - 公共框架版本差异由 CraftKit 公共 profile 维护;项目在 `project.json` 选择实际版本和 profile,并在 `standards/` 保存项目补充规则。
- `agents/` 保存项目对特定 Agent 的补充说明;`standards/` 保存项目开发规范;`knowledge/pitfalls/` 保存经验证且可复用的问题经验;`knowledge/decisions/` 保存重要技术决策。 - `agents/` 保存项目对特定 Agent 的补充说明;`standards/` 保存项目开发规范;`knowledge/pitfalls/` 保存经验证且可复用的问题经验;`knowledge/decisions/` 保存重要技术决策。
- 本地内容统一放入 `local/`,缓存统一放入 `cache/`。`.craftkit/.gitignore` 必须排除 `/local/` 和 `/cache/`,但不得排除整个 `.craftkit/`。 - 本地内容统一放入 `local/`,缓存统一放入 `cache/`。`.craftkit/.gitignore` 必须排除 `/local/` 和 `/cache/`,但不得排除整个 `.craftkit/`。
- 本地交接默认位于 `.craftkit/local/handoff/current.md`;只有用户明确选择共享时才写入 `.craftkit/handoff/current.md`。 - 本地交接默认位于 `.craftkit/local/handoff/current.md`;只有用户明确选择共享时才写入 `.craftkit/handoff/current.md`。
- 只在实际需要时创建目录,不一次生成空的 `agents/`、`standards/`、`knowledge/`、`handoff/`、`local/` 或 `cache/`。 - 只在实际需要时创建目录,不一次生成空的 `agents/`、`standards/`、`knowledge/`、`handoff/`、`local/` 或 `cache/`。
- Git Skill 默认排除 `.craftkit/local/**` 和 `.craftkit/cache/**`;其他 `.craftkit` 内容按普通项目资产评估,并在提交建议中单独标识为 Agent、规范或知识变更。 - Git Skill 默认排除 `.craftkit/local/**` 和 `.craftkit/cache/**`;其他 `.craftkit` 内容按普通项目资产评估,并在提交建议中单独标识。
- Git 交付或变更导出默认排除环境配置、本地配置和机器配置。只有适用的 `AGENTS.md`、`.craftkit/agents/` 或 `.craftkit/standards/` 明确要求交付,并经用户查看预览后再次确认,才可纳入;私钥、真实密钥和检测到的凭据始终禁止导出。
- 如果本地目录已经被 Git 跟踪,或整个 `.craftkit/` 被全局规则、`.git/info/exclude` 或项目规则忽略,应提示冲突并停止自动处理;不得自动修改索引或历史。 - 如果本地目录已经被 Git 跟踪,或整个 `.craftkit/` 被全局规则、`.git/info/exclude` 或项目规则忽略,应提示冲突并停止自动处理;不得自动修改索引或历史。
- `.craftkit/` 不得保存凭据、令牌、私钥、个人机器绝对路径或无必要的个人信息。 - `.craftkit/` 不得保存凭据、令牌、私钥、个人机器绝对路径或无必要的个人信息。
## 迁移与知识产权边界
- 迁移采用“分析能力 → 编写中性规格 → 脱离原文独立实现”的方式,不做目录复制、批量替词或近义改写。
- 不复制来源 Skill 的正文、脚本、提示词、模板、示例、规则库、注释或独特文档结构。
- 不写入公司名称、产品名称、内部域名、内部包名、项目名、人员信息、业务规则或环境路径。
- 公司框架 API、组件契约、审批流程、版本矩阵和内部消息格式不得直接迁移;评估时应先抽象其通用问题,并优先设计基于项目配置、公开标准或用户显式输入的中性平替。只有不存在独立通用价值、无法安全替代或与 Codex 场景不成立时才排除。
- 通用技术规范必须根据公开标准或官方资料重新设计;需要引用时记录来源和许可证。
- 来源文件只用于人工分析和哈希追踪,不得进入 `plugins/` 发布目录。
- 上游变化只触发复核,不自动覆盖已迁移 Skill。
详细顺序和门禁见 [迁移计划](migration/MIGRATION_PLAN.md)。
## Skill 迁移工作流
所有新增迁移、来源更新和已迁移 Skill 的重新评估,都必须按以下顺序处理。用户未明确确认前,只能进行只读分析,不得创建、复制、改写或删除目标 Skill 文件。
### 批次策略
- 迁移前期采用“特殊样本”方式,不以固定数量为目标,而是优先覆盖不同结构、依赖、权限和风险类型。
- 特殊样本至少覆盖纯指令、脚本与生成物、只读 Git、有副作用 Git、模板化文档、大型参考资料和复合工作流;缺少某一类型时不得宣告样本阶段完成。
- 每个特殊样本原则上单独完成迁移前确认、实现验证和提交确认,以便及时修正规则。
- 特殊样本全部通过后,应先总结可复用的命名、目录、改写、验证和状态同步规则,再进入批量阶段。
- 批量阶段按同一插件、相近能力和相同风险类型组织,每批建议 6~12 个 Skill;高度同质时可整组处理,但不得跨越不同权限边界强行合批。
- 批量迁移仍执行完整的两次确认。任何样本失败或出现新类型,都应暂停扩批并补充对应特殊样本。
### 连续迁移波次
当用户明确要求将一组已完整列明的剩余来源“按顺序一次迁移完,再统一确认提交”时,可以建立连续迁移波次:
- 迁移前必须一次性展示全部来源、目标映射、合并或排除关系、执行顺序、写入范围和总体验收门禁,并取得用户明确确认。
- 确认后可以按既定阶段连续实现,不再逐阶段请求迁移确认;不得加入方案外来源、目标 Skill 或新权限。
- 每个阶段仍须独立运行对应测试和敏感内容扫描。阶段失败时先在既定范围内修复;出现需扩大范围、改变目标或无法安全替代的情况时暂停并重新确认。
- 波次执行期间不创建 Git 提交。全部阶段完成后统一展示来源状态、目标文件、测试证据、未验证边界和建议提交序列,等待一次最终确认。
- 最终一次“确认提交”可以授权按已展示顺序创建多个逻辑清晰的本地提交;不得因此推送、发布、创建标签或修改远端历史。
### 1. 阅读迁移计划和现状
- 完整阅读 `migration/MIGRATION_PLAN.md`、`migration/README.md` 和 `migration/source-lock.json`。
- 检查 Git 状态、目标插件目录和现有 Skill,避免覆盖未提交改动或重复实现。
- 只读检查相关来源 Skill 及其辅助资源,确认来源提交、文件哈希、依赖关系和变化范围。
- 区分首次迁移、上游更新、目标重构和排除项复核,不把不同类型混为一次迁移。
### 2. 列出来源 Skill 和作用
在任何写入前,向用户列出本批计划处理的来源 Skill:
| 项目 | 内容 |
| --- | --- |
| 来源标识 | 使用本地迁移标识,不在发布目录写入公司品牌 |
| 来源 Skill | 原始名称和相对路径 |
| 当前作用 | 根据源码确认的能力、输入、输出和关键边界 |
| 迁移类型 | 新增、更新、合并、中性平替、公开资料重建或排除 |
| 风险 | 公司专属知识、内部依赖、重复能力和兼容性问题 |
来源作用必须有文件依据;无法从静态内容确认的运行行为应明确标记为未验证。
同一目标能力存在多个来源时,应在一个迁移方案中共同评估,明确合并、取舍、中性平替或排除关系,不按来源机械创建多个目标 Skill。来源含有大量专有规则时,不得只罗列删除项;必须说明通用问题如何由配置、标准接口、用户输入或项目资料替代。
### 3. 给出目标名称、作用和组织结构
针对每个来源 Skill,先给出建议方案:
| 项目 | 内容 |
| --- | --- |
| 目标插件 | `dev`、`doc`、`git`、`knowledge` 或 `skill` |
| 目标名称 | 简短、中性、动作或明确领域导向的名称 |
| 目标作用 | 迁移后保留的通用能力和明确排除的内容 |
| 组织结构 | `SKILL.md` 及确有需要的 `agents/`、`scripts/`、`references/`、`assets/` |
| 合并关系 | 独立 Skill、合并到已有 Skill、替代旧 Skill 或排除 |
| 验证方案 | 静态校验、脚本测试、真实请求和敏感内容扫描 |
此阶段只输出设计,不创建目标目录。若多个来源能力可以合并,应优先给出合并方案,避免一一照搬来源结构。
### 4. 与用户确认迁移方案
- 展示本批范围、目标映射、组织结构、排除内容和验证方式后,明确请求用户确认。
- 用户可以确认全部方案,也可以调整名称、拆分或合并方式、迁移顺序和排除项。
- 只有“确认执行”“按此方案迁移”等明确授权才允许进入写入阶段。
- 用户仅要求评估、查看方案或继续讨论时,不视为迁移授权。
- 确认后的授权只覆盖已展示范围;新增 Skill、扩大来源范围或改变组织方式必须重新确认。
### 5. 执行迁移并更新文档
获得确认后:
1. 按中性能力规格独立实现目标 Skill,不复制来源目录或正文。
2. 运行 `AGENTS.md` 中规定的全部验证,并记录真实结果和未验证边界。
3. 更新 `migration/source-lock.json` 的来源哈希、目标路径、状态和复核日期。
4. 更新 `migration/MIGRATION_PLAN.md` 的执行清单和批次进度。
5. 更新根 `README.md`、所属插件说明或其他确实受影响的文档;不得创建重复状态文档。
6. 向用户报告新增、更新、合并和排除结果,以及测试、扫描和剩余风险。
7. 展示本批待提交文件清单和建议的 Conventional Commit 提交信息,进入第二次确认。
### 6. 与用户确认并提交到本地
- 迁移前确认只授权执行迁移,不自动授权 Git 提交。
- 迁移和文档同步完成后,必须再次等待用户明确确认;“确认提交”“提交到本地”等表述才视为提交授权。
- 用户要求调整、补测或继续检查时,先完成相应工作并重新展示结果,不得沿用此前的提交确认。
- 获得确认后,只暂存本批迁移方案中已展示且验证通过的文件,不使用会混入其他改动的宽泛暂存命令。
- 暂存后执行 `git diff --cached --check`,并复核暂存文件清单;发现额外文件或敏感内容时停止提交并报告。
- 使用中文 Conventional Commit 信息创建本地提交,然后检查提交内容和工作区状态。
- 本地提交成功后向用户报告提交哈希、提交信息、文件范围和剩余未提交改动。
- 第二次确认只授权本地提交,不包含推送、创建标签、发布插件或修改远端历史;这些操作必须另行获得明确授权。
- 连续迁移波次按“连续迁移波次”约定执行一次最终确认;若计划创建多个本地提交,必须在确认前展示每个提交的范围和建议信息。
## 验证要求 ## 验证要求
每个新增或修改的 Skill 至少完成: 每个新增或修改的 Skill 至少完成:
1. 检查目录名、frontmatter 和引用路径。 1. 检查目录名、frontmatter、引用路径和工具名称。
2. 运行 Skill 校验器。 2. 运行仓库现有的 Skill 校验器及所属插件校验器。
3. 运行所属插件 manifest 校验器。 3. 检查凭据、无效路径、未替换占位符和无效工具名称。
4. 执行公司标识、内部路径、凭据和占位符扫描。 4. 有脚本时运行核心行为测试;有生成物时检查实际产物。
5. 有脚本时运行核心行为测试;有生成物时检查实际产物。 5. 以真实请求检查触发边界、权限边界和输出是否符合描述。
6. 用一个真实请求验证触发边界和输出结果。
7. 更新 `migration/source-lock.json` 中的状态、目标路径和复核日期。
静态检查通过不代表真实行为已经验证,交付时应分别说明静态校验、脚本测试和实际场景验证结果。 静态检查通过不代表真实行为已经验证,交付时应分别说明静态校验、脚本测试和实际场景验证结果。
@@ -189,5 +103,16 @@ plugins/<plugin>/skills/<skill>/
- 使用 Conventional Commits,提交说明使用中文。 - 使用 Conventional Commits,提交说明使用中文。
- 推荐类型:`feat`、`fix`、`docs`、`refactor`、`test`、`chore`。 - 推荐类型:`feat`、`fix`、`docs`、`refactor`、`test`、`chore`。
- 提交前执行 `git status --short`、`git diff --cached --check` 并检查暂存文件清单。 - 提交前执行 `git status --short`、`git diff --cached --check` 并检查暂存文件清单。
- 不提交本地来源配置、凭据、缓存、运行产物和 IDE 私有状态。 - 不提交本地配置、凭据、缓存、运行产物和 IDE 私有状态。
- 不使用破坏性历史改写,也不推送,除非用户明确授权。 - 不使用破坏性历史改写,也不推送,除非用户明确授权。
## 分支与发布约定
- 一个完整主题使用一个短生命周期开发分支;分支只包含同一能力闭环,不为凑版本混入无关变更。
- 分支命名优先遵循仓库现有约定;没有更具体规则时使用 `feature/<short-name>`、`fix/<short-name>`、`docs/<short-name>` 或 `chore/<short-name>`。
- 工作区不干净、当前 checkout 被 IDE/服务/测试占用,或用户要求不影响主工作区时,使用独立 Worktree。分支任务完成后必须通过安全清理门禁移除额外 Worktree,保留分支和提交。
- 已验证的开发分支合入 `master` 后,先在 `CHANGELOG.md` 的 `Unreleased` 累计;形成一组相关、完整、可交付的能力后再发布,不因每次普通修改单独发版。
- 阻断性缺陷、安全问题或已发布能力的明确回归可以单独发布修订版本;不得为等待批次延迟必要修复。
- 仓库整体版本遵循 SemVer:不兼容变化升级主版本,向后兼容的新能力升级次版本,向后兼容的问题修复升级修订版本。
- `dev`、`doc`、`git`、`knowledge` 和 `skill` 插件清单版本按实际变化独立维护;修改某个插件的可见能力时同步评估其 `plugin.json` 版本。
- 发布前必须确认工作区、发布范围、版本号、验证证据、`CHANGELOG.md`、插件清单版本、标签目标和远程地址。版本提交、标签和推送分别按用户授权执行。
+97
View File
@@ -0,0 +1,97 @@
# 更新日志
本文档记录 CraftKit 对使用者有明显影响的功能、工作流和兼容性变化。版本号遵循 [Semantic Versioning 2.0.0](https://semver.org/lang/zh-CN/):主版本表示不兼容变化,次版本表示向后兼容的新能力,修订版本表示向后兼容的问题修复。
历史版本根据 Git 提交倒推,只保留重要里程碑,不逐条复述全部提交。各版本对应的 Git 历史通过 `v<版本号>` 标签标记。
这里的版本号表示 CraftKit 仓库整体发布版本。`dev`、`doc`、`git`、`knowledge` 和 `skill` 的插件清单版本仍按各插件实际变化独立维护,不要求与仓库版本保持一致。
## [Unreleased]
### Planned
- 建设 Skill 评测基线,支持固定场景、验收规则和更新前后回归比较。
## [1.3.0] - 2026-08-31
### Added
- 增加 `.craftkit/project.json`、项目资料索引和本地内容忽略规则,为 Agent 提供可共享的项目上下文入口。
- 为 `branch` 增加当前工作区与独立 Worktree 两种分支创建方式,以及任务结束后的 `close` 清理模式。
- 为 `integrate` 增加预集成 Worktree 的 `cleanup` 阶段和完整生命周期状态。
- 增加 Worktree 分支占用诊断、安全移除门禁和清理后分支、提交及主工作区验证规则。
### Changed
- 脏工作区、当前 checkout 被占用或用户要求隔离时,优先在独立 Worktree 创建需求或预集成分支。
- 预集成交付范围统一以目标分支到预集成 HEAD 的真实差异判断,不再以 merge 输出、分支名、提交数量或测试结果代替纯度检查。
- 全流程自动化在独立 Worktree 的分支任务完成后进入清理门禁,释放分支占用后再结束任务。
- 增加短生命周期开发分支和批量发布约定:相关完整变更累计发布,阻断性修复可单独发布修订版本。
- 所有 Skill 的界面显示名增加完整模块调用名,例如 `代码审查(dev:review-code)`,便于在插件列表中识别并直接调用。
## [1.2.0] - 2026-08-31
### Added
- 增加项目初始化到应用拆分的共同工作流,明确应用边界、拆分判据、审批门和交付基线。
- 分别增加新项目与已有项目两种执行口径。
- 新项目流程覆盖项目上下文、需求基线、业务能力地图、应用拆分、契约设计和建设计划。
- 已有项目流程覆盖现状建模、耦合识别、目标与过渡架构、数据权威、渐进迁移和回滚。
### Changed
- 明确应用拆分不等于强制微服务化,允许模块化单体或暂不拆分成为评审结论。
- README 增加项目初始化与应用拆分工作流入口。
## [1.1.1] - 2026-08-31
### Fixed
- 文档转换脚本改为按需加载 `python-docx` 和 `openpyxl`,缺少依赖时返回明确提示,不再影响帮助和参数探测。
- 缺少转换依赖时不创建输出目录或半成品。
- 为后端设计、后端实现和前端实现补充 `guidance` 未安装时的规范检索回退路径,降低插件间隐式耦合。
## [1.1.0] - 2026-08-28
### Added
- 增加从需求归集、技术设计、代码实现、测试审核到本地提交的全流程自动化指南。
- 建立 S0 至 S9 阶段状态机、G1 至 G5 人工审批门、验证矩阵、回退规则和任务台账。
- 提供可直接交给 Codex 或其他兼容智能体的总控提示词。
## [1.0.0] - 2026-08-26
### Added
- 建立由 `dev`、`doc`、`git`、`knowledge` 和 `skill` 组成的完整 CraftKit 插件体系。
- 提供 44 个可独立发现的 Skill,覆盖软件设计与实现、文档处理、Git 交付、项目知识和规范维护。
- README 增加完整 Skill 目录、插件用途和本地使用说明。
### Changed
- 收敛插件元数据和能力描述,使 CraftKit 具备稳定、可理解的首个正式版本基线。
- 清理仅服务早期建设过程的资料,保留实际运行、维护和发布所需内容。
## [0.3.0] - 2026-08-26
### Added
- Skill 数量扩展到 44 个,形成首个完整能力集合。
- 补齐代码实现、审查、测试、升级、工作量评估和专项技术设计能力。
- 补齐版本发布、需求整理、报告和消息编写能力。
## [0.2.0] - 2026-08-25
### Added
- Skill 数量扩展到 25 个,形成可用的开发、文档、Git、知识和规范能力基础。
- 增加变更计划、后端设计和前端设计等开发分析能力。
- 建立项目初始化、规范检索、交接、复盘和经验维护的基础结构。
## [0.1.0] - 2026-08-25
### Added
- 初始化 CraftKit 仓库和 Codex 插件市场骨架。
- 建立 `dev`、`doc`、`git`、`knowledge` 和 `skill` 五个插件区域。
- 建立插件清单、仓库说明和基础维护结构。
@@ -0,0 +1,141 @@
# 已有项目初始化与应用拆分工作流
## 1. 适用范围
适用于已经存在源码、构建文件、模块、接口、数据库对象、任务或部署配置的项目。目标是在保护现有工作区的前提下建立可信项目上下文,识别真实运行与依赖边界,再形成可渐进实施、可回滚的应用拆分方案。
已有目录或模块名称不能直接视为正确应用边界;代码引用也不等于业务所有权。静态源码只能证明当前实现,运行拓扑、流量、数据规模和生产行为仍需配置、日志、部署资料或用户确认支持。
## 2. 输入与保护措施
开始时记录:
- 当前分支、HEAD、staged、unstaged 和 untracked 状态;
- 适用的 `AGENTS.md`、`.craftkit/`、项目说明和贡献规范;
- 构建清单、锁文件、源码根、模块、测试和自动化配置;
- 数据库、消息、缓存、文件、定时任务和外部系统的非秘密元数据;
- 制品、部署单元、环境配置入口和运行依赖;
- 已知问题、迁移限制、兼容窗口和不能变更的范围。
不得读取或记录凭据、完整连接串和生产敏感数据。不能为了扫描而构建、启动、迁移或连接外部环境。
## 3. 执行阶段
### E0:确认成熟项目模式
1. 使用 `init` 只读探测有效源码、构建文件、现有项目资料和代表性入口。
2. 展示“成熟项目”判断、项目根、工作区状态和拟扫描范围。
3. 多仓或多根目录时,先确认本次拆分主体及关联仓库只读范围。
4. 到达 G0,等待用户确认模式、范围和可使用的运行资料。
### E1:保守初始化项目资料
1. 完整读取现有 `AGENTS.md` 与 `.craftkit/`,禁止覆盖未知用户章节。
2. 从构建和锁文件提取语言、框架、精确版本、模块和依赖证据。
3. 读取少量代表性的入口、接口、服务、数据访问和测试文件;多种风格并存时不按数量自动裁决。
4. 展示当前项目事实、冲突、疑似历史结构和待确认项。
5. 用户确认后由 `init` 保守合并项目资料,并验证 JSON、索引和忽略规则。
6. 使用 `guidance` 或手工入口顺序验证一项真实项目规则。
### E2:建立现状基线
从源码和可用运行资料分别建立视图:
- 构建视图:模块、依赖方向、公共库和循环依赖;
- 调用视图:入口、同步调用、异步消息、批处理和人工步骤;
- 数据视图:表、文件、缓存、主数据、写入方和共享访问;
- 部署视图:制品、进程、配置、扩缩容、故障域和发布方式;
- 组织视图:维护团队、变更频率、审批和支持责任;
- 质量视图:测试边界、发布验证、已知风险和观测能力。
每项结论注明证据来源和置信度。无法验证的生产拓扑与流量必须标为待验证。
### E3:建立目标需求基线
1. 使用 `requirements` 区分当前行为、目标行为、必须保持的兼容和范围外事项。
2. 明确拆分动因,例如独立发布、团队自治、容量、故障隔离、安全边界或技术替换。
3. 为拆分目标设置可测指标,例如依赖减少、发布独立性、故障影响范围或迁移窗口,而不是只写“解耦”。
4. 记录必须继续兼容的调用方、数据格式、作业、报表和运维流程。
5. 到达 G1,由用户确认目标、约束、验收和允许改变的边界。
### E4:识别候选边界与阻塞耦合
1. 从业务能力和数据所有权出发提出候选边界,再映射当前模块,而不是按目录直接切分。
2. 为每个候选应用列出入口、核心职责、不负责事项、依赖、数据、任务和部署现状。
3. 识别阻塞拆分的耦合:共享写库、跨模块事务、循环调用、共享会话、共享缓存键、文件约定、定时任务、硬编码配置和公共类中的业务逻辑。
4. 区分必须拆除的耦合、迁移期允许的兼容桥和可以长期保留的公共基础能力。
5. 对证据不足的动态调用、反射、配置路由和外部任务安排专项验证。
### E5:设计目标应用和过渡架构
至少比较以下方案:
- 保持现状,仅加强模块边界;
- 模块化单体并清理依赖;
- 抽取少量高收益独立应用;
- 按多个业务能力逐步形成独立应用。
对推荐方案逐项说明业务收益、数据和事务变化、接口成本、部署与观测成本、团队影响、迁移风险和不采用其他方案的理由。应用拆分清单必须包含职责、不负责事项、契约、数据所有权、运行和安全边界。
过渡架构应明确:
- 旧入口与新入口的流量切换方式;
- 同步接口、事件、批处理或文件交换的适用范围;
- 数据迁移、复制、校验、回放和最终切换责任;
- 迁移期读写权威、兼容适配层和弃用条件;
- 跨边界事务的幂等、补偿、重试和对账方式;
- 发生失败时回到旧路径的条件和数据处置方法。
到达 G2,由用户确认目标边界、过渡架构和明确不拆分的部分。
### E6:细化专项设计
根据批准方案按需执行:
- `design-backend`:目标应用内部模块、服务、事务、异常和观测边界;
- `design-frontend`:前端应用、路由、状态、共享组件和渐进切换边界;
- `design-api`、`prepare-api`:新旧接口、适配层、版本、字段映射和弃用计划;
- `design-db`:数据所有权、结构、双读双写风险、迁移、校验和回滚;
- `design-workflow`:跨应用状态、任务、超时、撤回、补偿和审计。
所有专项设计必须映射回现有调用方和测试,不得只描述目标结构。
### E7:制定渐进迁移计划
使用 `plan-change` 将迁移拆为可独立验证的批次。通常按以下顺序评估,但不得机械套用:
1. 增加观测、契约测试和现状回归基线;
2. 收紧原系统内部模块边界并消除高风险循环依赖;
3. 建立目标应用骨架和兼容适配层;
4. 迁移低风险读取或旁路能力;
5. 迁移写入、任务和数据权威;
6. 分批切换调用方和流量;
7. 完成对账、稳定观察、旧路径下线和资料更新。
每个批次必须记录:前置条件、涉及应用、代码与数据范围、兼容方式、验证、观测窗口、停止条件、回滚步骤和不可逆点。未经额外授权,不执行数据库迁移、环境部署或流量切换。
### E8:形成交付基线
汇总并冻结本轮已批准的:现状事实、目标应用边界、决策记录、专项契约、迁移批次、验证矩阵、风险台账和待确认项。需要后续任务接续时使用 `handoff` 生成交接材料,不把未验证运行行为写成已完成结论。
## 4. 已有项目交付物
- 保守合并并验证的 `AGENTS.md` 与 `.craftkit/` 项目资料;
- 当前模块、调用、数据、部署、组织和质量视图;
- 拆分目标、兼容范围和可测验收指标;
- 候选应用、阻塞耦合和目标应用职责矩阵;
- 目标架构、过渡架构、数据权威和契约清单;
- 分批迁移、验证、观测、停止和回滚计划;
- 未验证运行事实、外部协调项和不可逆操作清单。
## 5. 停止条件
遇到以下情况时暂停:无法区分既有改动与本次产物、关键调用或数据写入方未知、生产运行资料与源码冲突、迁移需要未授权的外部系统访问、存在无法回滚的数据变更、关键调用方未纳入范围,或用户尚未批准对应审批门。
## 6. 可直接复制的总控提示词
```text
请按《已有项目初始化与应用拆分工作流》推进当前项目。先保护现有工作区,使用 init 只读判断成熟项目模式,展示项目根、分支、已有改动、拟扫描范围和运行资料边界,在 G0 等待我确认。确认后保守合并 AGENTS.md 与 .craftkit 项目资料,建立构建、调用、数据、部署、组织和质量现状视图,再使用 requirements 确认拆分动因、兼容范围和可测目标。
基于真实业务能力、调用链、数据写入方和部署单元提出候选应用,识别共享写库、跨模块事务、循环调用、共享会话、缓存、文件和任务耦合。比较保持现状、模块化单体、抽取少量应用和多应用渐进拆分方案,形成目标架构、过渡架构、数据权威、契约、迁移批次、验证、观测、停止和回滚计划。全过程区分源码事实、运行事实、用户确认、项目规则、设计建议和待验证项;不把目录名称直接视为业务边界。只在 G0、G1、G2、G3、业务决策、权限扩大或高风险副作用处暂停。未经我另行授权,不修改业务代码,不连接外部环境,不迁移数据,不部署,不提交,不推送。
```
@@ -0,0 +1,124 @@
# 新项目初始化与应用拆分工作流
## 1. 适用范围
适用于空目录、新建仓库,或只有需求、原型和少量说明材料、尚未形成有效源码与构建结构的项目。目标是先建立可信项目上下文,再从已确认需求推导应用边界和实施计划。
本流程不默认选择微服务、前后端框架、数据库、消息系统、云平台或最新版本。`init` 只创建 Agent 与项目知识资料;代码工程初始化属于后续实施任务。
## 2. 输入与前置确认
开始时收集并确认:
- 项目根目录和 Git 状态;
- 项目用途、目标用户、交付形态和预期运行环境;
- 需求、原型、会议材料和验收要求;
- 已确定的语言、框架、数据库、构建工具及精确版本;
- 组织标识、包名或 npm scope 等必须提前确定的标识;
- 允许参考的项目,以及只允许借鉴的架构、依赖、命名、测试、规范或工具范围;
- 安全、合规、容量、可用性、交付期限和团队边界。
未知信息保持为空或标记待确认,不能用流行方案自动补齐。
## 3. 执行阶段
### N0:确认空项目模式
1. 使用 `init` 只读检查源码、构建文件、`AGENTS.md` 和 `.craftkit/`。
2. 展示“空项目”判断、证据、项目根和允许扫描的参考材料。
3. 到达 G0,等待用户确认模式和参考范围。
### N1:建立项目上下文
1. 确认项目类型、交付形态、技术选型的已决项和未决项。
2. 如存在参考项目,分别记录当前项目输入事实、参考项目做法和建议采用项。
3. 预览拟创建的 `AGENTS.md`、`.craftkit/project.json`、`.craftkit/README.md`、必要索引与 `.craftkit/.gitignore`。
4. 用户确认后由 `init` 创建最小资料骨架,不提前生成空业务目录。
5. 校验 JSON、索引链接,以及 `/local/`、`/cache/` 的忽略规则;用 `guidance` 或手工入口顺序验证一次真实规则查询。
### N2:建立需求基线
1. 使用 `requirements` 整理角色、场景、触发条件、主流程、异常流程、数据、权限、兼容和范围外事项。
2. 为需求分配稳定编号,并为关键场景编写可测试验收标准。
3. 将技术偏好与业务约束分开;只有影响目标或验收的技术条件才进入需求基线。
4. 到达 G1,由用户确认需求、范围和暂时保留的业务待决项。
### N3:形成业务能力地图
按业务目标而不是技术分层拆解能力:
1. 识别核心域、支撑能力、通用能力和外部系统责任。
2. 为每项能力记录使用者、业务不变量、输入输出、关键数据、变化节奏和责任主体。
3. 标出必须强一致、允许最终一致、可离线处理和必须人工确认的流程。
4. 识别共享概念中的同名异义与不同上下文,避免直接建立全局统一数据模型。
产物至少包括能力清单、能力关系和待确认边界。
### N4:提出应用拆分候选
1. 先形成最小可行边界:模块化单体、少量独立应用或其他适合交付形态的结构。
2. 只有在业务所有权、数据、发布节奏、隔离或扩缩容需求明确时才增加独立应用。
3. 对每个候选应用填写职责、不负责事项、调用方、数据所有权、上下游、运行和安全边界。
4. 至少比较以下方案:维持单一应用、按主要业务能力拆分、按独立运行要求进一步拆分。
5. 计算拆分引入的契约治理、分布式一致性、测试、部署、观测和团队协作成本。
6. 到达 G2,由用户确认目标应用清单、保留模块和不拆分项。
### N5:细化应用契约
根据已批准边界按需执行:
- 使用 `design-backend` 设计应用内部模块、服务、事务和错误边界。
- 使用 `design-frontend` 设计前端应用、页面、状态和复用边界。
- 使用 `design-api` 设计跨应用同步接口,使用 `prepare-api` 整理调用方字段映射。
- 使用 `design-db` 明确数据所有权、表结构、索引和未来迁移边界。
- 使用 `design-workflow` 设计跨应用状态、任务、补偿、幂等和审计。
跨应用共享代码只保留稳定、无业务所有权争议的基础能力。共享数据库、跨库事务和双向同步必须显式记录风险,不能作为无说明默认方案。
### N6:设计工程和运行结构
在不创建代码的前提下给出建议结构:
- 仓库组织:单仓、多仓或混合方式及选择依据;
- 每个应用的源码、测试、契约和配置边界;
- 公共库的准入条件、版本和兼容策略;
- 本地开发、持续集成、制品、部署和环境配置方式;
- 日志、指标、追踪、健康检查和故障隔离要求;
- 认证授权、密钥管理和敏感数据处理边界。
无法从需求确定的平台决策继续作为待确认项,不把目录草案写成已创建结构。
### N7:形成建设计划
使用 `plan-change` 按以下依赖顺序制定计划:
1. 建立项目与契约基线;
2. 搭建最小工程和验证链路;
3. 优先实现能纵向验证核心场景的应用切片;
4. 建设公共能力和外部集成;
5. 完成跨应用契约、失败恢复和安全验证;
6. 补齐部署、可观测性、容量和验收验证。
每个批次记录输入、应用范围、产物、依赖、测试、停止条件和回退方式。到达 G3,由用户确认后续是否进入工程创建与代码实现。
## 4. 新项目交付物
- 已验证的 `AGENTS.md` 与 `.craftkit/` 项目资料;
- 已确认需求基线和待确认清单;
- 业务能力地图及能力关系;
- 应用拆分方案、替代方案和决策依据;
- 应用职责矩阵、契约清单、数据所有权矩阵和依赖关系;
- 工程与运行结构建议;
- 分阶段建设计划、验证矩阵和回退边界。
## 5. 停止条件
出现以下情况时暂停,不继续推导:核心业务目标冲突、数据所有权无法确定、关键外部契约未知、技术选型会实质改变交付边界、参考项目授权范围不清,或用户尚未批准对应审批门。
## 6. 可直接复制的总控提示词
```text
请按《新项目初始化与应用拆分工作流》推进当前项目。先使用 init 只读判断项目模式,展示项目根、Git 状态、输入材料、参考范围和待确认项,在 G0 等待我确认。确认后保守初始化 AGENTS.md 与 .craftkit 项目资料,使用 requirements 建立需求基线,再依次完成业务能力地图、应用拆分候选、方案比较、应用职责与数据所有权矩阵、跨应用契约、工程与运行结构建议以及分阶段建设计划。
全过程区分用户已确认目标、输入事实、参考项目做法、项目规则、设计建议、待确认项和待验证项。不要默认微服务、框架、数据库、云平台或最新版本;允许“不拆分”或“模块化单体”成为最终建议。只在 G0、G1、G2、G3、业务决策、权限扩大或高风险副作用处暂停。未经我另行授权,不创建业务代码,不安装依赖,不执行数据库、部署、提交或发布操作。
```
@@ -0,0 +1,94 @@
# 项目初始化到应用拆分工作流
## 1. 文档用途
本工作流用于把项目从“缺少稳定上下文”推进到“形成可执行、可验证的应用拆分基线”。这里的应用是具有明确职责、所有权、接口、数据边界和运行形态的交付单元;应用内部仍可继续划分模块。应用不必等同于微服务,也不要求每个业务能力都独立部署。
本工作流只完成项目资料初始化、需求基线、现状或目标架构分析、应用拆分设计和实施规划。除非用户另行明确授权,不创建业务代码,不迁移数据,不调整部署环境,也不执行 Git 提交或发布。
## 2. 两种执行口径
- 仓库为空、只有少量说明文件,或尚未形成有效源码与构建结构时,使用[新项目初始化与应用拆分工作流](NEW-PROJECT-INITIALIZATION-AND-APPLICATION-SPLIT.md)。
- 已存在有效源码、构建文件、模块、接口、数据库对象或部署配置时,使用[已有项目初始化与应用拆分工作流](EXISTING-PROJECT-INITIALIZATION-AND-APPLICATION-SPLIT.md)。
- 情况混合时,以待拆分主体为准:新建独立产品按新项目口径;从现有系统剥离能力按已有项目口径。
- 执行 `init` 探测后必须展示模式判断及证据,用户可以覆盖建议模式。
## 3. 共同原则
### 3.1 证据分层
所有结论区分为:用户已确认目标、当前项目事实、参考项目做法、项目明确规则、设计建议、待确认项和待验证项。不能把相似系统、单个代码样本或行业惯例直接写成当前项目事实。
### 3.2 拆分判据
候选应用至少从以下方面评估:
- 业务能力与变化节奏;
- 团队或责任主体;
- 数据所有权和一致性要求;
- 对外接口与上下游依赖;
- 安全、权限、审计和合规边界;
- 性能、容量、可用性和故障隔离要求;
- 构建、部署、扩缩容和发布节奏;
- 迁移、测试、运维和认知成本。
只有拆分收益大于新增的分布式事务、接口治理、数据同步、部署和运维成本时,才建议形成独立应用。证据不足时可以输出模块化单体、逻辑分区或暂不拆分方案。
### 3.3 Skill 使用边界
- `init`:只初始化或合并 `AGENTS.md` 与 `.craftkit/` 项目资料,不生成业务工程。
- `guidance`:检索项目规则;未安装时按 `AGENTS.md`、`.craftkit/agents/index.md`、`.craftkit/standards/index.md` 和命中正文的顺序手工读取。
- `requirements`:建立业务需求与验收基线。
- `design-backend`、`design-frontend`:形成应用内部的后端和前端边界。
- `design-api`、`prepare-api`:细化跨应用和前后端契约。
- `design-db`:细化数据所有权、结构、迁移和回滚。
- `design-workflow`:处理跨状态、跨角色或跨事务的业务流程。
- `plan-change`:把批准的拆分设计转成按依赖排序的实施计划。
## 4. 共同阶段与审批门
| 阶段 | 目标 | 主要产物 | 完成后动作 |
| --- | --- | --- | --- |
| P0 模式判断 | 确认新项目或已有项目口径 | 模式判断、扫描范围、风险 | 进入 G0 |
| P1 项目初始化 | 建立可持续读取的项目上下文 | `AGENTS.md`、`.craftkit/project.json`、必要索引 | 进入 P2 |
| P2 需求基线 | 明确目标、范围、角色、流程和验收 | 需求基线、疑问清单 | 进入 G1 |
| P3 边界分析 | 识别业务能力、数据、接口、团队和运行约束 | 能力地图、依赖图、拆分判据 | 进入 P4 |
| P4 应用拆分 | 形成候选方案并完成权衡 | 应用清单、职责、契约、数据和部署边界 | 进入 G2 |
| P5 细化设计 | 细化接口、数据、流程、前后端和非功能要求 | 专项设计与验证矩阵 | 进入 P6 |
| P6 实施规划 | 确定建设或迁移批次、兼容和回滚 | 分阶段计划、停止条件、回滚方案 | 进入 G3 |
| P7 交付基线 | 汇总批准结论和后续执行入口 | 架构基线、决策记录、任务清单 | 流程完成 |
审批门:
- G0:确认模式、项目根、参考资料和允许扫描范围。
- G1:确认需求基线以及仍需保留的业务待决项。
- G2:确认应用边界、保留的替代方案和不拆分项。
- G3:确认实施批次、外部依赖、数据迁移、兼容策略和回滚边界。
## 5. 应用拆分基线格式
每个候选应用至少记录:
| 字段 | 内容 |
| --- | --- |
| 应用名称 | 中性候选名;未确认时标记为暂定 |
| 核心职责 | 应用负责的业务能力和业务不变量 |
| 不负责事项 | 明确排除的能力,防止边界回流 |
| 使用者 | 用户角色、调用方和运维主体 |
| 对外契约 | API、事件、文件、任务或人工交互 |
| 数据所有权 | 主数据、写入责任、读取方式和一致性要求 |
| 上下游依赖 | 同步、异步、批处理及不可用时的行为 |
| 运行边界 | 构建、部署、配置、扩缩容和可用性要求 |
| 安全边界 | 认证、授权、审计、敏感数据和网络限制 |
| 验证方式 | 契约、集成、迁移、回归和运行验证 |
| 拆分理由 | 收益、成本、风险及未采用替代方案 |
## 6. 完成标准
- 项目模式和项目根已确认,初始化产物通过 JSON、索引和忽略规则检查。
- 需求、当前事实、参考做法和设计建议保持可追溯分离。
- 每个应用都有职责、不负责事项、接口、数据、依赖和运行边界。
- 跨应用事务、数据同步、失败恢复、兼容和回滚已有明确处理方式或阻塞项。
- 拆分方案包含至少一个替代方案,并说明为什么不选择过度拆分或维持现状。
- 实施计划具有依赖顺序、验证方式、停止条件和可恢复路径。
- 未经授权的编码、数据库、部署、提交和发布操作均未执行。
+117 -23
View File
@@ -1,30 +1,123 @@
# CraftKit # CraftKit
CraftKit 是一组面向 Codex 插件市场的中性 Skill 工具。项目从通用问题出发独立实现能力,不携带任何公司的品牌、内部框架、业务规则、源码、模板或环境信息。 CraftKit 是面向 Codex 的通用插件工具集,覆盖软件开发、文档处理、Git 交付、项目知识维护和 Skill 管理。每个插件的核心能力均可独立安装;同时安装相关插件时可以获得跨 Skill 增强,缺少增强插件时按各 Skill 声明的回退流程执行。能力描述保持简洁、中性,并以当前 Codex 能力和公开标准为基础维护。
## 文档导航 版本发布与重要变更记录见 [CHANGELOG.md](CHANGELOG.md)。
- [协作约定](AGENTS.md):项目范围、Skill 设计、迁移边界、验证和 Git 规则。 ## 全流程自动化
- [迁移计划](migration/MIGRATION_PLAN.md):迁移批次、依赖顺序、合并原则和验收门禁。
- [迁移追踪说明](migration/README.md):来源指纹和状态维护方式。 需要根据原始需求和项目代码,串联需求分析、技术设计、开发分支、代码实现、测试审核与本地提交时,请使用 [CraftKit 全流程自动化使用指南](WORKFLOW.md)。该指南提供阶段状态机、人工审批门、自动推进规则和可直接交给 Codex 或其他已安装 CraftKit 工具的总控提示词。
需要从项目上下文初始化推进到应用边界识别、拆分设计和实施规划时,请使用[项目初始化到应用拆分工作流](PROJECT-INITIALIZATION-AND-APPLICATION-SPLIT.md),并根据项目现状选择:
- [新项目初始化与应用拆分工作流](NEW-PROJECT-INITIALIZATION-AND-APPLICATION-SPLIT.md):适用于空目录、新仓库或尚未形成有效源码结构的项目。
- [已有项目初始化与应用拆分工作流](EXISTING-PROJECT-INITIALIZATION-AND-APPLICATION-SPLIT.md):适用于已有源码、数据、接口和部署形态,需要基于现状渐进拆分的项目。
## 插件组成 ## 插件组成
| 插件 | 用途 | 当前状态 | | 插件 | 用途 |
| --- | --- | --- | | --- | --- |
| `dev` | 软件设计、实现、审查、测试、升级与估算 | 已迁移 22 个 Skill,覆盖完整通用开发流程 | | `dev` | 软件需求分析、设计、实现、审查、测试、升级与估算 |
| `doc` | 文档转换、整理与写作 | 已迁移 8 个 Skill,新增报告、消息与需求整理 | | `doc` | 文档转换、整理、归档与写作 |
| `git` | 分支、提交、变更提取、集成与发布 | 已迁移 6 个 Skill,发布操作按阶段分别授权 | | `git` | 分支、提交、变更提取、集成与发布准备 |
| `knowledge` | 项目初始化、交接、复盘与经验 | 已迁移 `handoff`、`init`、`trace`、`distill`、`lessons`、`worklog` | | `knowledge` | 项目初始化、任务交接、问题复盘与经验维护 |
| `skill` | 项目规范及 Skill 创建、迁移与维护 | 已迁移 `guidance`、`guidance-edit` | | `skill` | 项目规范检索、维护与 Skill 辅助工具 |
## Skill 介绍
### Dev
`dev` 插件覆盖从变更规划、技术设计、代码实现到专项审查和测试的软件开发流程。
| Skill | 用途 |
| --- | --- |
| `plan-change` | 分析需求或问题的影响范围、依赖、风险和验证方式,形成可执行的变更计划。 |
| `design-backend` | 设计后端模块边界、服务职责、事务、错误处理及数据影响。 |
| `design-frontend` | 设计前端页面、路由、状态、交互、组件组合和数据流。 |
| `design-api` | 设计或评审 HTTP API 的资源、方法、状态码和请求响应契约。 |
| `design-db` | 设计或评审数据库表、约束、索引及变更与回滚方案。 |
| `design-frontend-data` | 设计前端请求边界、视图模型、状态所有权、缓存和并发处理。 |
| `design-workflow` | 设计业务流程的任务、状态转换、权限、回退、撤回和审计规则。 |
| `prepare-api` | 整理前后端接口清单、字段映射、类型转换、缺失项和联调风险。 |
| `component` | 根据项目依赖和现有用法选择组件,查证 props、events 和 slots。 |
| `form` | 设计或检查表单分组、布局、条件字段、错误展示和可访问性。 |
| `style` | 根据项目视觉基线设计颜色、排版、间距、主题和响应式样式。 |
| `implement-backend` | 按项目语言、框架和规范编写或修改后端代码。 |
| `implement-frontend` | 按项目框架、组件契约和请求封装编写或修改前端代码。 |
| `review-code` | 审查工作区、提交或分支变更中的正确性、兼容、安全、性能和测试问题。 |
| `review-java` | 专项评审 Java 代码的资源管理、并发、异常和可维护性。 |
| `review-mybatis` | 专项评审 Mapper、动态 SQL、参数映射、事务边界和查询性能。 |
| `review-frontend` | 专项评审前端组件边界、状态、渲染、交互、可访问性和性能。 |
| `test-backend` | 设计、生成、修改或运行后端聚焦测试,覆盖正常、边界和失败路径。 |
| `test-ui` | 设计 UI 测试计划,并在具备条件时执行真实浏览器测试。 |
| `analyze-bugs` | 整理结构化 Bug 清单,完成去重、分类、趋势和风险分析。 |
| `upgrade` | 规划并实施前端、后端或依赖的大版本升级。 |
| `estimate` | 根据范围、依赖、未知项和验证成本给出工作量区间与假设。 |
### Doc
`doc` 插件用于常见办公文档转换、Markdown 整理和项目写作。
| Skill | 用途 |
| --- | --- |
| `docx-to-md` | 将 Word `.docx` 转换为 Markdown,并提取表格、链接和图片。 |
| `md-to-docx` | 将 Markdown 转换为可编辑的 Word `.docx` 文档。 |
| `xlsx-to-md` | 将 Excel `.xlsx` 工作簿按工作表转换为 Markdown 表格。 |
| `format-md` | 修正 Markdown 的标题、空行、列表、代码块、表格和链接格式。 |
| `archive` | 按明确规则复制、重命名、清理或拆分项目文档并生成报告。 |
| `requirements` | 将技术说明、会议材料或问题描述整理成可确认的需求说明。 |
| `report` | 根据事实、Git 记录和任务状态撰写工作汇报或阶段总结。 |
| `message` | 根据事件、受众和行动要求起草通知、提醒、确认或故障沟通消息。 |
### Git
`git` 插件提供可预览、可确认的版本控制与交付操作。
| Skill | 用途 |
| --- | --- |
| `branch` | 按仓库约定创建本地分支,并管理独立 Worktree 的创建与安全关闭。 |
| `commit-msg` | 根据工作区或暂存区的真实变更生成 Conventional Commit 信息。 |
| `identity` | 查看或设置 Git 提交用户名和邮箱及其作用域。 |
| `export` | 按提交、时间或工作区范围导出变更文件并生成分类清单。 |
| `integrate` | 评估分支集成风险,通过隔离 Worktree 准备、验证、发布和清理预集成分支。 |
| `release` | 准备版本号、变更摘要和发布检查,并按授权执行版本提交、标签或发布。 |
### Knowledge
`knowledge` 插件通过项目内 `.craftkit/` 目录维护上下文、交接和可复用经验。
| Skill | 用途 |
| --- | --- |
| `init` | 初始化或更新项目的 `AGENTS.md` 与 `.craftkit/` 项目资料。 |
| `handoff` | 生成可持续更新的任务交接文档和新任务接续提示词。 |
| `distill` | 从任务证据中提炼可复用结论、决策和问题经验。 |
| `lessons` | 初始化、维护和审计项目问题经验库。 |
| `trace` | 复盘 Agent 的偏离、漏读或规则失效,并提出改进建议。 |
| `worklog` | 根据指定日期、时区和作者的 Git 提交生成工作日志。 |
### Skill
`skill` 插件负责项目规范的查询和维护。
| Skill | 用途 |
| --- | --- |
| `guidance` | 检索项目协作说明、项目资料和公共基线,返回可追溯的规则、冲突与缺口。 |
| `guidance-edit` | 建立、检查和维护 `.craftkit/standards/` 规范索引。 |
## 目录结构 ## 目录结构
```text ```text
CraftKit/ CraftKit/
├─ .agents/plugins/marketplace.json # 仓库级 Codex 市场清单 ├─ .agents/plugins/marketplace.json # 仓库级 Codex 插件市场清单
├─ AGENTS.md # Codex 与贡献者协作约定 ├─ AGENTS.md # 项目协作与维护约定
├─ migration/ # 迁移计划、来源指纹与状态追踪 ├─ CHANGELOG.md # SemVer 版本与重要变更记录
├─ WORKFLOW.md # 需求到本地提交的研发交付工作流
├─ PROJECT-INITIALIZATION-AND-APPLICATION-SPLIT.md
│ # 项目初始化到应用拆分的共同入口
├─ NEW-PROJECT-INITIALIZATION-AND-APPLICATION-SPLIT.md
│ # 新项目口径
├─ EXISTING-PROJECT-INITIALIZATION-AND-APPLICATION-SPLIT.md
│ # 已有项目口径
├─ plugins/ # 可独立安装的插件 ├─ plugins/ # 可独立安装的插件
│ ├─ dev/ │ ├─ dev/
│ ├─ doc/ │ ├─ doc/
@@ -34,15 +127,16 @@ CraftKit/
└─ README.md └─ README.md
``` ```
## 开发约束 ## 设计原则
- Skill 必须独立重写,不复制公司插件的正文、脚本、模板、示例或规则库。 - 每个 Skill 聚焦一个清晰能力,名称与触发描述准确,避免宽泛兜底或重复功能。
- 专有能力应先抽象为可配置、基于公开标准或用户显式输入的中性平替;不能仅因专有内容较多就整体排除。 - 优先读取当前项目的代码、配置、规范和用户输入,使建议与实际环境保持一致。
- 发布内容不得出现公司名称、产品名称、内部域名、内部包名、项目名或人员信息。 - 公共技术规则基于适用版本的官方资料维护;项目专属规则保存在项目自己的 `.craftkit/standards/` 中。
- 每个 Skill 文件夹名必须与 `SKILL.md` frontmatter 的 `name` 一致。 - 涉及写文件、Git 状态、外部系统或发布的操作必须明确边界,并按风险取得用户授权。
- 新增或修改插件后必须执行 Skill 校验、插件校验和去公司化扫描。 - 静态检查、脚本测试和真实场景验证分别记录,不用其中一种替代另一种。
- 上游变化只用于触发人工复核,不得自动覆盖已迁移内容。
## 本地使用 ## 本地使用
这是仓库级 marketplace。安装前先将本仓库注册为本地 marketplace,再按需安装其中的插件。正式发布前还需要补充许可证、公开仓库地址、作者信息、隐私政策及市场素材。 本仓库提供仓库级 marketplace。将仓库注册为本地 marketplace 后,可按需安装 `dev`、`doc`、`git`、`knowledge` 或 `skill` 插件。
正式公开发布前,还需补充许可证、公开仓库地址、作者信息、隐私政策和市场素材。
+445
View File
@@ -0,0 +1,445 @@
# CraftKit 全流程自动化使用指南
## 1. 文档用途
本文档是一份可直接交给 Codex 或其他已安装 CraftKit 的智能体执行的流程契约。它用于把原始需求、零散对话、会议纪要、问题描述和项目源码,逐步转化为:
1. 可确认的需求分析文档;
2. 基于项目事实的技术设计文档;
3. 符合仓库约定的开发分支;
4. 经过验证的代码与测试;
5. 可复核的代码审核结论;
6. 范围准确的本地 Git 提交。
流程采用“自动执行到审批门”的方式推进。智能体负责取证、分析、编写、验证、修正和阶段衔接;人只负责批准阶段基线、选择无法从证据确定的业务决策,以及确认具有 Git 或外部环境副作用的操作。
本文档不授予推送、合并、发布、生产数据库变更、生产环境操作或明文凭据访问权限。若需要这些操作,必须由用户另行明确授权。
## 2. 适用前提
执行环境应满足以下条件:
- 已安装 CraftKit 中任务所需的 `doc`、`dev`、`git`、`knowledge` 和 `skill` 插件;
- 智能体能够读取目标项目的需求材料、源码、Git 状态和项目内说明文件;
- 目标项目是 Git 仓库,且用户已经给出或允许智能体识别项目根目录;
- 项目使用的构建、测试或格式化工具在本地可用,或允许智能体如实记录环境阻塞;
- 用户允许智能体在已批准范围内修改项目文件并运行非破坏性验证命令。
如果项目尚未建立 `AGENTS.md` 或 `.craftkit/project.json`,智能体可以建议使用 `init` 初始化项目上下文,但必须遵循该 Skill 的确认要求,不能隐式覆盖现有配置。
## 3. 核心执行原则
### 3.1 证据优先
每项结论必须标记为以下类型之一:
- **输入事实**:来自用户原始材料或已确认对话;
- **源码事实**:来自当前分支的代码、配置、测试、数据库脚本或接口契约;
- **项目规则**:来自适用的 `AGENTS.md`、`.craftkit/` 或仓库明确约定;
- **设计建议**:尚未实施、但有依据的技术方案;
- **待确认项**:无法从现有证据可靠确定的业务或技术决策;
- **待验证项**:静态证据不足以证明的运行时行为。
不能从当前代码行为反推业务意图,也不能把相似模块的实现直接当作本需求规则。代码只能说明当前系统如何实现,不能替代用户对目标行为的确认。
### 3.2 最小充分读取
先读取项目入口、适用规则、构建文件和最相关调用链,再按证据需要扩大范围。禁止无目的加载整个仓库、整个知识库或与任务无关的业务数据。
### 3.3 自动推进
每个阶段完成且满足质量门后,智能体必须自动进入下一个阶段,不要求用户重复发送“继续”。只有到达审批门、发生阻塞或需要新增权限时才暂停。
用户批准某一审批门后,该批准同时表示允许智能体继续执行到下一审批门,但不扩大文件范围、外部系统范围或破坏性操作权限。
### 3.4 保留工作区
任何阶段都必须保护用户已有改动:
- 开始时记录 staged、unstaged 和 untracked 状态;
- 不覆盖、还原、清理或暂存无关改动;
- 不自动使用 `git reset --hard`、强制切换、强制删除或历史改写;
- 无法区分本次改动与既有改动时暂停并请求用户确认;
- `.craftkit/local/**` 和 `.craftkit/cache/**` 不纳入提交范围。
### 3.5 验证分层
验证结果必须区分:
- 静态检查;
- 编译或构建;
- 单元测试;
- 集成测试;
- 浏览器或真实环境验证;
- 未执行或受环境阻塞的验证。
一种验证通过不能替代另一种,也不能把“计划已生成”写成“功能已验收”。
## 4. 流程状态机
| 阶段 | 主要 Skill | 核心产物 | 完成后动作 |
| --- | --- | --- | --- |
| S0 项目接入 | `init`、`guidance` | 项目上下文、适用规则、初始 Git 快照 | 自动进入 S1 |
| S1 原始需求归集 | `requirements` | 需求分析文档草案、疑问清单、验收标准 | 进入审批门 G1 |
| S2 变更规划 | `plan-change` | 影响范围、依赖顺序、验证计划 | 自动进入 S3 |
| S3 技术设计 | `design-db`、`design-api`、`design-backend`、`design-frontend`、`design-frontend-data`、`design-workflow`、`prepare-api` | 按需生成的设计文档集合 | 进入审批门 G2 |
| S4 分支准备 | `branch` | 分支名、基准、创建命令及风险预览 | 进入审批门 G3 |
| S5 代码实现 | `implement-backend`、`implement-frontend` | 业务代码、配置内的非敏感必要变更、测试代码 | 自动进入 S6 |
| S6 验证与自修复 | `test-backend`、`test-ui` | 分层验证记录、失败分类、修复结果 | 自动进入 S7 |
| S7 代码审核 | `review-code`,按需叠加 `review-java`、`review-mybatis`、`review-frontend` | 审核报告、问题清单、未验证边界 | 自动修复明确问题后复审,进入 G4 |
| S8 提交准备 | `commit-msg` | 精确文件范围、提交拆分与提交信息 | 进入审批门 G5 |
| S9 本地提交与收尾 | Git 原生命令、`branch close` | 一个或多个本地提交、提交后状态、Worktree 清理结果 | 释放额外 Worktree 后输出最终交付报告 |
阶段不得仅凭名称跳过。确实不适用时,应记录“不适用”的证据和原因,再继续推进。
## 5. 审批门
### G1 需求基线审批
智能体必须展示:
- 目标、范围内和范围外事项;
- 用户角色、主流程、异常流程和业务规则;
- 可测试的验收标准;
- 输入冲突、模糊表述、缺失规则和建议默认值;
- 拟落盘路径及将创建或修改的需求文档。
用户批准后,需求文档成为后续设计和验收的基线。后续发现需求级冲突时,必须回到 G1 做增量审批。
### G2 技术设计审批
智能体必须展示:
- 当前实现与目标实现的差距;
- 前端、后端、数据库、API、工作流等适用设计;
- 关键技术决策、备选方案及选择依据;
- 数据迁移、兼容、回滚、安全和性能风险;
- 实施步骤、文件影响范围和验证矩阵;
- 仍需业务或外部系统确认的事项。
用户批准后,智能体不得自行扩大设计范围。实现过程中若必须改变已批准的核心契约,应暂停并返回 G2。
### G3 分支创建审批
按照 `branch` Skill 的要求,智能体必须在执行前展示:
- 当前分支和工作区状态;
- 目标分支名;
- 基准引用及提交短哈希;
- 在当前工作区还是独立 Worktree 创建,以及是否影响未提交修改;
- 使用独立 Worktree 时的准确绝对路径;
- 唯一的分支创建命令。
只有用户明确批准这组最终信息后才能创建本地分支。`fetch`、创建分支、移除 Worktree 和推送分支是彼此独立的授权;本流程默认不执行 fetch 和 push。
### G4 代码效果审批
智能体必须展示:
- 已实现的需求条目及对应文件;
- 需求验收标准与验证证据的映射;
- 构建、测试、静态检查和浏览器验证结果;
- 已自动修复的审核问题;
- 尚未修复的问题、环境阻塞、待验证行为和残余风险;
- 实际 Git 差异摘要。
只有用户确认代码效果可接受后,才能进入提交准备。用户要求调整时,流程返回 S5,并重新执行 S6、S7 和 G4。
### G5 提交审批
智能体必须展示:
- 每个拟提交逻辑分组及其精确文件清单;
- staged、unstaged、untracked 和排除项;
- Conventional Commit 提交信息;
- 敏感文件、本地配置、缓存、二进制文件和无关改动检查结果;
- 将执行的 `git add -- <明确路径>` 和 `git commit` 命令。
用户批准后,只允许暂存展示过的精确路径并创建本地提交。不得使用 `git add .`、`git add -A` 或包含未确认文件的通配符。提交完成后不得自动 push、合并、打标签或发布。
## 6. 各阶段执行要求
### S0:项目接入与基线检查
1. 确定项目根目录和需求材料范围。
2. 读取所有适用的 `AGENTS.md`。
3. 检查 `.craftkit/project.json`、规范索引、构建文件、依赖锁文件和主要源码目录。
4. 使用 `guidance` 获取本任务适用的项目规则,并区分明确规则、公共建议和代码现状。
5. 执行只读 Git 检查,记录当前分支、HEAD、工作树状态、远程引用现状和 worktree 占用情况。
6. 若项目上下文缺失,只在确有必要时提出 `init`;初始化写入仍遵守该 Skill 的确认步骤。
7. 建立任务台账,至少记录任务编号、当前状态、输入、产物、审批记录、风险和下一动作。
推荐将本次任务的本地运行状态保存到 `.craftkit/local/`;除非用户明确要求共享,不把运行状态写入可提交目录。
### S1:需求归集与落表
使用 `requirements` 处理原始需求。输入可以是完整需求文档,也可以是聊天记录、口头描述转写、邮件、会议纪要、Bug 描述、截图文字或零散技术说明。
需求文档至少包含:
1. 背景与目标;
2. 输入来源和证据索引;
3. 术语与角色;
4. 当前行为;
5. 目标行为;
6. 功能范围;
7. 主流程与异常流程;
8. 业务规则和状态规则;
9. 数据、权限、兼容和审计要求;
10. 非功能要求;
11. 范围外事项;
12. 可测试验收标准;
13. 冲突、假设、待确认项和待验证项;
14. 需求追踪编号。
需求追踪编号建议使用 `REQ-001`、`REQ-002`。后续设计、代码、测试和审核都引用这些编号,形成端到端追踪。
对于模糊输入,智能体应优先用源码确认“当前行为”,用用户材料提取“目标方向”,但不能替用户决定金额、时点、角色权限、状态跳转、合规要求、数据口径或其他会改变业务结果的规则。
### S2:变更计划
使用 `plan-change` 将批准的需求基线映射到项目实际代码。计划至少说明:
- 需求类型:新功能、增量修改、缺陷修复或重构;
- 真实入口、调用链、数据流和相邻稳定实现;
- 可能修改的模块和文件;
- 前后置依赖及执行顺序;
- 每一步的输入、产物、验证和停止条件;
- 数据库、接口、前端、后端、工作流、部署和外部系统影响;
- 对已有行为、版本兼容和脏工作区的保护方式。
变更计划是设计编排依据,不替代详细设计,也不执行代码修改。
### S3:技术设计
只调用实际适用的设计 Skill:
| 场景 | 使用 Skill | 重点输出 |
| --- | --- | --- |
| 后端模块、事务、异常、权限 | `design-backend` | 模块职责、调用关系、事务与错误语义 |
| 表结构、约束、索引、迁移 | `design-db` | 数据模型、DDL 方案、迁移与回滚 |
| HTTP 契约 | `design-api` | 路径、方法、请求响应、错误与兼容 |
| 页面、路由、交互、状态 | `design-frontend` | 页面级契约和交互流程 |
| 请求状态、缓存、竞态 | `design-frontend-data` | 状态所有权、转换和并发策略 |
| 审批、任务、状态流转 | `design-workflow` | 状态转换、权限、幂等和补偿 |
| 前后端字段对接 | `prepare-api` | 接口清单、字段映射和联调风险 |
| 组件、表单、样式 | `component`、`form`、`style` | 基于项目证据的专项设计 |
设计产物应使用 `REQ-*` 关联需求,并为关键方案使用 `DES-*` 编号。数据库、API、前端和后端设计相互引用,不能产生字段、枚举、状态或错误语义冲突。
若现有项目没有明确文档目录,智能体应在 G2 前提出建议路径并取得确认,不能自行制造固定目录约定。
### S4:创建开发分支
需求与设计批准后,使用 `branch`:
1. 检查当前分支、所有本地和远程分支、Git worktree 及工作区状态;
2. 从仓库约定识别分支格式,无约定时再建议 `feature/<short-name>`、`fix/<short-name>` 等名称;
3. 不默认基准分支,不默认远端为最新;
4. 验证分支名、基准提交和同名引用;
5. 到达 G3,等待用户对最终命令的明确批准;
6. 创建后验证实际工作目录中的分支、起点和状态;使用独立 Worktree 时,还要确认主工作区未变化并记录分支占用路径。
若工作区不干净,不能自动 stash、提交、还原或清理。必须说明直接切换会携带哪些修改,并优先建议在经 G3 批准的独立 Worktree 中创建分支。独立 Worktree 在开发期间保持使用,任务完成后必须通过 `branch close` 清理门禁释放分支占用。
### S5:代码实现
根据设计分别使用 `implement-backend` 和 `implement-frontend`。实现过程必须:
- 从当前项目确认语言、框架和精确版本;
- 延续现有目录、命名、注释、异常、日志、事务、组件、请求和测试风格;
- 追踪真实入口、调用方和数据落点;
- 只实现批准范围内的最小完整变更;
- 在代码和测试映射中引用相关 `REQ-*`、`DES-*`,但不为追踪编号制造不符合项目风格的代码注释;
- 不虚构内部依赖、组件属性、接口、数据库行为或业务校验;
- 不修改凭据、部署参数和生产配置;
- 新增依赖、执行数据迁移或触达外部系统前单独申请权限。
前后端可独立实现时可以并行分析,但共享契约必须先稳定。任何并行工作都不能让多个执行者同时修改同一文件或同一职责边界。
### S6:验证与自动修复
智能体根据项目已有命令执行与风险匹配的验证:
1. 格式化和静态检查;
2. 类型检查或编译;
3. 聚焦单元测试;
4. 必要的模块级或集成测试;
5. 在具备可访问系统、授权账号和数据清理策略时,使用 `test-ui` 执行浏览器测试。
失败后按以下类别处理:
- **本次实现缺陷**:自动修复,并重新执行受影响验证;
- **既有失败**:保留原始证据,确认与本次变更的关系,不擅自修复范围外代码;
- **环境或依赖阻塞**:记录命令、错误摘要和未验证范围;
- **需求或设计冲突**:返回 G1 或 G2;
- **需要新权限**:暂停并申请最小权限。
不得删除测试、降低断言、屏蔽错误或伪造结果来取得通过状态。
### S7:代码审核与闭环
使用 `review-code` 审查最终工作区差异;Java、MyBatis 或前端变更存在专项风险时,叠加相应专项审核 Skill。
审核范围必须包含:
- 基线提交和最终差异;
- 需求与设计追踪;
- 正确性、兼容、安全、性能、并发、事务、权限和数据风险;
- 测试充分性和未验证边界;
- staged、unstaged 和 untracked 的准确区分。
对于审核发现:
- 明确属于已批准范围、修复方式唯一且低风险的问题,智能体可自动返回 S5 修复并重新验证、复审;
- 会改变业务行为、公共契约、数据库结构、依赖或范围的问题,返回相应审批门;
- 范围外问题只记录,不擅自修改。
只有不存在阻塞级问题,或用户明确接受残余风险时,才能进入 G4。
### S8:提交准备
代码效果经 G4 批准后,使用 `commit-msg` 只读分析真实变更,生成一个或多个逻辑提交建议。然后由总控智能体补充精确暂存和提交命令,进入 G5。
提交拆分以业务目的和依赖关系为准,不按文件类型机械拆分。需求与设计文档如果是本功能交付的一部分,可以和实现一起提交,也可以按项目惯例单独提交,但必须在 G5 明确展示。
### S9:本地提交和交付
G5 批准后:
1. 再次检查 Git 状态;
2. 使用精确路径暂存每个已批准分组;
3. 检查 `git diff --cached --name-status`、`git diff --cached --check` 和暂存差异;
4. 确认无敏感文件、缓存、本地配置和无关改动;
5. 使用批准的提交信息创建本地提交;
6. 验证提交哈希、提交内容、当前分支和提交后工作区状态;
7. 如果本次使用独立 Worktree,确认开发、验证和本地交付已完成,检查 Worktree 干净、无进行中的 Git 操作且 HEAD 已被本地分支引用;
8. 展示准确清理路径、分支、HEAD 和 `git worktree remove` 命令,取得独立确认后使用 `branch close` 移除额外 Worktree;
9. 验证 Worktree 目录和占用记录已移除、分支与提交仍存在、主工作区未变化;
10. 输出最终交付报告。
最终报告至少包含:需求与设计产物路径、分支、提交哈希、实现摘要、验证结果、审核结论、未提交文件、未验证边界和后续建议。
## 7. 阻塞与回退规则
出现下列任一情况时,智能体必须暂停自动推进:
- 用户输入之间存在会改变业务结果的冲突;
- 无法确定项目根目录、基准分支或目标仓库;
- 关键业务规则、权限、状态、金额、时间或数据口径缺失;
- 项目规则要求人工确认或禁止当前操作;
- 工作区已有改动与本次改动重叠且无法安全区分;
- 需要新增依赖、联网下载、访问外部系统、执行数据库变更或使用凭据;
- 需要 fetch、push、合并、标签、发布或历史改写;
- 验证失败表明需求或设计基线需要变化;
- 发现疑似密钥、令牌、私钥、生产配置或个人敏感信息;
- 工具或权限不足,继续执行会让结果无法验证。
暂停时只提出完成决策所需的最少问题,并同时给出已有证据、推荐选项、影响和默认不执行的安全状态。
## 8. 任务台账格式
智能体应维护如下状态,避免长流程丢失上下文:
```markdown
## 当前任务状态
- 任务:<名称>
- 项目根目录:<路径>
- 当前分支:<分支>
- 基准提交:<哈希>
- 当前阶段:S0-S9
- 当前状态:执行中 / 等待审批 / 阻塞 / 已完成
- 已批准审批门:G1、G2……
- 本阶段输入:<文件或对话>
- 本阶段产物:<路径或结论>
- 已修改文件:<精确列表>
- 已执行验证:<命令与结果>
- 待确认项:<列表>
- 风险与未验证项:<列表>
- 下一动作:<唯一明确动作>
```
如果环境支持任务计划或交接文件,可以把上述台账保存在 `.craftkit/local/handoff/current.md`;默认不提交该文件。
## 9. 可直接复制的总控提示词
将以下提示词与原始需求材料一起输入 Codex 或其他已安装 CraftKit 的工具。方括号中的内容按实际情况替换;不知道的内容可以保留为空,由智能体从项目中探测。
```text
你是本次研发任务的总控智能体。请严格遵循项目内 AGENTS.md、CraftKit 各 Skill 的边界,以及《CraftKit 全流程自动化使用指南》。
项目根目录:[项目绝对路径]
原始需求材料:[文件路径、对话内容、会议纪要或问题描述]
期望交付:[功能目标]
明确范围外事项:[可为空]
期望基准分支或引用:[不知道时不要猜测]
文档期望目录:[不知道时先识别项目约定]
执行目标:
根据原始需求和当前项目代码,自动完成需求分析文档落盘、变更规划、适用的技术设计文档、开发分支创建、代码实现、测试验证、代码审核和本地 Git 提交。流程中由你统一维护状态并自动推进;人只负责审批阶段基线、无法从证据确定的业务决策和具有副作用的操作。
执行规则:
1. 开始时读取适用的 AGENTS.md,检查 .craftkit 项目资料、构建文件、Git 状态和相关源码。先保存只读基线,保护现有工作区。
2. 使用 CraftKit 的 requirements 整理原始需求,明确区分输入事实、源码事实、建议、待确认项和待验证项。生成 REQ 编号与可测试验收标准。
3. 到达 G1 时一次性展示需求基线、疑问、建议决策和拟落盘路径,等待我批准。批准后自动执行到下一审批门,不再要求我发送“继续”。
4. 使用 plan-change 和实际适用的设计 Skill 完成设计。只调用与需求有关的 design-db、design-api、design-backend、design-frontend、design-frontend-data、design-workflow、prepare-api、component、form、style,不为不适用领域制造空文档。
5. 到达 G2 时展示设计、影响范围、风险、实施步骤、验证矩阵和文档路径,等待批准。
6. 设计批准后使用 branch 检查并规划本地开发分支。严格按照该 Skill 展示当前状态、分支名、基准提交、创建位置、工作区影响和唯一创建命令,在 G3 等待明确批准。工作区不干净或要求不影响当前 checkout 时使用独立 Worktree。不要自动 fetch、stash、清理、提交或 push。
7. 分支创建后,使用 implement-backend 和/或 implement-frontend 在批准范围内完成最小完整实现。保持项目现有语言、框架版本、目录、注释和测试风格,不虚构接口、组件、业务规则或内部依赖。
8. 自动运行格式化、静态检查、编译、聚焦测试和具备条件的真实验证。本次实现缺陷可自动修复并重跑;需求或设计变化必须回到对应审批门。
9. 使用 review-code 审查最终差异,并按需叠加 review-java、review-mybatis、review-frontend。已批准范围内、低风险且修复方式明确的问题自动修复、复测和复审;范围外或改变契约的问题只报告并等待决策。
10. 到达 G4 时展示实现、REQ 验收映射、验证证据、审核结论、实际差异和残余风险,等待效果审批。
11. 效果批准后使用 commit-msg 生成提交建议,展示精确文件分组、排除项、提交信息以及将执行的 git add -- <明确路径> 和 git commit 命令,在 G5 等待批准。
12. G5 批准后只暂存已展示的精确路径,执行暂存检查并创建本地提交。禁止 git add .、git add -A、push、合并、打标签、发布和历史改写。
13. 每阶段满足质量门后自动进入下一阶段。只有审批门、证据不足、权限缺失、脏工作区冲突、敏感信息或高风险外部操作可以暂停。
14. 每次等待审批时,给出:已完成事项、产物、关键证据、需要批准的明确内容、批准后的自动动作。不要只问“是否继续”。
15. 本地提交核对完成后,如果使用了独立 Worktree,确认生命周期已经结束并进入 branch close:展示准确路径、分支、HEAD、干净状态和唯一移除命令,取得确认后移除,验证分支与提交仍存在且主工作区未变化。
16. 最终报告必须包含文档路径、分支、提交哈希、变更文件、验证结果、审核结论、未提交内容、未验证边界、Worktree 清理结果和后续建议。
现在从 S0 开始执行,并自动推进到 G1。
```
## 10. 审批回复建议
为减少歧义,用户可以使用以下简短格式审批:
```text
批准 G1。需求基线按当前版本执行;待确认项 2 采用方案 B,其余保持范围外。自动推进到 G2。
```
```text
批准 G2。允许按展示的设计和文件范围实现;不允许新增依赖或修改部署配置。自动推进到 G3。
```
```text
批准 G3。执行展示的唯一分支创建命令,随后自动推进到 G4。
```
```text
批准 G4。接受列明的未验证边界,按当前差异准备提交并推进到 G5。
```
```text
批准 G5。只暂存展示的精确文件并创建本地提交;不要 push。
```
如果用户只回复“批准”,智能体只能把它解释为对当前唯一审批门中已完整展示内容的批准,不能扩展为后续审批门、外部系统或高风险操作的预授权。
## 11. 流程完成标准
仅当以下条件全部满足时,任务状态才能标记为“已完成”:
- 需求基线已经批准并落盘;
- 所有适用设计已经批准并落盘;
- 本地开发分支已按批准命令创建;
- 批准范围内的代码已实现;
- 所有可执行验证已完成,阻塞与未验证项已如实记录;
- 代码审核不存在未接受的阻塞问题;
- 代码效果已通过 G4;
- 提交范围与信息已通过 G5;
- 本地提交已创建并核对内容;
- 使用独立 Worktree 时,其生命周期已经结束并安全移除;若用户明确要求保留现场,则任务状态应说明生命周期尚未结束及分支占用路径;
- 未发生未经授权的 push、合并、发布、生产操作或历史改写。
如果由于环境或外部依赖无法完成某项验证,任务只能标记为“本地实现完成,等待外部验证”,不能声称全流程验收通过。
-305
View File
@@ -1,305 +0,0 @@
# Skill 迁移计划
## 1. 目标
将两个本地来源中的通用能力整理为 CraftKit Skill。迁移结果必须适用于 Codex 插件市场,并与来源项目的品牌、内部框架、业务知识和运行环境解耦。
迁移不追求来源 Skill 与目标 Skill 一一对应。重复能力应合并;公司专属实现不得迁移,但应优先将其解决的通用问题重建为中性平替,确无独立价值或无法安全替代时才排除。目标数量以职责清晰和实际复用价值为准。
## 2. 迁移路径
```text
来源发现
→ 风险分类
→ 重复能力合并
→ 中性能力规格
→ 独立实现
→ 行为验证
→ 敏感内容扫描
→ Codex 与插件校验
→ 更新迁移状态
```
禁止采用“完整复制来源目录后批量替换名称”的方式。中性能力规格形成后,目标实现应基于该规格、当前 Codex 能力和公开资料完成。
## 3. 状态模型
| 状态 | 含义 | 进入条件 |
| --- | --- | --- |
| `pending` | 待评估 | 已发现来源 Skill |
| `specified` | 已完成中性规格 | 已明确目标、边界、依赖和排除内容 |
| `rewriting` | 独立实现中 | 已创建目标 Skill |
| `review` | 待复核 | 功能完成,但版权、依赖或行为仍需确认 |
| `migrated` | 已迁移 | 全部门禁通过 |
| `excluded` | 不迁移 | 公司专属或没有独立价值 |
| `superseded` | 已合并 | 能力已由另一个目标 Skill 覆盖 |
## 4. 迁移批次
迁移分为“特殊样本”和“同质批量”两个阶段。前者用于覆盖迁移机制的不同风险类型,后者在规则稳定后提高吞吐量。批次数量不固定:特殊样本原则上逐个迁移,批量阶段每批建议 6~12 个同质 Skill。
### 第 0 批:迁移基础设施
第 0 批属于仓库内部维护工具,不发布为市场 Skill:
1. `migration/scripts/scan_sources.py`:只读发现来源 Skill、辅助资源和文件哈希。
2. `AGENTS.md` 迁移工作流:完成分类、目标设计和用户确认。
3. `migration/scripts/check_skill.py`:检查结构、引用、占位符、敏感内容和凭据风险。
4. `migration/scripts/update_lock.py`:预览或更新来源指纹和迁移状态。
完成门槛:能够对全部来源 Skill 生成稳定清单,并在不复制来源内容的情况下更新状态。
### 第 1 阶段:特殊样本
特殊样本不追求模块连续性,而是覆盖不同迁移机制:
1. `doc/format-md`:纯指令型样本,已完成。
2. `doc/docx-to-md`:多来源合并、脚本、依赖和生成物样本,已完成。
3. `git/commit-msg`:只读 Git 状态分析样本,已完成。
4. `git/branch`:修改仓库状态和二次授权边界样本,已完成。
5. `knowledge/handoff`:模板化文档与项目上下文样本,已完成。
6. `skill/guidance`:大型参考资料、索引和渐进式加载样本,已完成;只检索项目资料和基于公开一级资料独立重建的中性公共基线。
7. `knowledge/init`:成熟项目与空项目双模式初始化、参考项目提炼和共享/本地信息边界样本,已完成。
8. `skill/migrate`:不迁移。来源能力用于把 Claude Code Skill 转换为 Codex Skill;CraftKit 自始按 Codex 规范开发,不存在平台转换需求。来源扫描、目标检查和状态追踪继续由 `migration/scripts/` 作为仓库维护设施承担。
完成门槛:适用样本均通过对应验证,不适用样本记录排除依据,并形成可复用的命名、目录、独立实现、测试、扫描和状态同步规则。出现未覆盖的新结构或权限类型时,应补充样本,不直接扩批。当前特殊样本阶段已完成,可以进入同质批量迁移。
### 第 2 阶段:同质批量迁移
特殊样本完成后,按同一插件、相近能力和相同风险类型组织批量迁移:
- 每批建议 6~12 个 Skill,高度同质时可以整组处理。
- 不把只读能力与有副作用能力、纯指令与复杂脚本、普通迁移与公开资料重建强行合为一批。
- 每批仍需迁移前确认和提交前确认,并统一更新 README、迁移计划和来源状态。
- 任一验收门禁失败时暂停该批,不继续扩大范围。
### 文档转换批次
所属插件:`doc`
1. `docx-to-md`
2. `md-to-docx`(已完成)
3. `xlsx-to-md`(已完成)
4. `archive`(已完成,以可配置规则平替固定目录、业务文件名、专有章节拆分和内部系统校验)
重点验证图片、表格、合并单元格、编码、覆盖策略和路径安全。脚本及测试数据必须独立创建。
### Git 工作流批次
所属插件:`git`
1. `branch`(已完成)
2. `identity`(已完成,只管理 Git 提交用户名和邮箱)
3. `export`(已完成,环境配置默认排除,规范明确要求并再次确认后才可导出)
4. `integrate`(已完成,按项目规范通过隔离 worktree 评估、准备和发布预集成分支)
`commit-msg` 将作为只读 Git 特殊样本先行完成。涉及提交、合并和远端操作的 Skill 必须保留明确授权边界,并保护脏工作区。
### 知识管理批次
所属插件:`knowledge`
1. `trace`(已完成)
2. `distill`(已完成)
3. `lessons`(已完成,合并初始化、维护、审计和提升)
4. `worklog`(已完成)
5. `init`(已完成)
来源中与经验初始化、提升和回扫相关的多个能力统一合并为 `maintain-lessons`,通过模式区分具体工作。
### 开发主流程批次
所属插件:`dev`
```text
plan-change
├─ design-backend → implement-backend → test-backend
└─ design-frontend → implement-frontend → test-ui
↓
review-code
```
建议顺序:
1. `plan-change`(已完成)
2. `design-backend`(已完成)
3. `design-frontend`(已完成)
4. `prepare-api`(已完成)
5. `implement-backend`(已完成)
6. `implement-frontend`(已完成)
7. `review-code`(已完成)
8. `test-backend`(已完成)
9. `test-ui`(已完成)
10. `analyze-bugs`(已完成)
多个来源中的代码检查、代码审查能力合并为 `review-code`,通过工作区、提交和分支三种模式覆盖。
### 项目规范与前端辅助批次
1. `dev/component`(已完成,基于项目证据完成组件选型与契约查证)
2. `dev/style`(已完成,基于项目视觉基线设计样式)
3. `dev/form`(已完成,设计表单结构、响应布局和可访问性)
4. `skill/guidance`(特殊样本已完成)
5. `skill/guidance-edit`(已完成,建立、检查和维护项目规范索引)
这里只实现读取和维护“当前项目自身规范”的机制,不随 CraftKit 提供任何来源项目规范。
原前端导航能力由上述 Skill 的精确触发描述和 Codex 自动发现替代,不再维护独立路由 Skill。混合任务按 `style` → `component` → `form` → 前端设计或实现的依赖顺序处理。
### 基于公开资料重建批次
以下能力不从来源文本改写,而是根据官方资料重新设计:
1. `dev/design-api`(已完成)
2. `dev/design-db`(已完成)
3. `dev/review-java`(已完成)
4. `dev/review-frontend`(已完成)
5. `dev/design-frontend-data`(已完成)
6. `dev/review-mybatis`(已完成)
7. `dev/design-workflow`(已完成)
中性规格必须记录所采用的公开标准、官方文档和许可证信息。
### 平替或排除结果
- 公司框架知识与专属规范由 `skill/guidance`、项目源码和 `.craftkit/standards/` 平替。
- 内部组件契约、审批规则和消息格式不迁移,目标 Skill 只读取项目证据和用户输入。
- 升级、估算与发布已重建为通用能力,不携带内部版本矩阵和仓库流程。
- 浏览器代理配置由 Codex 浏览器或计算机操作能力平替。
- Skill 路由依靠精确触发描述和 Codex 自动发现,不增加独立路由器。
- 非 Codex 平台转换能力保持排除。
### 剩余迁移波次
截至 2026-08-26,原有 29 个 `pending` 来源已在一个连续波次内全部处理完成。所有来源均已闭合为 `migrated`、`superseded` 或 `excluded`;本波次完成验证后统一等待本地提交确认。
#### 阶段 1:公开基线与专项设计
| 来源能力 | 目标 Skill | 处理方式 |
| --- | --- | --- |
| API 约定 | `dev/design-api` | 基于 HTTP、OpenAPI 等官方资料独立重建 |
| 数据库约定 | `dev/design-db` | 基于目标数据库官方资料和项目证据独立重建 |
| Java 约定 | `dev/review-java` | 基于 Java 与所用框架对应版本官方资料独立重建 |
| 前端约定 | `dev/review-frontend` | 基于当前框架版本官方资料独立重建 |
| 前端数据约定 | `dev/design-frontend-data` | 建立请求、状态与视图模型的通用设计边界 |
| MyBatis 约定 | `dev/review-mybatis` | 基于 MyBatis 官方资料独立重建 |
| 工作流约定 | `dev/design-workflow` | 以项目工作流契约和用户输入平替内部审批规则 |
公开资料只采用一级官方来源,记录适用版本、重建日期和许可证或引用边界;不得改写来源规范正文。
#### 阶段 2:代码实现
| 来源 Skill | 目标 Skill | 状态关系 |
| --- | --- | --- |
| `back-code`、同类后端编码来源 | `dev/implement-backend` | 两个来源合并,一个迁移、一个 `superseded` |
| `front-code`、同类前端编码来源 | `dev/implement-frontend` | 两个来源合并,一个迁移、一个 `superseded` |
实现 Skill 直接修改业务源码,必须保护脏工作区、使用项目真实版本与规范、执行风险相称的验证,且不自动提交或推送。
#### 阶段 3:审查、测试与问题分析
| 来源能力 | 目标 Skill | 处理方式 |
| --- | --- | --- |
| 两套代码检查与一套代码审查 | `dev/review-code` | 合并为工作区、提交和分支审查模式 |
| 后端单元测试 | `dev/test-backend` | 按当前测试框架和项目模式生成或修改测试 |
| UI 测试 | `dev/test-ui` | 按用户可观察行为设计并执行浏览器测试 |
| Bug 列表分析 | `dev/analyze-bugs` | 解析通用结构化问题清单并形成证据化报告 |
本阶段不得把静态检查、单元测试、浏览器测试和真实环境验收混为同一结论。
#### 阶段 4:交付、升级与估算
| 来源能力 | 目标 Skill 或处理结果 | 处理方式 |
| --- | --- | --- |
| 后端升级、前端升级 | `dev/upgrade` | 合并为基于源版本、目标版本和官方迁移资料的通用升级流程 |
| 版本发布 | `git/release` | 只准备版本、变更摘要、标签和发布检查;远端动作单独授权 |
| 工作量评估 | `dev/estimate` | 基于范围、依赖、风险和假设输出区间估算 |
| 框架约定 | `skill/guidance` | `superseded`,内部规则由项目 `.craftkit/standards/` 提供 |
| 框架知识查询 | `skill/guidance` | `superseded`,改为检索项目证据和公开官方资料 |
#### 阶段 5:写作与平台工具收尾
| 来源能力 | 目标 Skill 或处理结果 | 处理方式 |
| --- | --- | --- |
| 领导汇报 | `doc/report` | 中性化为面向不同受众的事实型工作汇报 |
| 消息模板 | `doc/message` | 中性化为项目消息与通知草稿 |
| 技术内容转需求 | `doc/requirements` | 将技术输入转换为可确认的需求说明 |
| 能力创建器 | 官方 `skill-creator` | `superseded`,不重复发布同类 CraftKit Skill |
| 浏览器代理配置 | Codex 浏览器或计算机操作能力 | `superseded`,不迁移宿主专属代理配置 |
| Skill 转 Cursor | 无 | `excluded`,CraftKit 只面向 Codex |
#### 波次总门禁
1. 29 个来源全部更新为 `migrated`、`superseded` 或 `excluded`,不得遗留 `pending`。
2. 所有目标 Skill 通过结构、引用、敏感内容和真实请求边界测试。
3. 公开基线记录官方来源、适用版本和重建边界。
4. 有脚本的 Skill 完成隔离行为测试;有外部工具的 Skill 明确权限和未验证边界。
5. README、插件版本、迁移计划和来源锁同步一致。
6. 全量测试、JSON 校验和 `git diff --check` 通过。
7. 波次完成后只展示提交方案,不创建提交;用户统一确认后按逻辑范围创建本地提交。
## 5. 合并原则
来源数量不等于目标数量。优先执行以下合并:
| 来源能力类型 | 目标 Skill |
| --- | --- |
| 代码检查、提交审查、分支审查 | `review-code` |
| 多套开发计划 | `plan-change` |
| 多套后端设计 | `design-backend` |
| 多套前端设计 | `design-frontend` |
| 多套后端实现 | `implement-backend` |
| 多套前端实现 | `implement-frontend` |
| 多套 Word 转 Markdown | `docx-to-md` |
| 经验初始化、提升、回扫 | `maintain-lessons` |
| 规范检索和索引维护 | `guidance`、`guidance-edit` |
目标 Skill 总量不设硬指标,预期控制在约 30 个,避免细碎能力和重复触发。
## 6. 单个 Skill 的迁移步骤
1. 记录来源相对路径、提交和 SHA-256。
2. 判断该能力是迁移、合并、重建还是排除。
3. 只提炼目标、输入、输出、关键边界和真实用例,形成中性规格。
4. 确定目标插件和简短 Skill 名称。
5. 基于中性规格和公开资料独立实现。
6. 对脚本执行单元或行为测试,对文档型 Skill 执行真实请求测试。
7. 扫描敏感内容、来源残留、无效工具名和宿主绑定表达。
8. 运行 Skill 与插件校验。
9. 将状态更新为 `migrated`,记录目标路径、目标版本和复核日期。
10. 展示迁移结果、验证证据、待提交文件和建议提交信息,等待用户再次确认。
11. 用户确认后精确暂存本批文件并创建本地提交;推送和发布仍需独立授权。
## 7. 每批验收门禁
- Skill 名称简洁,目录名与 frontmatter 一致。
- `description` 能准确触发,不是大而全的能力描述。
- 不存在来源正文、脚本、模板、示例或独特结构的直接复制。
- 公司标识、内部域名、包名、路径和人员信息扫描为零。
- 所有引用文件存在,脚本和生成物经过实际验证。
- Skill 校验和所属插件校验通过。
- 至少一个真实请求用例通过。
- `source-lock.json` 已更新。
任一门禁未通过时,不进入下一批的大规模迁移。
## 8. 当前执行顺序
- [x] 扩展 `source-lock.json` 的 Skill 级记录结构。
- [x] 实现来源只读扫描脚本。
- [x] 在 `AGENTS.md` 中固化分类、设计和确认流程。
- [x] 实现 Skill 与敏感内容检查脚本。
- [x] 实现迁移台账预览和更新脚本。
- [x] 迁移并验证首个样板 `doc/format-md`。
- [x] 迁移并验证脚本型特殊样本 `doc/docx-to-md`。
- [x] 迁移并验证只读 Git 特殊样本 `git/commit-msg`。
- [x] 迁移并验证有副作用 Git 特殊样本 `git/branch`。
- [x] 迁移并验证模板化交接特殊样本 `knowledge/handoff`。
- [x] 完成其余特殊样本并总结批量迁移规则。
- [ ] 按插件和风险类型继续推进同质批量迁移。
- [x] 完成首个同质批量:`md-to-docx`、`xlsx-to-md`、`archive`。
- [x] 完成 Git 本地操作批次:`identity`、`export`。
- [x] 完成 Git 高风险隔离集成样本:`integrate`。
- [x] 完成知识管理批次:`trace`、`distill`、`lessons`、`worklog`。
- [x] 完成前端辅助批次:`component`、`style`、`form`,并以精确触发替代独立路由。
- [x] 完成项目规范维护能力:`guidance-edit`。
- [x] 完成开发分析与设计批次:`plan-change`、`design-backend`、`design-frontend`、`prepare-api`。
-23
View File
@@ -1,23 +0,0 @@
# 迁移追踪说明
本目录只记录来源指纹、迁移状态和人工复核结果,不存放或打包公司插件内容。
- [迁移计划](MIGRATION_PLAN.md):批次顺序、依赖、合并策略与验收门禁。
- `source-lock.json`:可提交的来源指纹和迁移状态。
- `local-sources.json`:本地来源路径,不提交;结构参考 `local-sources.example.json`。
- `scripts/scan_sources.py`:只读生成来源 Skill 指纹清单。
- `scripts/check_skill.py`:检查迁移后 Skill 的结构、引用和敏感内容。
- `scripts/update_lock.py`:根据扫描报告预览或更新迁移台账。
- `tests/`:迁移脚本的隔离行为测试。
扫描报告可能包含来源 Skill 名称和相对路径,应写入本地临时目录或忽略文件,不进入插件发布内容。
同步流程:
1. 读取来源仓库当前提交与 Skill 文件哈希。
2. 与 `source-lock.json` 比较,生成新增、修改、删除清单。
3. 人工判断变化是否属于可公开迁移的通用能力。
4. 在 CraftKit 中独立重写并完成验证。
5. 更新来源指纹、目标路径、状态和复核日期。
允许的状态:`pending`、`specified`、`rewriting`、`review`、`migrated`、`excluded`、`superseded`。
-4
View File
@@ -1,4 +0,0 @@
{
"source-a": "D:/path/to/local/source-a",
"source-b": "D:/path/to/local/source-b"
}
-79
View File
@@ -1,79 +0,0 @@
#!/usr/bin/env python3
"""检查 CraftKit Skill 的基础结构、引用和敏感内容。"""
from __future__ import annotations
import argparse
import json
import re
from pathlib import Path
NAME_RE = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
LINK_RE = re.compile(r"\[[^]]+]\((?!https?://|#)([^)]+)\)")
SECRET_RE = re.compile(
r"(?i)(api[_-]?key|password|private[_-]?key|access[_-]?token)\s*[:=]\s*[^\s]+"
)
def frontmatter(text: str) -> dict[str, str]:
"""解析当前校验所需的简单顶层 frontmatter 字段。"""
if not text.startswith("---\n"):
return {}
end = text.find("\n---", 4)
if end < 0:
return {}
values: dict[str, str] = {}
for line in text[4:end].splitlines():
if ":" in line and not line.startswith((" ", "\t")):
key, value = line.split(":", 1)
values[key.strip()] = value.strip().strip("'\"")
return values
def check_skill(root: Path, forbidden: list[str]) -> dict:
"""返回结构化问题列表,不修改被检查目录。"""
issues: list[dict[str, str]] = []
skill_md = root / "SKILL.md"
if not skill_md.is_file():
return {"skill": root.name, "issues": [{"code": "missing-skill-md", "path": "SKILL.md"}]}
text = skill_md.read_text(encoding="utf-8-sig")
metadata = frontmatter(text)
name = metadata.get("name", "")
if not NAME_RE.fullmatch(name):
issues.append({"code": "invalid-name", "path": "SKILL.md"})
if name != root.name:
issues.append({"code": "name-path-mismatch", "path": "SKILL.md"})
if not metadata.get("description"):
issues.append({"code": "missing-description", "path": "SKILL.md"})
for path in sorted(item for item in root.rglob("*") if item.is_file()):
relative = path.relative_to(root).as_posix()
content = path.read_text(encoding="utf-8-sig", errors="replace")
if "[TODO:" in content:
issues.append({"code": "todo-placeholder", "path": relative})
if SECRET_RE.search(content):
issues.append({"code": "possible-secret", "path": relative})
lowered = content.casefold()
for term in forbidden:
if term.casefold() in lowered:
issues.append({"code": f"forbidden:{term}", "path": relative})
if path.suffix.lower() == ".md":
for link in LINK_RE.findall(content):
target = link.split("#", 1)[0]
if target and not (path.parent / target).resolve().is_file():
issues.append({"code": "broken-link", "path": f"{relative}:{link}"})
return {"skill": root.name, "issues": issues}
def main() -> None:
parser = argparse.ArgumentParser(description="检查 CraftKit Skill")
parser.add_argument("skill", nargs="+", type=Path)
parser.add_argument("--forbid", action="append", default=[])
args = parser.parse_args()
results = [check_skill(path.resolve(), args.forbid) for path in args.skill]
print(json.dumps({"results": results}, ensure_ascii=False, indent=2))
raise SystemExit(1 if any(item["issues"] for item in results) else 0)
if __name__ == "__main__":
main()
-111
View File
@@ -1,111 +0,0 @@
#!/usr/bin/env python3
"""只读扫描来源目录中的 Skill,并输出稳定的 JSON 清单。"""
from __future__ import annotations
import argparse
import hashlib
import json
import subprocess
from datetime import datetime, timezone
from pathlib import Path
def file_hash(path: Path) -> str:
"""按二进制内容计算单文件 SHA-256。"""
digest = hashlib.sha256()
with path.open("rb") as stream:
for block in iter(lambda: stream.read(65536), b""):
digest.update(block)
return digest.hexdigest()
def directory_hash(root: Path) -> tuple[str, int]:
"""把相对路径和文件内容共同纳入哈希,确保结构变化也可被识别。"""
digest = hashlib.sha256()
files = sorted(path for path in root.rglob("*") if path.is_file())
for path in files:
relative = path.relative_to(root).as_posix()
digest.update(relative.encode("utf-8"))
digest.update(b"\0")
digest.update(bytes.fromhex(file_hash(path)))
return digest.hexdigest(), len(files)
def read_skill_name(skill_md: Path) -> str:
"""从 YAML frontmatter 中读取 name;解析失败时回退为目录名。"""
for line in skill_md.read_text(encoding="utf-8-sig").splitlines():
if line.startswith("name:"):
return line.split(":", 1)[1].strip().strip("'\"") or skill_md.parent.name
return skill_md.parent.name
def git_commit(root: Path) -> str | None:
"""尽力读取来源提交;非 Git 目录时返回空值,不阻塞文件扫描。"""
result = subprocess.run(
["git", "-c", f"safe.directory={root.as_posix()}", "-C", str(root), "rev-parse", "HEAD"],
capture_output=True,
text=True,
check=False,
)
return result.stdout.strip() if result.returncode == 0 else None
def scan_source(source_id: str, root: Path) -> dict:
"""扫描单个来源,结果仅包含相对路径和指纹,不包含本地绝对路径。"""
if not root.is_dir():
raise ValueError(f"来源目录不存在:{root}")
skills = []
for skill_md in sorted(root.rglob("SKILL.md")):
skill_root = skill_md.parent
digest, file_count = directory_hash(skill_root)
children = sorted(
child.name for child in skill_root.iterdir() if child.is_dir()
)
skills.append(
{
"name": read_skill_name(skill_md),
"relativePath": skill_root.relative_to(root).as_posix(),
"sha256": digest,
"fileCount": file_count,
"resources": children,
}
)
return {
"id": source_id,
"commit": git_commit(root),
"skillCount": len(skills),
"skills": skills,
}
def parse_source(value: str) -> tuple[str, Path]:
"""解析 id=path 参数,避免把本地路径写入输出文件。"""
if "=" not in value:
raise argparse.ArgumentTypeError("来源参数必须使用 id=path 格式")
source_id, raw_path = value.split("=", 1)
if not source_id.strip() or not raw_path.strip():
raise argparse.ArgumentTypeError("来源标识和路径均不能为空")
return source_id.strip(), Path(raw_path).expanduser().resolve()
def main() -> None:
parser = argparse.ArgumentParser(description="只读扫描 Skill 来源目录")
parser.add_argument("--source", action="append", required=True, type=parse_source)
parser.add_argument("--output", type=Path, help="可选 JSON 输出路径;省略时输出到终端")
args = parser.parse_args()
payload = {
"schemaVersion": 1,
"generatedAt": datetime.now(timezone.utc).isoformat(),
"sources": [scan_source(source_id, root) for source_id, root in args.source],
}
content = json.dumps(payload, ensure_ascii=False, indent=2) + "\n"
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(content, encoding="utf-8")
else:
print(content, end="")
if __name__ == "__main__":
main()
-65
View File
@@ -1,65 +0,0 @@
#!/usr/bin/env python3
"""根据扫描报告更新迁移台账;默认预览,显式 --apply 才写入。"""
from __future__ import annotations
import argparse
import hashlib
import json
from datetime import date
from pathlib import Path
VALID_STATUS = {
"pending", "specified", "rewriting", "review",
"migrated", "excluded", "superseded",
}
def merge(lock: dict, report: dict) -> dict:
"""保留人工维护字段,仅同步来源提交、数量和 Skill 指纹。"""
lock.setdefault("schemaVersion", 1)
lock["updatedAt"] = date.today().isoformat()
lock.setdefault("sources", {})
lock.setdefault("skills", {})
for source in report.get("sources", []):
source_id = source["id"]
lock["sources"][source_id] = {
"commit": source.get("commit"),
"skillCount": source["skillCount"],
}
for skill in source["skills"]:
# 发布台账不保存来源名称和路径;本地扫描报告负责提供可读映射。
path_hash = hashlib.sha256(skill["relativePath"].encode("utf-8")).hexdigest()
key = f"{source_id}:{path_hash[:16]}"
current = lock["skills"].get(key, {})
status = current.get("status", "pending")
if status not in VALID_STATUS:
raise ValueError(f"非法迁移状态:{key}={status}")
lock["skills"][key] = {
**current,
"sourcePathHash": path_hash,
"sourceSha256": skill["sha256"],
"status": status,
}
return lock
def main() -> None:
parser = argparse.ArgumentParser(description="更新 Skill 迁移台账")
parser.add_argument("--lock", required=True, type=Path)
parser.add_argument("--report", required=True, type=Path)
parser.add_argument("--apply", action="store_true", help="确认写入台账")
args = parser.parse_args()
lock = json.loads(args.lock.read_text(encoding="utf-8"))
report = json.loads(args.report.read_text(encoding="utf-8"))
content = json.dumps(merge(lock, report), ensure_ascii=False, indent=2) + "\n"
if args.apply:
temporary = args.lock.with_suffix(args.lock.suffix + ".tmp")
temporary.write_text(content, encoding="utf-8")
temporary.replace(args.lock)
else:
print(content, end="")
if __name__ == "__main__":
main()
-538
View File
@@ -1,538 +0,0 @@
{
"schemaVersion": 1,
"updatedAt": "2026-08-26",
"sources": {
"source-a": {
"commit": "31883f7e81c2bdb19505e591ba9db8a3d9fd4352",
"skillCount": 12
},
"source-b": {
"commit": "2947a163538cd119a4e241c9d36f061d6dcae841",
"skillCount": 52
}
},
"skills": {
"source-a:2c317f4ba8859f5e": {
"sourcePathHash": "2c317f4ba8859f5e210c18e69e99e8e472d38d97e193c5f213508d4afb672c11",
"sourceSha256": "ed08a64bf0f5004d43a9c09a40087aebec8f52323c5a47d8fc73f9f9aca9be0e",
"status": "migrated",
"target": "plugins/dev/skills/review-code",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-a:1b7843684955ce5a": {
"sourcePathHash": "1b7843684955ce5a0beb3ef16c6e751c5e9eb5a17409084fed806b74ec0c522f",
"sourceSha256": "eabf9fabba7b751a0b85547b141d92821f461e8d904f4fb15d5746eca1965769",
"status": "migrated",
"target": "plugins/dev/skills/plan-change",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-a:5d76364c580248a5": {
"sourcePathHash": "5d76364c580248a54449e5ec7ede0079f19df21fef95922d3538fb82c095b181",
"sourceSha256": "9a664d17f276b01311f661aa6e8fd40063708550c97cc3340246922194d8390b",
"status": "migrated",
"target": "plugins/dev/skills/implement-backend",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-a:a0b791b6dac96b81": {
"sourcePathHash": "a0b791b6dac96b8180df558278b5c7ef643b4806cc4e534ffe6395b9236666d3",
"sourceSha256": "603b4fd6e4b52c9b5d368c5ba6bdff333d32f3e03e9b5393686b0729eb4ffc7c",
"status": "migrated",
"target": "plugins/dev/skills/design-backend",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-a:0124493991019160": {
"sourcePathHash": "0124493991019160a06c65d6200db0a50abb212a79bcefab4be4631be85c07c8",
"sourceSha256": "eca051fb929c5bd74c34564631872c2d6755b4427a7c4fbb4d6240eeb6331ee3",
"status": "migrated",
"target": "plugins/dev/skills/prepare-api",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-a:b80c5913e146321c": {
"sourcePathHash": "b80c5913e146321ca5efef5e45f11172e79c542855c2ae65478fe253414e66ea",
"sourceSha256": "5e33d979bb08fdcc47b2dee2e0d5cb39c24825b391f8bbf5606e57cbd4c2ef2c",
"status": "migrated",
"target": "plugins/dev/skills/implement-frontend",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-a:cd36505dd718e889": {
"sourcePathHash": "cd36505dd718e88966ad74421c65e6c7cccd71a1436f2843eb8cfa7a4f1534c5",
"sourceSha256": "a0137bd615988c49a8967b4e0129618858268b90391ab2ed912cc72f338f41f8",
"status": "migrated",
"target": "plugins/dev/skills/design-frontend",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-a:e14c1a3cd3b041f6": {
"sourcePathHash": "e14c1a3cd3b041f650d71eb5701c71c0de76cb7a909e9a419f1d277159cd763a",
"sourceSha256": "7780de4d979a6bafd1f5f503f08261858907f110acddb630dfab36d32e54c6a8",
"status": "superseded",
"target": "official:skill-creator",
"reason": "Codex 官方 skill-creator 已覆盖通用 Skill 创建与更新能力",
"reviewedAt": "2026-08-26"
},
"source-a:fdde0da43c1ec4c7": {
"sourcePathHash": "fdde0da43c1ec4c7fddad96b9172435af6be96ceafa2cc824f70e04a3cf11085",
"sourceSha256": "46a02dbff9c3fff57a0802853e2424983927f53c7f1cc3d8a42542403e62a161",
"status": "migrated",
"target": "plugins/doc/skills/docx-to-md",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-a:4efb6f9dd0f39dff": {
"sourcePathHash": "4efb6f9dd0f39dff13339e62ff24b672552efcbcedb007e9ea86fe41950adbb0",
"sourceSha256": "a914823bdf50d9e5c8ee70d6363c5c0e7aea341ed390587a9285c70a7047558e",
"status": "migrated",
"target": "plugins/doc/skills/xlsx-to-md",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-a:32e7cfc21d252985": {
"sourcePathHash": "32e7cfc21d25298559bffbe7f0918f0f6e7d2aac29e3eca39d1f43d8226cf934",
"sourceSha256": "0f0486e3f25279a1a1cb8cecfef4dc01830471b5d31b52a9d17a425919997bc2",
"status": "excluded",
"reason": "CraftKit 原生面向 Codex,不需要 Claude Code 到 Codex 的平台转换能力",
"reviewedAt": "2026-08-25"
},
"source-a:bc91b57fa2a50049": {
"sourcePathHash": "bc91b57fa2a500498b31b0f1a87dfa8ab83c32e4b361b4ea1614ea6721234b7c",
"sourceSha256": "1436c7e25325c927d55d1dfc2a3117ec8e30845d66e06d2e6d7cc75abe4e0792",
"status": "excluded",
"reason": "CraftKit 仅面向 Codex,不增加 Cursor 平台转换能力",
"reviewedAt": "2026-08-26"
},
"source-b:3fb4282b0b034d52": {
"sourcePathHash": "3fb4282b0b034d5286bccd9924c37bedbe857c4673798aa844f540fe0ee5186a",
"sourceSha256": "196cd701c67d06fce4ee3b9894cbed95e6bc9101cc2089f83ae8f35b61a11332",
"status": "migrated",
"target": "plugins/git/skills/release",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:0dedfcd01cd89e28": {
"sourcePathHash": "0dedfcd01cd89e28e83d841f78c270b1210399cc867fd3e1865c14acf1ea865a",
"sourceSha256": "be6a694d4de9b8a792e19e1ed116e2fdd54d91731d9efcadfca1b0055a4a3145",
"status": "superseded",
"target": "plugins/dev/skills/implement-backend",
"targetVersion": "0.1.0",
"reason": "后端实现能力已与另一来源合并并按项目证据中性重建",
"reviewedAt": "2026-08-26"
},
"source-b:3f05784a0a86ba5f": {
"sourcePathHash": "3f05784a0a86ba5fb8f268797ca1ceabe8ca237b3c9d3c4396e622c1ef421cc6",
"sourceSha256": "9a39cd7af138519fe3de3f7a3cb031ea5da1243ff5779504a13215fa03a5a3c2",
"status": "superseded",
"target": "plugins/dev/skills/design-backend",
"targetVersion": "0.1.0",
"reason": "后端设计能力已与另一来源合并并按项目证据中性重建",
"reviewedAt": "2026-08-25"
},
"source-b:41934e94a1922a1b": {
"sourcePathHash": "41934e94a1922a1bbccec4377246e67bd84f1ffbd2b9d1bf4027c1ed15d12260",
"sourceSha256": "a0db48d88dab0537e061146edf6dbdeb4b7f8a935febd1556f798ef234b29e5b",
"status": "migrated",
"target": "plugins/dev/skills/test-backend",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:0cfb12fd60357a34": {
"sourcePathHash": "0cfb12fd60357a346c8e0079536540fba81e406d58205098f1a5162c6ce7e9d1",
"sourceSha256": "aa722e0dbcb613748b75bc2f5c81b8a97cbff07905b1fcee6d58cb176135fc08",
"status": "superseded",
"target": "plugins/dev/skills/review-code",
"targetVersion": "0.1.0",
"reason": "代码检查能力已合并到统一代码审查入口",
"reviewedAt": "2026-08-26"
},
"source-b:f171b05e239c2e2c": {
"sourcePathHash": "f171b05e239c2e2c5f2a0c636dfac0c2456b9d3b5215fdaa7fb86a33be1ee188",
"sourceSha256": "cd16d17204009509e6798badab786b9bd4d3969117b3ad4b9005337b76a4791b",
"status": "superseded",
"target": "plugins/dev/skills/plan-change",
"targetVersion": "0.1.0",
"reason": "开发计划能力已与另一来源合并为通用变更计划",
"reviewedAt": "2026-08-25"
},
"source-b:3b29e4441f642e65": {
"sourcePathHash": "3b29e4441f642e65d442fd284f37a45364cebb7a6c53446c354d15c70e532410",
"sourceSha256": "d2689d3ac45070389473cb8cc414df44e82ad267915d8cd7c53c023f5b8f3696",
"status": "superseded",
"target": "plugins/dev/skills/implement-frontend",
"targetVersion": "0.1.0",
"reason": "前端实现能力已与另一来源合并并按项目证据中性重建",
"reviewedAt": "2026-08-26"
},
"source-b:f6ec7bd5501e6ea5": {
"sourcePathHash": "f6ec7bd5501e6ea5f6e907a586a8d7cbe5fbbeebd3e5c8efead9b250742a148e",
"sourceSha256": "25905b3b294b0be2e245099acb1a557793215f157b96b9e964e78170838652f0",
"status": "migrated",
"target": "plugins/dev/skills/component",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:c49d1db42acd7220": {
"sourcePathHash": "c49d1db42acd722071d61420be58ba8ccbc54f5370bdf951abfb5981914271cf",
"sourceSha256": "20875df87fb84c7d5830d6eaaff544c9e32e3f9a2359ed099de5f3a4a36eaa7d",
"status": "superseded",
"target": "plugins/dev/skills/design-frontend",
"targetVersion": "0.1.0",
"reason": "前端设计能力已与另一来源合并并移除内部组件与固定版本约束",
"reviewedAt": "2026-08-25"
},
"source-b:9a3f3d3c43d9017a": {
"sourcePathHash": "9a3f3d3c43d9017a080a9832df5b975e6ddd3f8c39947ee768aeb973fc23974e",
"sourceSha256": "47c072e792d1cf80ddcc69536236ccb525d21e7d5408ed41cca55483556e8acb",
"status": "superseded",
"target": "plugins/dev/skills",
"reason": "前端导航能力已由 component、style、form 的精确触发描述和 Codex 自动发现替代",
"reviewedAt": "2026-08-25"
},
"source-b:eb730c56bc7a4a15": {
"sourcePathHash": "eb730c56bc7a4a158ddec74c7497a10fdea2d0b628783b968e3ee9ee4572b15c",
"sourceSha256": "89aa46526fa7531958df46be4b162a9f7b9ea597e3e9121991cf25e9f25158f5",
"status": "migrated",
"target": "plugins/skill/skills/guidance",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:422174b0c00bbd65": {
"sourcePathHash": "422174b0c00bbd657f4c08415d4a722584556cfa5f3edc997b11e8ab0ef8656a",
"sourceSha256": "017a4ca0b7648ed91003deeb97a3757e8f17acd6963c6019140389a907f1345a",
"status": "migrated",
"target": "plugins/dev/skills/style",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:742ba3611bee4e34": {
"sourcePathHash": "742ba3611bee4e34dfee6dadbe192dd8be677c4b9f49108e2f04d9e22dc8801f",
"sourceSha256": "7582e333966b45ee2d145601831449d75c4f3159670c31df25e5d18e6a357bbe",
"status": "migrated",
"target": "plugins/dev/skills/test-ui",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:d6d4166800584a61": {
"sourcePathHash": "d6d4166800584a61ad6cbf96af57bb8ee269a16babba753f267b48efd66f6d2d",
"sourceSha256": "60f8708db24f368ecec55bd3f326683fac60bd6776ac69e0fafaf4c77333e703",
"status": "migrated",
"target": "plugins/doc/skills/docx-to-md",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:023767d9c7be6295": {
"sourcePathHash": "023767d9c7be62956d12fd01c94ac676b9fe6719741acd6f412c8b446efa2760",
"sourceSha256": "ba579f8d6a62b4745d8d3922b33e3c20974d8081ae4d1c87727d1a9e4b609945",
"status": "migrated",
"target": "plugins/doc/skills/format-md",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:b127c47139270a7f": {
"sourcePathHash": "b127c47139270a7f9157e18ad6a0c975b99caf07b9a0874daeac86d9a99ce48c",
"sourceSha256": "0214b80071f07beaff4f5837b983546ff71dbaa65bc29e34a5a23c9d946b683b",
"status": "migrated",
"target": "plugins/doc/skills/md-to-docx",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:34ab291ce3cfed2e": {
"sourcePathHash": "34ab291ce3cfed2e69cc6f08309b1a41142be91235b9a573daf98639b4a001cc",
"sourceSha256": "239d7efb1f9a53fc1b40f367954a6f6eaaf9d7a56cdf712128dcf393dc5a5067",
"status": "migrated",
"target": "plugins/dev/skills/design-api",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:3c8596a5d95de214": {
"sourcePathHash": "3c8596a5d95de214d381b2f04898dd7042a266aa7889569981a97a1cf50045d1",
"sourceSha256": "cc19258fcf63018ce2dac4d7253e23fbfb71596ee27235871ad78aae743b57e6",
"status": "migrated",
"target": "plugins/dev/skills/design-db",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:a388287911bc39cb": {
"sourcePathHash": "a388287911bc39cb3f88824044426782df3c3e6450c4db9e4948c28a47dbf9f1",
"sourceSha256": "20e13c3458f6af8d0b131f650f573eb824978438b52475a2f480a67c2f846cd6",
"status": "superseded",
"target": "plugins/skill/skills/guidance",
"targetVersion": "0.1.0",
"reason": "框架约定改由通用规范检索结合项目真实依赖解析",
"reviewedAt": "2026-08-26"
},
"source-b:ca08ee8839bccc15": {
"sourcePathHash": "ca08ee8839bccc1504c253cdb9bfc09803ddf77f948605f8563d04e0466a2ce7",
"sourceSha256": "0086c8da048d94303e9d5765b91b7eff9a10a798ffa73de9f245be5422cf3255",
"status": "superseded",
"target": "plugins/dev/skills/component",
"targetVersion": "0.1.0",
"reason": "组件约定与契约查证能力已合并到 component",
"reviewedAt": "2026-08-25"
},
"source-b:1b4cee54060ef399": {
"sourcePathHash": "1b4cee54060ef3995d6c98e99dee3c3200ab2e90dd62136cef07b9b09e31364d",
"sourceSha256": "24df169fbb18f39be5ec4046cc8d33535b1b86d2114673ee35d1a2a0324c1b25",
"status": "migrated",
"target": "plugins/dev/skills/review-frontend",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:7b7552c8c6e1b5f5": {
"sourcePathHash": "7b7552c8c6e1b5f56a283db304272da8cd63210aaf7327231a7b26f3cf903082",
"sourceSha256": "9c373fe6bb955d4431e92bba66239cb707a6179082a624b74bc77fa36663fc61",
"status": "migrated",
"target": "plugins/dev/skills/design-frontend-data",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:5f525e6aeda55dd8": {
"sourcePathHash": "5f525e6aeda55dd81c26d38cf59097cb52abbcfc5b05e35fe7e8101a1a3353b5",
"sourceSha256": "8068a6137951e94a01bc68cf9011db69f3d759304a3167291afa4c238531e34a",
"status": "migrated",
"target": "plugins/dev/skills/form",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:f2079362cf82e719": {
"sourcePathHash": "f2079362cf82e719e2dde6098c55ce984709a97446a7f66113f8b5f317f1f55b",
"sourceSha256": "f57f3c0d5175c3c1700b74e8489d820eed4ef36d10e79ab14e096cb2a20a0cec",
"status": "migrated",
"target": "plugins/dev/skills/review-java",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:b78122ad4813e9ba": {
"sourcePathHash": "b78122ad4813e9baef0ee5281bddb910b01fd89bdbbd2753011ac3deb52a8ce5",
"sourceSha256": "c06d7eb2674dcbb4bad01a8c65c6d372dffd95c585a16ab2c3f1bba19df199e0",
"status": "superseded",
"target": "plugins/skill/skills/guidance",
"targetVersion": "0.1.0",
"reason": "框架知识查询改由通用规范检索与项目源码证据完成",
"reviewedAt": "2026-08-26"
},
"source-b:7fd143975b0071c3": {
"sourcePathHash": "7fd143975b0071c3efa271a9ad19c3d7e422f64d3e23aa08d7a091c805cb9467",
"sourceSha256": "33582dcda068247a5c56238134dad308d1f66b03abbfdc7bc282a9ac9ff918a2",
"status": "migrated",
"target": "plugins/dev/skills/review-mybatis",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:affd0805467646c1": {
"sourcePathHash": "affd0805467646c1fcb4c196776e9cb610fcec721e1a44df3bc02f5c08b84aeb",
"sourceSha256": "c7f565da3a1f412d3f15642fc9ad44733adee73c4d13cdebffaff1de4ca5652c",
"status": "migrated",
"target": "plugins/dev/skills/design-workflow",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:2a64acb63e8b2030": {
"sourcePathHash": "2a64acb63e8b203031c4ef292f957c0fe0d3722e079dc9a299730015b9e3359c",
"sourceSha256": "580da97b2fb16543db9aef62359f153fe5aca382d88987f10f233289bc02790d",
"status": "migrated",
"target": "plugins/git/skills/commit-msg",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:3a5d583c511959b3": {
"sourcePathHash": "3a5d583c511959b3d1934d15eaa9b0cd0590c558535b6a8307f870d7ce0c8dd0",
"sourceSha256": "6b69964b6e65ddc6eefa857fa1016587d3b2d2fac22d2bda4196762233fb025b",
"status": "migrated",
"target": "plugins/git/skills/branch",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:13bb77c66fee0964": {
"sourcePathHash": "13bb77c66fee09641fe61781ffbba4c51ed47b14ce225972ea35219e489405a3",
"sourceSha256": "abe529dcc85b1ac88791c95cf7be12cf5b1f545988b515f10edb55f0a8c94fb1",
"status": "migrated",
"target": "plugins/git/skills/export",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:fd03e996181699d9": {
"sourcePathHash": "fd03e996181699d95a09c1940f685ac366daeeda1aa901175d26dcf153f755fb",
"sourceSha256": "b4ef1f7847dd9081fdacaecae8853b2946e22c899b08bbe48ef280d21546e044",
"status": "migrated",
"target": "plugins/git/skills/identity",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:4ca0940d73b4b626": {
"sourcePathHash": "4ca0940d73b4b6264a37e1a344d669367dc313364a0077abf8bcaf97cfed42a1",
"sourceSha256": "92b3cea563f544806493245bdf099e2357112ccbce15007593c94d62c26c258c",
"status": "migrated",
"target": "plugins/git/skills/integrate",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:0b917cc63232dff3": {
"sourcePathHash": "0b917cc63232dff38c375d3bec3fabe57390201c34c3ce96f7885eb23bca91d7",
"sourceSha256": "5ecfa8040f5d71f347cfcfb89040a5cbfdb96f71867fb11b750b78d4aa17d66a",
"status": "migrated",
"target": "plugins/knowledge/skills/init",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:905e74ea51281da1": {
"sourcePathHash": "905e74ea51281da179c1eb31677af71569a95139a74fb139d39dadd52db2ce62",
"sourceSha256": "cfc1eb3fafb15dc51b730dd36d3ad507b5ae415d5f1a5da5138471170afdde38",
"status": "migrated",
"target": "plugins/knowledge/skills/trace",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:274c4aac900a23c3": {
"sourcePathHash": "274c4aac900a23c3ca0506807c4df8e09c95e21b84fd633951bae146f6c41028",
"sourceSha256": "b4beacdc745cd06ee4be20694d60bae0c9c6122fb61b917b12b66451497651e9",
"status": "migrated",
"target": "plugins/knowledge/skills/lessons",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:6e1a4d182b2e41fd": {
"sourcePathHash": "6e1a4d182b2e41fddd28051c20d7170821a87b518e49684cc4f12b731789831d",
"sourceSha256": "cf106df97100aabf260db7c8d45c2d6d499c643b4898d7e46c98852a90b7134d",
"status": "superseded",
"target": "plugins/knowledge/skills/lessons",
"targetVersion": "0.1.0",
"reason": "经验提升能力已合并到 lessons 的 promote 模式",
"reviewedAt": "2026-08-25"
},
"source-b:cd4c15d66d5cfba0": {
"sourcePathHash": "cd4c15d66d5cfba0c568b6f3ac4ceac9a7cea797eca8c6bf5eab81187e70f7d7",
"sourceSha256": "a9b109dc659e8e65645b6507ab2a39a04598d0c0ed63feaef0985765d3ff9f3f",
"status": "superseded",
"target": "plugins/knowledge/skills/lessons",
"targetVersion": "0.1.0",
"reason": "经验回扫能力已合并到 lessons 的 audit 模式",
"reviewedAt": "2026-08-25"
},
"source-b:a6062c4036828615": {
"sourcePathHash": "a6062c4036828615945db311cf68c2ac886feef91c79b53d882bbe41fd71143f",
"sourceSha256": "94b7e58d4c4903498cf29365fe466aaba8bfdf4675754f65cd30bad37c99e02f",
"status": "migrated",
"target": "plugins/knowledge/skills/distill",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:f58c6cd52bc983e8": {
"sourcePathHash": "f58c6cd52bc983e87d265308ade0da8d89bd74d5d4f1e96fc463ef299a9fe94a",
"sourceSha256": "98392eb0fe65dab6877495e6863a6722cd2777f5767dfd95b3a829ed7798a5e7",
"status": "migrated",
"target": "plugins/knowledge/skills/handoff",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:258ee10299925494": {
"sourcePathHash": "258ee1029992549420c8465c8de2d63c1bfaf9334b812d6b79a07a9823b49733",
"sourceSha256": "b4958d4aee3c06b778273d87a07dd423cf15fc8fbfdde27e00552ff60138795e",
"status": "migrated",
"target": "plugins/dev/skills/analyze-bugs",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:52592d4a59314c74": {
"sourcePathHash": "52592d4a59314c74de9b58cfb4cda6e9f5d9514bb05c9b586044c12754982d52",
"sourceSha256": "e8be1b2c17ab3aaccbb5559a07b64da8a7c718293e83cb8dd685c87708c7cbdc",
"status": "migrated",
"target": "plugins/dev/skills/upgrade",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:f478193705d1d02d": {
"sourcePathHash": "f478193705d1d02d6bb6375728dc10d34487e3f67ab8d164a80104f6a71fccdc",
"sourceSha256": "218eb591d1944b2280853bbf39cb028f9a41e55dee01218838b14f87755fad72",
"status": "superseded",
"target": "official:browser-computer-use",
"reason": "Codex 浏览器与计算机操作能力已覆盖宿主浏览器代理配置",
"reviewedAt": "2026-08-26"
},
"source-b:e4b14f4b2719e28b": {
"sourcePathHash": "e4b14f4b2719e28b409bc66980b3698588684dde81a0c627e862262e76c34b2f",
"sourceSha256": "de185022a0cf8bfce340709f47052578ec761664c96723172c48bdd4f10e71f1",
"status": "superseded",
"target": "plugins/dev/skills/review-code",
"targetVersion": "0.1.0",
"reason": "分支、提交与目录审查已合并到统一代码审查入口",
"reviewedAt": "2026-08-26"
},
"source-b:aa42f7c1a045bb49": {
"sourcePathHash": "aa42f7c1a045bb496bbc9e62beb7a7b74544153ed9551492f5b801ca4c9df098",
"sourceSha256": "aff6fad01afa2a15756a36d210c67b229a1eb2155981292450f0487c3005125d",
"status": "migrated",
"target": "plugins/knowledge/skills/worklog",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:3763e17cff825df6": {
"sourcePathHash": "3763e17cff825df6920f57d84736093082014f2a0316d9e84ee18ac8078ff6e5",
"sourceSha256": "bd2e4ae7f8a9bded1f7a983289fb49556f30cd5d6c6382500dd8c464fdd66e02",
"status": "migrated",
"target": "plugins/doc/skills/archive",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:9e3e75840a46af96": {
"sourcePathHash": "9e3e75840a46af96c33a1885f192813239b511eb972a4962b30b76de7392c88c",
"sourceSha256": "379c41e41d833880653ce065a63380e9cf29d2b7663f7ee91a3d77594202d042",
"status": "superseded",
"target": "plugins/dev/skills/upgrade",
"targetVersion": "0.1.0",
"reason": "前后端升级合并为按真实版本与官方迁移资料执行的统一能力",
"reviewedAt": "2026-08-26"
},
"source-b:766516f82e8e9be3": {
"sourcePathHash": "766516f82e8e9be32e1ff3f29123202b6bad85bf9a88e36f87f256446ea31eaf",
"sourceSha256": "c66ca1651923b20cd62395ba2cc6b8532093d5df10f420c1673a103a88dbf5aa",
"status": "migrated",
"target": "plugins/doc/skills/message",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:31dec2fa162b06d3": {
"sourcePathHash": "31dec2fa162b06d379aa676d426d2de01c3b082061ff15adb7a8c721cd5635af",
"sourceSha256": "a146d79ccfec7b1f9275cda8e6cf3ab1190430d0c3bc11886a722ce1d6fabc90",
"status": "migrated",
"target": "plugins/skill/skills/guidance-edit",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-25"
},
"source-b:988b200efd0b8be0": {
"sourcePathHash": "988b200efd0b8be024060e0785da2f864bf7ee30d7c7b434fde7ab1e4cdcc9d8",
"sourceSha256": "e7fa073d592167c07b06698d664192baafb126afa1a6bfdc50a056e1ea13e56f",
"status": "excluded",
"reason": "CraftKit 原生面向 Codex,不需要 Claude Code 到 Codex 的平台转换能力",
"reviewedAt": "2026-08-25"
},
"source-b:8a7cdf719d8f8a3e": {
"sourcePathHash": "8a7cdf719d8f8a3e0200781a82062cc5a839e4d2c2fd400982771968245d62dd",
"sourceSha256": "3461485ba35dceffc5fc0e9266793003a0355d412aa8a609c93709745226d00a",
"status": "migrated",
"target": "plugins/dev/skills/estimate",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:6e5eb2b9ab9273cf": {
"sourcePathHash": "6e5eb2b9ab9273cf889bd908ff446eda59cc3e121ee7a1861fb2664d7fa049e9",
"sourceSha256": "31eef261e6e3e930cd88b18c938176c3ef1d3e8d039986712ee6c3191481872d",
"status": "migrated",
"target": "plugins/doc/skills/report",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
},
"source-b:a985b2c0fb927117": {
"sourcePathHash": "a985b2c0fb9271174d9d46425eb6792d2483ebcd6da89ecc1cbfb61407259499",
"sourceSha256": "e2f92df8dc489e79bfdd74de8817abf742f425422765c76c4a46263d1fbd7a9e",
"status": "migrated",
"target": "plugins/doc/skills/requirements",
"targetVersion": "0.1.0",
"reviewedAt": "2026-08-26"
}
}
}
-58
View File
@@ -1,58 +0,0 @@
"""开发分析与设计批次的职责边界测试。"""
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[2]
SKILLS = ROOT / "plugins" / "dev" / "skills"
def read_skill(name: str) -> str:
"""合并读取 Skill 入口及引用文件。"""
folder = SKILLS / name
files = [folder / "SKILL.md", *sorted((folder / "references").glob("*.md"))]
return "\n".join(path.read_text(encoding="utf-8") for path in files)
class DevDesignBatchTest(unittest.TestCase):
"""验证四个 Skill 的通用化设计和相互边界。"""
def test_plan_is_evidence_based_and_does_not_implement(self) -> None:
content = read_skill("plan-change")
self.assertIn("不执行设计或编码", content)
self.assertIn("验收标准", content)
self.assertIn("运行行为标为待验证", content)
self.assertNotIn("docs/{moduleCode}", content)
def test_backend_design_uses_project_version(self) -> None:
content = read_skill("design-backend")
self.assertIn("精确版本", content)
self.assertIn("guidance", content)
self.assertIn("事务边界", content)
self.assertIn("不生成业务代码", content)
def test_frontend_design_composes_existing_helpers(self) -> None:
content = read_skill("design-frontend")
for helper in ("component", "style", "form", "prepare-api"):
self.assertIn(helper, content)
self.assertIn("不编造组件", content)
self.assertIn("不跨版本混用", content)
def test_prepare_api_does_not_assume_contract(self) -> None:
content = read_skill("prepare-api")
self.assertIn("不预设请求客户端", content)
self.assertIn("未匹配字段", content)
self.assertIn("不发起真实请求", content)
self.assertIn("不能反向覆盖", content)
def test_descriptions_are_distinct(self) -> None:
descriptions = []
for name in ("plan-change", "design-backend", "design-frontend", "prepare-api"):
text = (SKILLS / name / "SKILL.md").read_text(encoding="utf-8")
descriptions.append(text.split("---", 2)[1])
self.assertEqual(4, len(set(descriptions)))
if __name__ == "__main__":
unittest.main()
-78
View File
@@ -1,78 +0,0 @@
"""验证文档批次三个独立实现的核心行为和安全边界。"""
from __future__ import annotations
import json
import subprocess
import tempfile
import unittest
from pathlib import Path
from docx import Document
from openpyxl import Workbook
ROOT = Path(__file__).resolve().parents[2]
MD_SCRIPT = ROOT / "plugins/doc/skills/md-to-docx/scripts/convert.py"
XLSX_SCRIPT = ROOT / "plugins/doc/skills/xlsx-to-md/scripts/convert.py"
ARCHIVE_SCRIPT = ROOT / "plugins/doc/skills/archive/scripts/archive.py"
class DocumentBatchTests(unittest.TestCase):
"""使用临时目录执行真实转换,不依赖仓库外测试材料。"""
def run_script(self, script: Path, *args: object) -> subprocess.CompletedProcess[str]:
"""使用当前受控 Python 运行时执行目标脚本。"""
return subprocess.run(
[str(Path(__import__("sys").executable)), str(script), *(str(value) for value in args)],
capture_output=True, text=True, encoding="utf-8", check=False,
)
def test_markdown_to_docx_preserves_common_blocks(self) -> None:
"""标题、列表、表格和代码块应生成可重新打开的 DOCX。"""
with tempfile.TemporaryDirectory() as temp:
folder = Path(temp); source = folder / "sample.md"; output = folder / "sample.docx"
source.write_text("# 标题\n\n- 项目\n\n| 名称 | 值 |\n| --- | --- |\n| A | 1 |\n\n```py\nprint('ok')\n```\n", encoding="utf-8")
result = self.run_script(MD_SCRIPT, source)
self.assertEqual(0, result.returncode, result.stderr)
document = Document(output)
self.assertEqual("标题", document.paragraphs[0].text)
self.assertEqual(1, len(document.tables))
self.assertNotEqual(0, self.run_script(MD_SCRIPT, source).returncode)
def test_xlsx_to_markdown_handles_merges_and_escaping(self) -> None:
"""合并区域应填充值,表格特殊字符应安全转义。"""
with tempfile.TemporaryDirectory() as temp:
folder = Path(temp); source = folder / "sample.xlsx"
workbook = Workbook(); sheet = workbook.active; sheet.title = "数据"
sheet.append(["名称", "值"]); sheet.append(["A|B", "=1+1"])
sheet.merge_cells("A3:B3"); sheet["A3"] = "合并"
workbook.create_sheet("空表"); workbook.save(source)
result = self.run_script(XLSX_SCRIPT, source)
self.assertEqual(0, result.returncode, result.stderr)
markdown = source.with_suffix(".md").read_text(encoding="utf-8")
self.assertIn(r"A\|B", markdown)
self.assertIn("| 合并 | 合并 |", markdown)
self.assertIn("_空工作表_", markdown)
def test_archive_is_dry_run_first_and_keeps_source(self) -> None:
"""预演不得写目标,执行后仍须保留来源并只抽取指定章节。"""
with tempfile.TemporaryDirectory() as temp:
root = Path(temp); source = root / "specs/api.md"; source.parent.mkdir()
source.write_text("---\nowner: team\n---\n# 概述\n正文\n## API\n接口\n## 其他\n忽略\n", encoding="utf-8")
config = root / "rules.json"
config.write_text(json.dumps({"version": 1, "archiveRoot": "archive", "rules": [{"match": "specs/*.md", "target": "{stem}.md", "section": "API", "stripFrontmatter": True, "requiredText": ["接口"]}]}, ensure_ascii=False), encoding="utf-8")
target = root / "archive/api.md"
preview = self.run_script(ARCHIVE_SCRIPT, "--root", root, "--config", config)
self.assertEqual(0, preview.returncode, preview.stderr); self.assertFalse(target.exists())
applied = self.run_script(ARCHIVE_SCRIPT, "--root", root, "--config", config, "--apply")
self.assertEqual(0, applied.returncode, applied.stderr)
self.assertTrue(source.exists()); self.assertEqual("## API\n接口\n", target.read_text(encoding="utf-8"))
if __name__ == "__main__":
unittest.main()
-117
View File
@@ -1,117 +0,0 @@
import importlib.util
import sys
import tempfile
import unittest
import zipfile
from pathlib import Path
SCRIPT = (
Path(__file__).parents[2]
/ "plugins"
/ "doc"
/ "skills"
/ "docx-to-md"
/ "scripts"
/ "convert.py"
)
SPEC = importlib.util.spec_from_file_location("docx_to_md_convert", SCRIPT)
MODULE = importlib.util.module_from_spec(SPEC)
assert SPEC.loader is not None
sys.modules[SPEC.name] = MODULE
SPEC.loader.exec_module(MODULE)
class DocxToMarkdownTest(unittest.TestCase):
"""验证转换器的核心内容、覆盖保护和输入边界。"""
def make_docx(self, path: Path) -> None:
"""创建只包含公开 OOXML 结构的最小测试文档。"""
document = """<?xml version="1.0" encoding="UTF-8"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<w:body>
<w:p><w:pPr><w:pStyle w:val="Heading1"/></w:pPr><w:r><w:t>测试标题</w:t></w:r></w:p>
<w:p><w:r><w:rPr><w:b/></w:rPr><w:t>加粗正文</w:t></w:r>
<w:hyperlink r:id="rLink"><w:r><w:t>示例链接</w:t></w:r></w:hyperlink></w:p>
<w:p><w:pPr><w:numPr><w:ilvl w:val="0"/><w:numId w:val="1"/></w:numPr></w:pPr>
<w:r><w:t>列表项目</w:t></w:r></w:p>
<w:p><w:r><w:drawing><a:blip r:embed="rImage"/></w:drawing></w:r></w:p>
<w:tbl>
<w:tr><w:tc><w:p><w:r><w:t>名称</w:t></w:r></w:p></w:tc><w:tc><w:p><w:r><w:t>值</w:t></w:r></w:p></w:tc></w:tr>
<w:tr><w:tc><w:p><w:r><w:t>A</w:t></w:r></w:p></w:tc><w:tc><w:p><w:r><w:t>1</w:t></w:r></w:p></w:tc></w:tr>
</w:tbl>
</w:body>
</w:document>"""
styles = """<?xml version="1.0" encoding="UTF-8"?>
<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:style w:type="paragraph" w:styleId="Heading1"><w:name w:val="heading 1"/></w:style>
</w:styles>"""
numbering = """<?xml version="1.0" encoding="UTF-8"?>
<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:abstractNum w:abstractNumId="0"><w:lvl w:ilvl="0"><w:numFmt w:val="bullet"/></w:lvl></w:abstractNum>
<w:num w:numId="1"><w:abstractNumId w:val="0"/></w:num>
</w:numbering>"""
relationships = """<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rLink" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="https://example.com" TargetMode="External"/>
<Relationship Id="rImage" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/test.png"/>
</Relationships>"""
with zipfile.ZipFile(path, "w") as archive:
archive.writestr("word/document.xml", document)
archive.writestr("word/styles.xml", styles)
archive.writestr("word/numbering.xml", numbering)
archive.writestr("word/_rels/document.xml.rels", relationships)
archive.writestr("word/media/test.png", b"\x89PNG\r\n\x1a\n")
archive.writestr("word/comments.xml", "<comments/>")
def test_convert_common_content_and_report_warning(self) -> None:
"""常见结构应转换,无法处理的批注应明确告警。"""
with tempfile.TemporaryDirectory() as temp:
root = Path(temp)
source = root / "sample.docx"
output = root / "result"
self.make_docx(source)
result = MODULE.DocxConverter(source, output).convert()
markdown = result.output_file.read_text(encoding="utf-8")
self.assertIn("# 测试标题", markdown)
self.assertIn("**加粗正文**", markdown)
self.assertIn("[示例链接](https://example.com)", markdown)
self.assertIn("- 列表项目", markdown)
self.assertIn("| 名称 | 值 |", markdown)
self.assertIn("![图片](images/image-", markdown)
self.assertEqual(result.images, 1)
self.assertTrue(any("批注" in warning for warning in result.warnings))
def test_existing_output_requires_force(self) -> None:
"""默认不得写入已有输出目录,显式覆盖后才可继续。"""
with tempfile.TemporaryDirectory() as temp:
root = Path(temp)
source = root / "sample.docx"
output = root / "result"
self.make_docx(source)
output.mkdir()
with self.assertRaises(FileExistsError):
MODULE.DocxConverter(source, output).convert()
result = MODULE.DocxConverter(source, output, force=True).convert()
self.assertTrue(result.output_file.exists())
def test_cli_rejects_non_docx(self) -> None:
"""命令行入口应拒绝扩展名不正确的文件。"""
with tempfile.TemporaryDirectory() as temp:
source = Path(temp) / "sample.txt"
source.write_text("not docx", encoding="utf-8")
self.assertEqual(MODULE.main([str(source)]), 2)
if __name__ == "__main__":
unittest.main()
-51
View File
@@ -1,51 +0,0 @@
"""前端辅助 Skill 的结构与行为边界测试。"""
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[2]
DEV_SKILLS = ROOT / "plugins" / "dev" / "skills"
def read_skill(name: str) -> str:
"""读取指定 Skill 的入口及引用文档。"""
folder = DEV_SKILLS / name
files = [folder / "SKILL.md", *sorted((folder / "references").glob("*.md"))]
return "\n".join(path.read_text(encoding="utf-8") for path in files)
class FrontendHelpersTest(unittest.TestCase):
"""验证三个 Skill 能互补协作且不恢复来源专有规则。"""
def test_component_requires_project_evidence(self) -> None:
content = read_skill("component")
self.assertIn(".craftkit/project.json", content)
self.assertIn(".craftkit/standards/frontend/components.md", content)
self.assertIn("不编造结论", content)
self.assertIn("官方文档", content)
self.assertIn("不复制依赖库文档", content)
def test_style_uses_existing_visual_authority(self) -> None:
content = read_skill("style")
self.assertIn("设计令牌", content)
self.assertIn("经用户确认后再修改", content)
self.assertIn("不内置固定矩阵", content)
def test_form_does_not_invent_business_rules(self) -> None:
content = read_skill("form")
self.assertIn("不自行确定字段必填", content)
self.assertIn("不设计接口", content)
self.assertIn("component", content)
def test_precise_skills_replace_router(self) -> None:
self.assertFalse((DEV_SKILLS / "guide").exists())
descriptions = {
name: (DEV_SKILLS / name / "SKILL.md").read_text(encoding="utf-8").split("---", 2)[1]
for name in ("component", "style", "form")
}
self.assertEqual(3, len(set(descriptions.values())))
if __name__ == "__main__":
unittest.main()
-102
View File
@@ -1,102 +0,0 @@
"""验证 Git 变更导出的选择、配置保护和写入边界。"""
from __future__ import annotations
import json
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
SCRIPT = ROOT / "plugins/git/skills/export/scripts/export.py"
class GitExportTests(unittest.TestCase):
"""在临时 Git 仓库中验证真实命令行为。"""
def git(self, repo: Path, *args: str) -> str:
"""执行测试仓库内的 Git 命令。"""
result = subprocess.run(["git", "-C", str(repo), *args], capture_output=True, text=True, encoding="utf-8", check=True)
return result.stdout.strip()
def run_export(self, repo: Path, target: Path, *args: str) -> subprocess.CompletedProcess[str]:
"""使用当前受控 Python 运行导出脚本。"""
return subprocess.run([sys.executable, str(SCRIPT), "--repo", str(repo), "--target", str(target), *args], capture_output=True, text=True, encoding="utf-8", check=False)
def make_repo(self, folder: Path) -> Path:
"""创建具有稳定身份和首个提交的隔离仓库。"""
repo = folder / "repo"; repo.mkdir()
self.git(repo, "init", "-q"); self.git(repo, "config", "user.name", "Test User"); self.git(repo, "config", "user.email", "test@example.invalid")
(repo / "src").mkdir(); (repo / "src/app.txt").write_text("v1\n", encoding="utf-8")
self.git(repo, "add", "src/app.txt"); self.git(repo, "commit", "-q", "-m", "init")
return repo
def test_worktree_preview_classifies_shared_and_protected_files(self) -> None:
"""共享 CraftKit 文档可导出,环境配置默认受保护,私钥永久阻断。"""
with tempfile.TemporaryDirectory() as temp:
root = Path(temp); repo = self.make_repo(root); target = root / "out"
(repo / "src/app.txt").write_text("v2\n", encoding="utf-8")
(repo / "application-dev.yml").write_text("server: local\n", encoding="utf-8")
(repo / "private.pem").write_text("-----BEGIN PRIVATE KEY-----\nabc\n", encoding="utf-8")
standard = repo / ".craftkit/standards/export.md"; standard.parent.mkdir(parents=True); standard.write_text("规范\n", encoding="utf-8")
result = self.run_export(repo, target, "--mode", "worktree", "--snapshot", "worktree", "--include-untracked")
self.assertEqual(2, result.returncode)
manifest = json.loads(result.stdout)
self.assertIn("src/app.txt", manifest["exported"])
self.assertIn(".craftkit/standards/export.md", manifest["exported"])
self.assertIn("application-dev.yml", manifest["protected"])
self.assertIn("private.pem", manifest["blocked"])
self.assertFalse(target.exists())
def test_protected_file_requires_matching_project_evidence(self) -> None:
"""环境配置只有在项目规范明确点名且执行确认后才写入。"""
with tempfile.TemporaryDirectory() as temp:
root = Path(temp); repo = self.make_repo(root); target = root / "out"
(repo / "application-dev.yml").write_text("server: local\n", encoding="utf-8")
evidence = repo / ".craftkit/standards/delivery.md"; evidence.parent.mkdir(parents=True)
evidence.write_text("交付必须包含 application-dev.yml。\n", encoding="utf-8")
result = self.run_export(repo, target, "--mode", "worktree", "--snapshot", "worktree", "--include-untracked",
"--allow-protected", "--policy-evidence", ".craftkit/standards/delivery.md", "--apply")
self.assertEqual(0, result.returncode, result.stderr)
self.assertTrue((target / "application-dev.yml").is_file())
manifest = json.loads((target / "export-manifest.json").read_text(encoding="utf-8"))
self.assertEqual(".craftkit/standards/delivery.md", manifest["policyEvidence"])
def test_range_uses_explicit_snapshot_and_records_deleted_file(self) -> None:
"""范围负责选路径,快照负责取内容;快照中删除的文件只进入清单。"""
with tempfile.TemporaryDirectory() as temp:
root = Path(temp); repo = self.make_repo(root); target = root / "out"
base = self.git(repo, "rev-parse", "HEAD")
(repo / "src/app.txt").unlink(); (repo / "src/new.txt").write_text("new\n", encoding="utf-8")
self.git(repo, "add", "-A"); self.git(repo, "commit", "-q", "-m", "change")
head = self.git(repo, "rev-parse", "HEAD")
result = self.run_export(repo, target, "--mode", "range", "--base", base, "--head", head, "--snapshot", head)
self.assertEqual(0, result.returncode, result.stderr)
manifest = json.loads(result.stdout)
self.assertIn("src/app.txt", manifest["deleted"])
self.assertIn("src/new.txt", manifest["exported"])
def test_time_mode_selects_committed_paths_without_writing(self) -> None:
"""时间模式应选择提交中出现的路径,并保持预演只读。"""
with tempfile.TemporaryDirectory() as temp:
root = Path(temp); repo = self.make_repo(root); target = root / "out"
(repo / "src/app.txt").write_text("v2\n", encoding="utf-8")
self.git(repo, "add", "src/app.txt"); self.git(repo, "commit", "-q", "-m", "update")
result = self.run_export(repo, target, "--mode", "time", "--since", "2000-01-01", "--snapshot", "HEAD")
self.assertEqual(0, result.returncode, result.stderr)
self.assertIn("src/app.txt", json.loads(result.stdout)["exported"])
self.assertFalse(target.exists())
if __name__ == "__main__":
unittest.main()
-79
View File
@@ -1,79 +0,0 @@
"""验证预集成 Skill 的文档契约和隔离 Git 行为。"""
from __future__ import annotations
import subprocess
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
SKILL = ROOT / "plugins/git/skills/integrate/SKILL.md"
class GitIntegrateTests(unittest.TestCase):
"""在临时仓库中验证 worktree 集成不会移动输入引用。"""
def git(self, repo: Path, *args: str, check: bool = True) -> subprocess.CompletedProcess[str]:
"""执行测试仓库中的 Git 命令并保留失败状态供冲突断言。"""
return subprocess.run(["git", "-C", str(repo), *args], capture_output=True, text=True, encoding="utf-8", check=check)
def make_repo(self, root: Path, conflict: bool = False) -> tuple[Path, str, str]:
"""创建具有源、目标分叉的隔离仓库。"""
repo = root / "repo"; repo.mkdir(); self.git(repo, "init", "-q")
self.git(repo, "config", "user.name", "Test User"); self.git(repo, "config", "user.email", "test@example.invalid")
(repo / "base.txt").write_text("base\n", encoding="utf-8")
self.git(repo, "add", "base.txt"); self.git(repo, "commit", "-q", "-m", "base")
self.git(repo, "branch", "target"); self.git(repo, "switch", "-q", "-c", "source")
source_file = repo / ("base.txt" if conflict else "source.txt")
source_file.write_text("source\n", encoding="utf-8")
self.git(repo, "add", "-A"); self.git(repo, "commit", "-q", "-m", "source")
source_hash = self.git(repo, "rev-parse", "source").stdout.strip()
self.git(repo, "switch", "-q", "target")
target_file = repo / ("base.txt" if conflict else "target.txt")
target_file.write_text("target\n", encoding="utf-8")
self.git(repo, "add", "-A"); self.git(repo, "commit", "-q", "-m", "target")
target_hash = self.git(repo, "rev-parse", "target").stdout.strip()
return repo, source_hash, target_hash
def test_isolated_merge_preserves_source_and_target_refs(self) -> None:
"""预集成分支发生 merge 后,源和目标引用必须保持原哈希。"""
with tempfile.TemporaryDirectory() as temp:
root = Path(temp); repo, source_hash, target_hash = self.make_repo(root)
worktree = root / "integration"
self.git(repo, "worktree", "add", "-q", "-b", "pre/integration", str(worktree), "target")
self.git(worktree, "merge", "--no-edit", "source")
self.assertEqual(source_hash, self.git(repo, "rev-parse", "source").stdout.strip())
self.assertEqual(target_hash, self.git(repo, "rev-parse", "target").stdout.strip())
self.assertTrue((worktree / "source.txt").is_file())
self.assertTrue((worktree / "target.txt").is_file())
def test_conflict_stops_before_automatic_resolution(self) -> None:
"""真实冲突应停留在未合并状态,不移动源或目标引用。"""
with tempfile.TemporaryDirectory() as temp:
root = Path(temp); repo, source_hash, target_hash = self.make_repo(root, conflict=True)
worktree = root / "integration"
self.git(repo, "worktree", "add", "-q", "-b", "pre/conflict", str(worktree), "target")
result = self.git(worktree, "merge", "--no-edit", "source", check=False)
self.assertNotEqual(0, result.returncode)
self.assertIn("UU base.txt", self.git(worktree, "status", "--short").stdout)
self.assertEqual(source_hash, self.git(repo, "rev-parse", "source").stdout.strip())
self.assertEqual(target_hash, self.git(repo, "rev-parse", "target").stdout.strip())
def test_skill_separates_stages_and_protects_delivery(self) -> None:
"""Skill 必须保留阶段授权、环境配置阻断和强推禁用边界。"""
text = SKILL.read_text(encoding="utf-8")
for value in ("assess", "prepare", "publish", "独立 worktree", "环境配置", "私钥"):
self.assertIn(value, text)
publish = (SKILL.parent / "references/publish.md").read_text(encoding="utf-8")
self.assertIn("默认不改用 `--force`", publish)
if __name__ == "__main__":
unittest.main()
-83
View File
@@ -1,83 +0,0 @@
"""规范索引维护 Skill 的确定性行为测试。"""
import json
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[2]
SKILL = ROOT / "plugins" / "skill" / "skills" / "guidance-edit"
SCRIPT = SKILL / "scripts" / "check_index.py"
class GuidanceEditTest(unittest.TestCase):
"""验证索引检查器和读写职责边界。"""
def run_check(self, standards: Path) -> subprocess.CompletedProcess[str]:
"""以 JSON 模式执行检查器。"""
return subprocess.run(
[sys.executable, str(SCRIPT), str(standards), "--json"],
check=False,
capture_output=True,
text=True,
encoding="utf-8",
)
def test_clean_index_is_reachable(self) -> None:
"""根索引能够逐级访问所有规范时应检查通过。"""
with tempfile.TemporaryDirectory() as temp:
standards = Path(temp)
(standards / "frontend").mkdir()
(standards / "index.md").write_text(
"[前端](frontend/index.md)\n", encoding="utf-8"
)
(standards / "frontend" / "index.md").write_text(
"[组件](components.md)\n", encoding="utf-8"
)
(standards / "frontend" / "components.md").write_text(
"# 组件\n", encoding="utf-8"
)
result = self.run_check(standards)
self.assertEqual(0, result.returncode, result.stderr)
report = json.loads(result.stdout)
self.assertEqual([], report["unindexed"])
self.assertEqual([], report["broken"])
def test_reports_unindexed_broken_and_cycle(self) -> None:
"""未索引文件、失效链接和循环引用应同时报告。"""
with tempfile.TemporaryDirectory() as temp:
standards = Path(temp)
(standards / "index.md").write_text(
"[主题](topic.md)\n[缺失](missing.md)\n", encoding="utf-8"
)
(standards / "topic.md").write_text(
"[返回](index.md)\n", encoding="utf-8"
)
(standards / "orphan.md").write_text("# 未索引\n", encoding="utf-8")
result = self.run_check(standards)
self.assertEqual(1, result.returncode)
report = json.loads(result.stdout)
self.assertEqual(["orphan.md"], report["unindexed"])
self.assertEqual("missing.md", report["broken"][0]["target"])
self.assertTrue(report["cycles"])
def test_missing_entry_is_input_error(self) -> None:
"""入口不存在时应返回输入错误,而不是覆盖问题。"""
with tempfile.TemporaryDirectory() as temp:
result = self.run_check(Path(temp))
self.assertEqual(2, result.returncode)
self.assertIn("索引入口不存在", result.stderr)
def test_skill_separates_read_and_write_modes(self) -> None:
"""检查默认只读,所有写入均需预览与确认。"""
content = (SKILL / "SKILL.md").read_text(encoding="utf-8")
self.assertIn("检查默认只读", content)
self.assertIn("经用户确认后写入", content)
self.assertIn("不强制创建固定层级", content)
if __name__ == "__main__":
unittest.main()
-112
View File
@@ -1,112 +0,0 @@
import json
import unittest
from pathlib import Path
ROOT = Path(__file__).parents[2]
GUIDANCE = ROOT / "plugins" / "skill" / "skills" / "guidance"
INIT = ROOT / "plugins" / "knowledge" / "skills" / "init"
class GuidanceInitTest(unittest.TestCase):
"""验证规范检索和项目初始化之间的职责边界。"""
def test_guidance_is_read_only_and_uses_progressive_sources(self) -> None:
"""检索 Skill 应声明项目优先、渐进读取且不隐式初始化。"""
content = (GUIDANCE / "SKILL.md").read_text(encoding="utf-8")
self.assertIn("以只读方式", content)
self.assertIn("不得先递归加载整个知识库", content)
self.assertIn("不得在检索过程中隐式初始化", content)
self.assertIn(".craftkit/project.json", content)
def test_public_baseline_routes_frontend_and_backend(self) -> None:
"""公共基线应提供前后端入口,并让索引引用实际规则文件。"""
root = GUIDANCE / "references" / "guidance"
index = (root / "index.md").read_text(encoding="utf-8")
self.assertIn("frontend/index.md", index)
self.assertIn("backend/index.md", index)
self.assertEqual(6, len(list((root / "frontend").glob("*.md"))))
self.assertEqual(6, len(list((root / "backend").glob("*.md"))))
sources = (root / "sources.md").read_text(encoding="utf-8")
for authority in ("RFC 9110", "WCAG 2.2", "OWASP", "MDN Fetch API"):
self.assertIn(authority, sources)
def test_framework_profiles_are_selected_by_project_version(self) -> None:
"""普通任务只能使用当前 profile,且不存在的 profile 不得被猜测。"""
routing = (GUIDANCE / "references" / "profile-routing.md").read_text(
encoding="utf-8"
)
self.assertIn("普通开发任务只加载当前 profile", routing)
self.assertIn("升级、迁移或版本比较", routing)
self.assertIn("不默认最新版本", routing)
self.assertIn("不创建空目录", routing)
init = (INIT / "SKILL.md").read_text(encoding="utf-8")
config = (INIT / "references" / "project-config.md").read_text(
encoding="utf-8"
)
self.assertIn("精确版本、公共 profile", init)
for field in ('"name"', '"version"', '"profile"'):
self.assertIn(field, config)
def test_init_supports_existing_and_new_projects(self) -> None:
"""初始化 Skill 应同时包含成熟项目与空项目流程。"""
content = (INIT / "SKILL.md").read_text(encoding="utf-8")
self.assertIn("成熟项目", content)
self.assertIn("空项目", content)
self.assertIn("参考项目", content)
self.assertTrue((INIT / "references" / "existing.md").is_file())
self.assertTrue((INIT / "references" / "new.md").is_file())
def test_project_template_has_safe_shared_fields(self) -> None:
"""项目模板应包含检索入口,且不得预置内部标识或机器路径。"""
path = INIT / "assets" / "project.json"
payload = json.loads(path.read_text(encoding="utf-8"))
self.assertEqual(1, payload["schemaVersion"])
self.assertIn(payload["initialization"]["mode"], {"existing", "new"})
self.assertEqual([], payload["dependencies"]["internal"])
self.assertEqual([], payload["frontend"]["componentLibraries"])
self.assertEqual([], payload["frontend"]["componentRoots"])
self.assertEqual([], payload["frontend"]["documentation"])
self.assertEqual(
".craftkit/standards/index.md",
payload["guidance"]["standardsIndex"],
)
serialized = json.dumps(payload, ensure_ascii=False)
self.assertNotIn(":\\", serialized)
def test_init_collects_frontend_component_metadata(self) -> None:
"""初始化应记录组件库证据和入口,但不能默认选择组件库。"""
content = "\n".join(
path.read_text(encoding="utf-8")
for path in (
INIT / "SKILL.md",
INIT / "references" / "project-config.md",
INIT / "references" / "existing.md",
INIT / "references" / "new.md",
)
)
self.assertIn("componentLibraries", content)
self.assertIn("组件根", content)
self.assertIn("不默认选择流行方案", content)
self.assertTrue((INIT / "assets" / "frontend-components.md").is_file())
def test_local_directories_are_the_only_nested_ignores(self) -> None:
"""初始化模板只排除本地状态和缓存,不得排除共享资料。"""
rules = (INIT / "assets" / "craftkit.gitignore").read_text(
encoding="utf-8"
)
active = [line for line in rules.splitlines() if line and not line.startswith("#")]
self.assertEqual(["/local/", "/cache/"], active)
if __name__ == "__main__":
unittest.main()
-59
View File
@@ -1,59 +0,0 @@
"""验证知识管理批次的职责边界、中性模板和模式路由。"""
from __future__ import annotations
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
KNOWLEDGE = ROOT / "plugins/knowledge/skills"
class KnowledgeBatchTests(unittest.TestCase):
"""检查四个 Skill 不重叠,并保持项目知识目录一致。"""
def read(self, relative: str) -> str:
"""读取待验证的 Skill 资源。"""
return (KNOWLEDGE / relative).read_text(encoding="utf-8")
def test_trace_is_evidence_based_and_does_not_auto_write(self) -> None:
"""偏离复盘应使用中性失效分类,不能自动修改规则。"""
text = self.read("trace/SKILL.md")
for value in ("missing", "unread", "mismatch", "misapplied", "insufficient", "尚未执行"):
self.assertIn(value, text)
self.assertNotIn("七问", text)
def test_distill_routes_long_term_knowledge_and_excludes_handoff(self) -> None:
"""知识提炼和任务交接必须具有不同产物边界。"""
text = self.read("distill/SKILL.md")
routing = self.read("distill/references/routing.md")
self.assertIn("不生成接续提示词", text)
for path in (".craftkit/knowledge/pitfalls/", ".craftkit/knowledge/decisions/", ".craftkit/standards/"):
self.assertIn(path, routing)
def test_lessons_merges_full_lifecycle_without_fixed_categories(self) -> None:
"""经验初始化、维护、审计和提升应共享一个目录与中性模板。"""
text = self.read("lessons/SKILL.md")
for mode in ("`init`", "`curate`", "`audit`", "`promote`"):
self.assertIn(mode, text)
self.assertIn(".craftkit/knowledge/pitfalls/", text)
index = self.read("lessons/assets/index.md")
self.assertIn("当前尚未建立分类", index)
self.assertNotIn("lowcode", index.casefold())
def test_worklog_requires_git_evidence_and_separate_write_permission(self) -> None:
"""工作日志不能推测工作量或自动写入日报文件。"""
text = self.read("worklog/SKILL.md")
self.assertIn("不把提交数量当作工作量", text)
self.assertIn("范围内确实没有提交时如实说明", text)
self.assertIn("需要新的明确授权", text)
if __name__ == "__main__":
unittest.main()
-76
View File
@@ -1,76 +0,0 @@
import subprocess
import tempfile
import unittest
from pathlib import Path
class LocalStateTest(unittest.TestCase):
"""验证 `.craftkit/` 能同时承载共享资料和本地状态。"""
def run_git(self, root: Path, *args: str) -> subprocess.CompletedProcess[str]:
"""在隔离仓库执行 Git,并保留输出供断言使用。"""
return subprocess.run(
["git", "-C", str(root), *args],
check=False,
capture_output=True,
text=True,
encoding="utf-8",
)
def test_nested_gitignore_separates_shared_and_local_content(self) -> None:
"""共享资料应可提交,本地目录和缓存应由嵌套规则排除。"""
with tempfile.TemporaryDirectory() as temp:
root = Path(temp)
initialized = self.run_git(root, "init")
self.assertEqual(initialized.returncode, 0, initialized.stderr)
craftkit = root / ".craftkit"
craftkit.mkdir()
(craftkit / ".gitignore").write_text(
"# Local CraftKit data\n/local/\n/cache/\n",
encoding="utf-8",
)
(craftkit / "README.md").write_text("# 项目资料\n", encoding="utf-8")
shared = craftkit / "standards" / "coding" / "rules.md"
shared.parent.mkdir(parents=True)
shared.write_text("# 编码规范\n", encoding="utf-8")
local = craftkit / "local" / "handoff" / "current.md"
local.parent.mkdir(parents=True)
local.write_text("# 本地交接\n", encoding="utf-8")
cache = craftkit / "cache" / "result.json"
cache.parent.mkdir(parents=True)
cache.write_text("{}\n", encoding="utf-8")
ignored = self.run_git(
root,
"check-ignore",
"--no-index",
"-v",
".craftkit/local/handoff/current.md",
)
self.assertEqual(ignored.returncode, 0, ignored.stderr)
self.assertIn(".craftkit/.gitignore", ignored.stdout.replace("\\", "/"))
cache_ignored = self.run_git(
root,
"check-ignore",
"--no-index",
".craftkit/cache/result.json",
)
self.assertEqual(cache_ignored.returncode, 0, cache_ignored.stderr)
status = self.run_git(root, "status", "--short", "--untracked-files=all")
self.assertEqual(status.returncode, 0, status.stderr)
normalized = status.stdout.replace("\\", "/")
self.assertIn(".craftkit/.gitignore", normalized)
self.assertIn(".craftkit/README.md", normalized)
self.assertIn(".craftkit/standards/coding/rules.md", normalized)
self.assertNotIn(".craftkit/local", normalized)
self.assertNotIn(".craftkit/cache", normalized)
self.assertFalse((root / ".gitignore").exists())
if __name__ == "__main__":
unittest.main()
-95
View File
@@ -1,95 +0,0 @@
"""迁移内部脚本的最小行为测试。"""
from __future__ import annotations
import importlib.util
import hashlib
import json
import tempfile
import unittest
from pathlib import Path
SCRIPT_ROOT = Path(__file__).parents[1] / "scripts"
def load_module(name: str):
"""从脚本路径加载模块,避免要求项目安装为 Python 包。"""
spec = importlib.util.spec_from_file_location(name, SCRIPT_ROOT / f"{name}.py")
module = importlib.util.module_from_spec(spec)
assert spec.loader
spec.loader.exec_module(module)
return module
scan_sources = load_module("scan_sources")
check_skill = load_module("check_skill")
update_lock = load_module("update_lock")
class MigrationScriptTests(unittest.TestCase):
def test_scan_uses_relative_paths_and_stable_hash(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
skill = root / "group" / "demo"
skill.mkdir(parents=True)
(skill / "SKILL.md").write_text(
"---\nname: demo\ndescription: 示例\n---\n", encoding="utf-8"
)
first = scan_sources.scan_source("source-a", root)
second = scan_sources.scan_source("source-a", root)
self.assertEqual("group/demo", first["skills"][0]["relativePath"])
self.assertEqual(first["skills"][0]["sha256"], second["skills"][0]["sha256"])
self.assertNotIn(str(root), json.dumps(first, ensure_ascii=False))
def test_check_reports_name_mismatch_and_forbidden_term(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory) / "expected"
root.mkdir()
(root / "SKILL.md").write_text(
"---\nname: other\ndescription: 示例\n---\n内部标识",
encoding="utf-8",
)
result = check_skill.check_skill(root, ["内部标识"])
codes = {issue["code"] for issue in result["issues"]}
self.assertIn("name-path-mismatch", codes)
self.assertIn("forbidden:内部标识", codes)
def test_update_lock_preserves_manual_fields(self):
path_hash = hashlib.sha256("group/demo".encode("utf-8")).hexdigest()
key = f"source-a:{path_hash[:16]}"
lock = {
"schemaVersion": 1,
"sources": {},
"skills": {
key: {
"status": "specified",
"target": "plugins/skill/skills/demo",
}
},
}
report = {
"sources": [
{
"id": "source-a",
"commit": "abc",
"skillCount": 1,
"skills": [
{
"name": "demo",
"relativePath": "group/demo",
"sha256": "123",
}
],
}
]
}
merged = update_lock.merge(lock, report)
entry = merged["skills"][key]
self.assertEqual("specified", entry["status"])
self.assertEqual("plugins/skill/skills/demo", entry["target"])
self.assertEqual("123", entry["sourceSha256"])
self.assertNotIn("name", entry)
if __name__ == "__main__":
unittest.main()
@@ -1,39 +0,0 @@
"""公开基线与专项设计 Skill 测试。"""
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[2]
SKILLS = ROOT / "plugins" / "dev" / "skills"
NAMES = (
"design-api", "design-db", "review-java", "review-frontend",
"design-frontend-data", "review-mybatis", "design-workflow",
)
class PublicBaselineBatchTest(unittest.TestCase):
"""验证公开来源登记和版本边界。"""
def test_sources_are_official_and_dated(self) -> None:
for name in NAMES:
source = (SKILLS / name / "references" / "sources.md").read_text(encoding="utf-8")
self.assertIn("重建日期:2026-08-25", source)
self.assertIn("https://", source)
def test_project_version_is_not_replaced_by_latest(self) -> None:
content = "\n".join(
(SKILLS / name / "SKILL.md").read_text(encoding="utf-8") for name in NAMES
)
self.assertIn("不得默认最新", content)
self.assertIn("规则混用", content)
self.assertIn("精确版本", content)
def test_workflow_does_not_embed_approval_rules(self) -> None:
content = (SKILLS / "design-workflow" / "SKILL.md").read_text(encoding="utf-8")
self.assertIn("不内置审批节点", content)
self.assertIn("项目实际流程引擎", content)
if __name__ == "__main__":
unittest.main()
-87
View File
@@ -1,87 +0,0 @@
"""剩余来源连续迁移波次验收测试。"""
from pathlib import Path
import json
import unittest
ROOT = Path(__file__).resolve().parents[2]
DEV = ROOT / "plugins" / "dev" / "skills"
DOC = ROOT / "plugins" / "doc" / "skills"
GIT = ROOT / "plugins" / "git" / "skills"
TARGETS = {
DEV: (
"design-api", "design-db", "review-java", "review-frontend",
"design-frontend-data", "review-mybatis", "design-workflow",
"implement-backend", "implement-frontend", "review-code",
"test-backend", "test-ui", "analyze-bugs", "upgrade", "estimate",
),
DOC: ("report", "message", "requirements"),
GIT: ("release",),
}
class RemainingWaveTest(unittest.TestCase):
"""验证目标完整性、操作边界和来源状态闭合。"""
def read_skill(self, root: Path, name: str) -> str:
return (root / name / "SKILL.md").read_text(encoding="utf-8")
def test_all_targets_have_skill_and_ui_metadata(self) -> None:
names = []
for root, targets in TARGETS.items():
for name in targets:
names.append(name)
self.assertTrue((root / name / "SKILL.md").is_file(), name)
self.assertTrue((root / name / "agents" / "openai.yaml").is_file(), name)
self.assertEqual(19, len(names))
self.assertEqual(len(names), len(set(names)))
def test_implementation_preserves_workspace_and_authorization(self) -> None:
content = "\n".join(
self.read_skill(DEV, name)
for name in ("implement-backend", "implement-frontend")
)
self.assertIn("保留用户已有改动", content)
self.assertIn("精确版本", content)
self.assertIn("不自动暂存", content)
self.assertIn("环境配置", content)
def test_review_and_test_results_are_separated(self) -> None:
review = self.read_skill(DEV, "review-code")
backend = self.read_skill(DEV, "test-backend")
ui = self.read_skill(DEV, "test-ui")
self.assertIn("只读", review)
self.assertIn("不启动完整应用", backend)
self.assertIn("授权后", ui)
self.assertIn("不能替代浏览器结果", ui)
def test_delivery_actions_are_independently_authorized(self) -> None:
upgrade = self.read_skill(DEV, "upgrade")
estimate = self.read_skill(DEV, "estimate")
release = self.read_skill(GIT, "release")
self.assertIn("源版本", upgrade)
self.assertIn("目标版本", upgrade)
self.assertIn("区间", estimate)
self.assertIn("假设", estimate)
self.assertIn("分阶段授权", release)
def test_writing_skills_do_not_send_or_invent(self) -> None:
content = "\n".join(self.read_skill(DOC, name) for name in TARGETS[DOC])
self.assertIn("不得虚构", content)
self.assertIn("默认只生成草稿", content)
def test_source_lock_has_no_pending_items(self) -> None:
data = json.loads((ROOT / "migration" / "source-lock.json").read_text(encoding="utf-8"))
skills = data["skills"]
self.assertEqual(64, len(skills))
self.assertNotIn("pending", {item["status"] for item in skills.values()})
self.assertEqual(
{"migrated", "superseded", "excluded"},
{item["status"] for item in skills.values()},
)
if __name__ == "__main__":
unittest.main()
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "dev", "name": "dev",
"version": "0.4.0", "version": "0.4.1",
"description": "通用软件设计、编码、审查与测试工作流。", "description": "通用软件设计、编码、审查与测试工作流。",
"author": { "author": {
"name": "CraftKit" "name": "CraftKit"
@@ -9,7 +9,7 @@
"interface": { "interface": {
"displayName": "Dev", "displayName": "Dev",
"shortDescription": "软件设计、编码、审查与测试工具", "shortDescription": "软件设计、编码、审查与测试工具",
"longDescription": "提供不绑定公司框架的设计、实现、审查、测试、升级与估算工作流。", "longDescription": "提供基于项目实际技术栈的设计、实现、审查、测试、升级与估算工作流。",
"developerName": "CraftKit", "developerName": "CraftKit",
"category": "Productivity", "category": "Productivity",
"capabilities": ["Read", "Write"], "capabilities": ["Read", "Write"],
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Bug 分析" display_name: "Bug 分析(dev:analyze-bugs)"
short_description: "分析结构化 Bug 清单和质量风险" short_description: "分析结构化 Bug 清单和质量风险"
default_prompt: "使用 $analyze-bugs 分析这份 Bug 清单并给出证据化结论。" default_prompt: "使用 $analyze-bugs 分析这份 Bug 清单并给出证据化结论。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "组件选型" display_name: "组件选型(dev:component)"
short_description: "基于项目证据选择并查证前端组件" short_description: "基于项目证据选择并查证前端组件"
default_prompt: "使用 $component 为当前场景选择组件,并列出契约证据和风险。" default_prompt: "使用 $component 为当前场景选择组件,并列出契约证据和风险。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "API 设计" display_name: "API 设计(dev:design-api)"
short_description: "设计和评审可验证的 HTTP API 契约" short_description: "设计和评审可验证的 HTTP API 契约"
default_prompt: "使用 $design-api 为当前需求设计 HTTP API 契约。" default_prompt: "使用 $design-api 为当前需求设计 HTTP API 契约。"
+1 -1
View File
@@ -18,7 +18,7 @@ description: 基于需求、现有后端代码和项目规范设计模块边界
## 边界 ## 边界
- 项目规则通过 `guidance` 获取;不存在的规范、框架能力和依赖接口不得猜测。 - 已安装 `guidance` 时用它获取项目规则;未安装时按“目标目录适用的 `AGENTS.md` → `.craftkit/agents/index.md` → `.craftkit/standards/index.md` → 命中正文”的顺序手工读取。不存在的规范、框架能力和依赖接口不得猜测。
- 版本从 `.craftkit/project.json`、构建文件、锁文件或源码证据确认,不默认最新版本。 - 版本从 `.craftkit/project.json`、构建文件、锁文件或源码证据确认,不默认最新版本。
- 对现有系统的设计先追踪真实调用链和数据流,区分已验证事实与建议。 - 对现有系统的设计先追踪真实调用链和数据流,区分已验证事实与建议。
- 不把接口示例、表结构草案或伪代码视为已实施行为。 - 不把接口示例、表结构草案或伪代码视为已实施行为。
@@ -1,4 +1,4 @@
interface: interface:
display_name: "后端设计" display_name: "后端设计(dev:design-backend)"
short_description: "设计后端边界、数据影响和运行约束" short_description: "设计后端边界、数据影响和运行约束"
default_prompt: "使用 $design-backend 为当前需求制定后端技术设计。" default_prompt: "使用 $design-backend 为当前需求制定后端技术设计。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "数据库设计" display_name: "数据库设计(dev:design-db)"
short_description: "设计表结构、约束、索引和迁移方案" short_description: "设计表结构、约束、索引和迁移方案"
default_prompt: "使用 $design-db 为当前需求设计数据库结构和迁移方案。" default_prompt: "使用 $design-db 为当前需求设计数据库结构和迁移方案。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "前端数据设计" display_name: "前端数据设计(dev:design-frontend-data)"
short_description: "设计请求、状态、缓存和视图模型边界" short_description: "设计请求、状态、缓存和视图模型边界"
default_prompt: "使用 $design-frontend-data 设计当前页面的数据流和状态边界。" default_prompt: "使用 $design-frontend-data 设计当前页面的数据流和状态边界。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "前端设计" display_name: "前端设计(dev:design-frontend)"
short_description: "设计页面、交互、状态和组件组合" short_description: "设计页面、交互、状态和组件组合"
default_prompt: "使用 $design-frontend 为当前需求制定前端页面设计。" default_prompt: "使用 $design-frontend 为当前需求制定前端页面设计。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "工作流设计" display_name: "工作流设计(dev:design-workflow)"
short_description: "设计状态、任务、权限和异常流程" short_description: "设计状态、任务、权限和异常流程"
default_prompt: "使用 $design-workflow 为当前业务设计可验证的工作流。" default_prompt: "使用 $design-workflow 为当前业务设计可验证的工作流。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "工作量评估" display_name: "工作量评估(dev:estimate)"
short_description: "输出带假设和风险的软件工作量区间" short_description: "输出带假设和风险的软件工作量区间"
default_prompt: "使用 $estimate 评估当前需求的工作量区间和风险。" default_prompt: "使用 $estimate 评估当前需求的工作量区间和风险。"
+1 -1
View File
@@ -1,4 +1,4 @@
interface: interface:
display_name: "表单设计" display_name: "表单设计(dev:form)"
short_description: "设计和检查前端表单结构与响应布局" short_description: "设计和检查前端表单结构与响应布局"
default_prompt: "使用 $form 为当前需求设计表单结构,并列出证据与待确认项。" default_prompt: "使用 $form 为当前需求设计表单结构,并列出证据与待确认项。"
@@ -10,7 +10,7 @@ description: 按当前项目真实语言、框架版本、规范和现有实现
## 工作流 ## 工作流
1. 检查 Git 状态,保留用户已有改动;确认目标、范围和不可修改项。 1. 检查 Git 状态,保留用户已有改动;确认目标、范围和不可修改项。
2. 读取 `AGENTS.md`、`.craftkit/project.json`,使用 `guidance` 获取项目规范,并从构建文件确认真实版本。 2. 读取 `AGENTS.md`、`.craftkit/project.json`,从构建文件确认真实版本。已安装 `guidance` 时用它检索规范;未安装时按“目标目录适用的 `AGENTS.md` → `.craftkit/agents/index.md` → `.craftkit/standards/index.md` → 命中正文”的顺序手工读取,不因缺少另一插件中断实现。
3. 追踪入口、调用链、数据流、测试和相邻稳定实现;设计不足时先补最小决策,不套用固定模板。 3. 追踪入口、调用链、数据流、测试和相邻稳定实现;设计不足时先补最小决策,不套用固定模板。
4. 实施最小完整变更,保持项目目录、依赖、异常、事务、日志和测试风格。 4. 实施最小完整变更,保持项目目录、依赖、异常、事务、日志和测试风格。
5. 执行项目已有的编译、静态检查和相关测试,分别报告未执行的真实环境验证。 5. 执行项目已有的编译、静态检查和相关测试,分别报告未执行的真实环境验证。
@@ -1,4 +1,4 @@
interface: interface:
display_name: "后端实现" display_name: "后端实现(dev:implement-backend)"
short_description: "按项目版本与规范实现后端代码变更" short_description: "按项目版本与规范实现后端代码变更"
default_prompt: "使用 $implement-backend 实现并验证当前后端需求。" default_prompt: "使用 $implement-backend 实现并验证当前后端需求。"
@@ -10,7 +10,7 @@ description: 按当前项目框架版本、组件契约、设计令牌和请求
## 工作流 ## 工作流
1. 检查 Git 状态并确认目标文件;从项目元数据、依赖和锁文件识别框架、构建工具及精确版本。 1. 检查 Git 状态并确认目标文件;从项目元数据、依赖和锁文件识别框架、构建工具及精确版本。
2. 使用 `guidance` 获取规范,检查相邻页面和公共封装;组件、样式、表单分别消费 `component`、`style`、`form` 的证据。 2. 已安装 `guidance` 时用它检索规范;未安装时按“目标目录适用的 `AGENTS.md` → `.craftkit/agents/index.md` → `.craftkit/standards/index.md` → 命中正文”的顺序手工读取。随后检查相邻页面和公共封装;组件、样式、表单分别消费 `component`、`style`、`form` 的证据。
3. 有设计与接口映射时消费 `design-frontend` 和 `prepare-api`;没有时只补当前实现必需的最小决策。 3. 有设计与接口映射时消费 `design-frontend` 和 `prepare-api`;没有时只补当前实现必需的最小决策。
4. 修改页面、路由、状态和 API 层,保持项目现有契约,不虚构组件、props、事件、接口或业务校验。 4. 修改页面、路由、状态和 API 层,保持项目现有契约,不虚构组件、props、事件、接口或业务校验。
5. 执行已有格式化、类型检查、测试和构建;未进行真实渲染或浏览器验证时明确说明。 5. 执行已有格式化、类型检查、测试和构建;未进行真实渲染或浏览器验证时明确说明。
@@ -1,4 +1,4 @@
interface: interface:
display_name: "前端实现" display_name: "前端实现(dev:implement-frontend)"
short_description: "按项目框架与组件契约实现前端变更" short_description: "按项目框架与组件契约实现前端变更"
default_prompt: "使用 $implement-frontend 实现并验证当前前端需求。" default_prompt: "使用 $implement-frontend 实现并验证当前前端需求。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "变更计划" display_name: "变更计划(dev:plan-change)"
short_description: "基于项目证据制定可执行的软件变更计划" short_description: "基于项目证据制定可执行的软件变更计划"
default_prompt: "使用 $plan-change 分析当前需求并制定可验证的实施计划。" default_prompt: "使用 $plan-change 分析当前需求并制定可验证的实施计划。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "接口准备" display_name: "接口准备(dev:prepare-api)"
short_description: "整理前后端接口映射和契约差异" short_description: "整理前后端接口映射和契约差异"
default_prompt: "使用 $prepare-api 分析前端需求与现有 API 契约的映射。" default_prompt: "使用 $prepare-api 分析前端需求与现有 API 契约的映射。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "代码审查" display_name: "代码审查(dev:review-code)"
short_description: "审查工作区、提交或分支的代码变更" short_description: "审查工作区、提交或分支的代码变更"
default_prompt: "使用 $review-code 审查当前代码变更并按严重度报告问题。" default_prompt: "使用 $review-code 审查当前代码变更并按严重度报告问题。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "前端审查" display_name: "前端审查(dev:review-frontend)"
short_description: "按项目框架版本专项审查前端代码" short_description: "按项目框架版本专项审查前端代码"
default_prompt: "使用 $review-frontend 按当前框架版本审查前端代码。" default_prompt: "使用 $review-frontend 按当前框架版本审查前端代码。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Java 审查" display_name: "Java 审查(dev:review-java)"
short_description: "按项目版本专项审查 Java 代码" short_description: "按项目版本专项审查 Java 代码"
default_prompt: "使用 $review-java 按当前项目版本审查这段 Java 代码。" default_prompt: "使用 $review-java 按当前项目版本审查这段 Java 代码。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "MyBatis 审查" display_name: "MyBatis 审查(dev:review-mybatis)"
short_description: "专项审查映射、动态 SQL 和查询边界" short_description: "专项审查映射、动态 SQL 和查询边界"
default_prompt: "使用 $review-mybatis 审查当前 MyBatis Mapper 与 SQL。" default_prompt: "使用 $review-mybatis 审查当前 MyBatis Mapper 与 SQL。"
+1 -1
View File
@@ -1,4 +1,4 @@
interface: interface:
display_name: "样式设计" display_name: "样式设计(dev:style)"
short_description: "延续项目视觉语言并设计前端样式" short_description: "延续项目视觉语言并设计前端样式"
default_prompt: "使用 $style 基于当前项目视觉基线设计或调整样式。" default_prompt: "使用 $style 基于当前项目视觉基线设计或调整样式。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "后端测试" display_name: "后端测试(dev:test-backend)"
short_description: "生成并运行聚焦的后端自动化测试" short_description: "生成并运行聚焦的后端自动化测试"
default_prompt: "使用 $test-backend 为当前后端变更补充并运行聚焦测试。" default_prompt: "使用 $test-backend 为当前后端变更补充并运行聚焦测试。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "UI 测试" display_name: "UI 测试(dev:test-ui)"
short_description: "设计并执行用户行为导向的页面测试" short_description: "设计并执行用户行为导向的页面测试"
default_prompt: "使用 $test-ui 为当前页面制定测试计划并在条件允许时执行。" default_prompt: "使用 $test-ui 为当前页面制定测试计划并在条件允许时执行。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "版本升级" display_name: "版本升级(dev:upgrade)"
short_description: "基于官方迁移资料执行可回滚升级" short_description: "基于官方迁移资料执行可回滚升级"
default_prompt: "使用 $upgrade 评估并实施当前项目的版本升级。" default_prompt: "使用 $upgrade 评估并实施当前项目的版本升级。"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "doc", "name": "doc",
"version": "0.3.0", "version": "0.3.1",
"description": "通用文档转换、表格提取、规则化归档与写作工具。", "description": "通用文档转换、表格提取、规则化归档与写作工具。",
"author": { "author": {
"name": "CraftKit" "name": "CraftKit"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Archive Documents" display_name: "Archive Documents(doc:archive)"
short_description: "按项目规则安全预演并归档文档" short_description: "按项目规则安全预演并归档文档"
default_prompt: "使用 $archive 根据项目规则预演文档归档,确认后再执行写入。" default_prompt: "使用 $archive 根据项目规则预演文档归档,确认后再执行写入。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "DOCX to Markdown" display_name: "DOCX to Markdown(doc:docx-to-md)"
short_description: "将 Word 文档转换为 Markdown 并提取图片" short_description: "将 Word 文档转换为 Markdown 并提取图片"
default_prompt: "使用 $docx-to-md 将这份 Word 文档转换为 Markdown,并报告转换警告。" default_prompt: "使用 $docx-to-md 将这份 Word 文档转换为 Markdown,并报告转换警告。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Format Markdown" display_name: "Format Markdown(doc:format-md)"
short_description: "整理 Markdown 排版并严格保持原文语义不变" short_description: "整理 Markdown 排版并严格保持原文语义不变"
default_prompt: "使用 $format-md 整理这份 Markdown 文件的排版,不改写内容。" default_prompt: "使用 $format-md 整理这份 Markdown 文件的排版,不改写内容。"
+2 -1
View File
@@ -12,7 +12,8 @@ description: 将 Markdown 文档转换为 Word .docx,保留常见标题、段
- 只接受存在的 `.md` 或 `.markdown` 文件。 - 只接受存在的 `.md` 或 `.markdown` 文件。
- 默认在输入文件旁生成同名 `.docx`;文件已存在时停止,只有用户明确同意覆盖后才传入 `--force`。 - 默认在输入文件旁生成同名 `.docx`;文件已存在时停止,只有用户明确同意覆盖后才传入 `--force`。
- 用户提供 `.docx` 模板时可传入 `--template`,转换器沿用模板样式并在文档末尾追加内容,不替换模板中的占位符。 - 用户提供 `.docx` 模板时可传入 `--template`,转换器沿用模板样式并在文档末尾追加内容,不替换模板中的占位符。
- 不自动下载图片、字体或依赖;远程图片保留为文字提示,本地缺失图片产生警告。 - 转换依赖 Python 包 `python-docx`。优先使用 Codex 工作区依赖运行时;环境缺失时停止并给出提示,不自动安装依赖。
- 不自动下载图片或字体;远程图片保留为文字提示,本地缺失图片产生警告。
- 不伪造修订记录、批注或目录。需要人工审阅留痕时应使用独立的文档修订流程。 - 不伪造修订记录、批注或目录。需要人工审阅留痕时应使用独立的文档修订流程。
## 转换流程 ## 转换流程
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Markdown to DOCX" display_name: "Markdown to DOCX(doc:md-to-docx)"
short_description: "将 Markdown 转换为结构清晰的 Word 文档" short_description: "将 Markdown 转换为结构清晰的 Word 文档"
default_prompt: "使用 $md-to-docx 将这份 Markdown 转换为 Word,并报告降级内容。" default_prompt: "使用 $md-to-docx 将这份 Markdown 转换为 Word,并报告降级内容。"
@@ -9,10 +9,6 @@ import sys
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from docx import Document
from docx.shared import Inches, Pt
@dataclass @dataclass
class Result: class Result:
"""记录生成物与转换统计。""" """记录生成物与转换统计。"""
@@ -43,6 +39,10 @@ class Converter:
"""使用可预测的小型解析器转换常见 Markdown。""" """使用可预测的小型解析器转换常见 Markdown。"""
def __init__(self, source: Path, output: Path, template: Path | None) -> None: def __init__(self, source: Path, output: Path, template: Path | None) -> None:
# 第三方库在真正转换时才加载,使 --help、参数错误和能力探测不依赖本机预装包。
from docx import Document
self.document_type = Document
self.source = source self.source = source
self.doc = Document(template) if template else Document() self.doc = Document(template) if template else Document()
self.result = Result(output) self.result = Result(output)
@@ -61,12 +61,14 @@ class Converter:
self.add_line(lines[index]) self.add_line(lines[index])
index += 1 index += 1
self.doc.save(self.result.output) self.doc.save(self.result.output)
Document(self.result.output) self.document_type(self.result.output)
return self.result return self.result
def add_code(self, lines: list[str], start: int) -> int: def add_code(self, lines: list[str], start: int) -> int:
"""读取围栏代码块;未闭合时输出其余内容并记录警告。""" """读取围栏代码块;未闭合时输出其余内容并记录警告。"""
from docx.shared import Pt
index = start + 1 index = start + 1
content: list[str] = [] content: list[str] = []
while index < len(lines) and not lines[index].lstrip().startswith("```"): while index < len(lines) and not lines[index].lstrip().startswith("```"):
@@ -101,6 +103,8 @@ class Converter:
def add_line(self, line: str) -> None: def add_line(self, line: str) -> None:
"""识别标题、列表、引用、分隔线和普通段落。""" """识别标题、列表、引用、分隔线和普通段落。"""
from docx.shared import Inches
value = line.strip() value = line.strip()
if not value: if not value:
return return
@@ -158,6 +162,8 @@ class Converter:
def add_image(self, paragraph, alt: str, target: str) -> None: def add_image(self, paragraph, alt: str, target: str) -> None:
"""只处理本地图片,防止转换过程产生隐式网络访问。""" """只处理本地图片,防止转换过程产生隐式网络访问。"""
from docx.shared import Inches
if re.match(r"^[a-z][a-z0-9+.-]*://", target, re.I): if re.match(r"^[a-z][a-z0-9+.-]*://", target, re.I):
paragraph.add_run(f"[远程图片:{alt or target}]") paragraph.add_run(f"[远程图片:{alt or target}]")
self.result.warnings.append(f"未下载远程图片:{target}") self.result.warnings.append(f"未下载远程图片:{target}")
@@ -193,9 +199,22 @@ def main(argv: list[str] | None = None) -> int:
print("错误:输出必须是可写的 .docx;覆盖需使用 --force", file=sys.stderr); return 1 print("错误:输出必须是可写的 .docx;覆盖需使用 --force", file=sys.stderr); return 1
if template and (not template.is_file() or template.suffix.lower() != ".docx"): if template and (not template.is_file() or template.suffix.lower() != ".docx"):
print("错误:模板必须是存在的 .docx 文件", file=sys.stderr); return 2 print("错误:模板必须是存在的 .docx 文件", file=sys.stderr); return 2
try:
converter = Converter(source, output, template)
except ModuleNotFoundError as error:
if error.name == "docx":
print(
"错误:缺少 python-docx。请使用 Codex 工作区依赖运行时,"
"或在已获授权的本地 Python 环境中安装 python-docx 后重试。",
file=sys.stderr,
)
return 3
raise
except (OSError, ValueError) as error:
print(f"错误:{error}", file=sys.stderr); return 1
output.parent.mkdir(parents=True, exist_ok=True) output.parent.mkdir(parents=True, exist_ok=True)
try: try:
result = Converter(source, output, template).convert() result = converter.convert()
except (OSError, ValueError) as error: except (OSError, ValueError) as error:
print(f"错误:{error}", file=sys.stderr); return 1 print(f"错误:{error}", file=sys.stderr); return 1
print(f"DOCX:{result.output}") print(f"DOCX:{result.output}")
@@ -1,4 +1,4 @@
interface: interface:
display_name: "项目消息" display_name: "项目消息(doc:message)"
short_description: "起草清晰可执行的项目通知与提醒" short_description: "起草清晰可执行的项目通知与提醒"
default_prompt: "使用 $message 把这些事实整理成一条项目通知。" default_prompt: "使用 $message 把这些事实整理成一条项目通知。"
+1 -1
View File
@@ -1,4 +1,4 @@
interface: interface:
display_name: "工作汇报" display_name: "工作汇报(doc:report)"
short_description: "基于事实撰写面向不同受众的汇报" short_description: "基于事实撰写面向不同受众的汇报"
default_prompt: "使用 $report 根据这些事实整理一份简洁工作汇报。" default_prompt: "使用 $report 根据这些事实整理一份简洁工作汇报。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "需求整理" display_name: "需求整理(doc:requirements)"
short_description: "将技术输入整理为可确认的需求说明" short_description: "将技术输入整理为可确认的需求说明"
default_prompt: "使用 $requirements 将这些材料整理成需求和疑问清单。" default_prompt: "使用 $requirements 将这些材料整理成需求和疑问清单。"
+2 -1
View File
@@ -13,7 +13,8 @@ description: 将 Excel .xlsx 工作簿转换为 Markdown,按工作表提取表
- 默认保留公式文本;只有用户希望读取工作簿内已有缓存值时才使用 `--values`。转换器不会计算公式。 - 默认保留公式文本;只有用户希望读取工作簿内已有缓存值时才使用 `--values`。转换器不会计算公式。
- 合并单元格默认将锚点值填充到合并区域,可用 `--merged anchor` 仅保留左上角值。 - 合并单元格默认将锚点值填充到合并区域,可用 `--merged anchor` 仅保留左上角值。
- 默认在输入文件旁生成同名 `.md`,已存在时停止;覆盖必须获得用户确认并传入 `--force`。 - 默认在输入文件旁生成同名 `.md`,已存在时停止;覆盖必须获得用户确认并传入 `--force`。
- 不提取宏、图表、批注、数据验证、条件格式或图片,也不自动安装依赖。 - 转换依赖 Python 包 `openpyxl`。优先使用 Codex 工作区依赖运行时;环境缺失时停止并给出提示,不自动安装依赖。
- 不提取宏、图表、批注、数据验证、条件格式或图片。
## 工作流 ## 工作流
@@ -1,4 +1,4 @@
interface: interface:
display_name: "XLSX to Markdown" display_name: "XLSX to Markdown(doc:xlsx-to-md)"
short_description: "将 Excel 工作簿按工作表转换为 Markdown" short_description: "将 Excel 工作簿按工作表转换为 Markdown"
default_prompt: "使用 $xlsx-to-md 将这个 Excel 工作簿转换为 Markdown,并说明公式和合并单元格策略。" default_prompt: "使用 $xlsx-to-md 将这个 Excel 工作簿转换为 Markdown,并说明公式和合并单元格策略。"
@@ -5,12 +5,10 @@ from __future__ import annotations
import argparse import argparse
import datetime as dt import datetime as dt
import importlib.util
import sys import sys
from pathlib import Path from pathlib import Path
from openpyxl import load_workbook
def display(value: object) -> str: def display(value: object) -> str:
"""将单元格值转换为稳定、可读且适合表格的文本。""" """将单元格值转换为稳定、可读且适合表格的文本。"""
@@ -59,6 +57,9 @@ def render_sheet(title: str, rows: list[list[str]]) -> str:
def convert(source: Path, output: Path, values_only: bool, merged_mode: str) -> tuple[int, int, int, list[str]]: def convert(source: Path, output: Path, values_only: bool, merged_mode: str) -> tuple[int, int, int, list[str]]:
"""打开工作簿、转换全部工作表并写入 UTF-8 Markdown。""" """打开工作簿、转换全部工作表并写入 UTF-8 Markdown。"""
# 第三方库在真正读取工作簿时才加载,使 --help 和参数校验可在干净环境中执行。
from openpyxl import load_workbook
workbook = load_workbook(source, read_only=False, data_only=values_only) workbook = load_workbook(source, read_only=False, data_only=values_only)
sections = [f"# {source.stem}"] sections = [f"# {source.stem}"]
row_count = merged_count = 0 row_count = merged_count = 0
@@ -94,9 +95,26 @@ def main(argv: list[str] | None = None) -> int:
print("错误:输出文件必须是 Markdown", file=sys.stderr); return 2 print("错误:输出文件必须是 Markdown", file=sys.stderr); return 2
if output.exists() and not args.force: if output.exists() and not args.force:
print(f"错误:输出文件已存在:{output}", file=sys.stderr); return 1 print(f"错误:输出文件已存在:{output}", file=sys.stderr); return 1
# 在创建输出目录前完成依赖预检,保证缺少运行库时不会留下空目录或半成品。
if importlib.util.find_spec("openpyxl") is None:
print(
"错误:缺少 openpyxl。请使用 Codex 工作区依赖运行时,"
"或在已获授权的本地 Python 环境中安装 openpyxl 后重试。",
file=sys.stderr,
)
return 3
output.parent.mkdir(parents=True, exist_ok=True) output.parent.mkdir(parents=True, exist_ok=True)
try: try:
sheets, rows, merged, warnings = convert(source, output, args.values, args.merged) sheets, rows, merged, warnings = convert(source, output, args.values, args.merged)
except ModuleNotFoundError as error:
if error.name == "openpyxl":
print(
"错误:缺少 openpyxl。请使用 Codex 工作区依赖运行时,"
"或在已获授权的本地 Python 环境中安装 openpyxl 后重试。",
file=sys.stderr,
)
return 3
raise
except (OSError, ValueError) as error: except (OSError, ValueError) as error:
print(f"错误:{error}", file=sys.stderr); return 1 print(f"错误:{error}", file=sys.stderr); return 1
print(f"Markdown:{output}") print(f"Markdown:{output}")
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "git", "name": "git",
"version": "0.4.0", "version": "0.5.1",
"description": "安全、可复核的通用 Git 工作流。", "description": "安全、可复核的通用 Git 工作流。",
"author": { "author": {
"name": "CraftKit" "name": "CraftKit"
+21 -6
View File
@@ -1,18 +1,25 @@
--- ---
name: branch name: branch
description: 根据当前仓库约定生成名称,并在用户确认准确命令后创建和切换本地 Git 分支。适用于明确要求新建分支的场景;仅查看、重命名、删除、合并、推送或发布分支不应触发本 Skill。 description: 根据当前仓库约定,在用户确认后于当前工作区或独立 worktree 创建本地 Git 分支,并在分支任务结束后安全关闭额外 worktree。适用于新增需求、缺陷、维护分支及解除 worktree 分支占用;仅查看、重命名、删除、合并、推送或发布分支不应触发本 Skill。
--- ---
# 创建 Git 分支 # 创建 Git 分支
安全地创建并切换本地分支。所有检查默认只读;`fetch` 和分支创建分别需要明确授权,不得把它们合并为一次隐含操作。 安全地创建本地分支并管理其独立 worktree 生命周期。所有检查默认只读;`fetch`、分支创建和 worktree 移除分别需要明确授权,不得把它们合并为一次隐含操作。
## 选择模式
- `create`:规划名称,并在当前工作区或独立 worktree 创建分支。
- `close`:分支任务结束后安全移除额外 worktree,释放分支占用。
涉及独立 worktree 时读取 [Worktree 分支规则](references/worktree-branch.md)。用户只说“创建分支”时进入 `create`;用户要求结束、关闭、清理额外 worktree,或出现分支已被 worktree 占用错误时进入 `close`。
## 创建前检查 ## 创建前检查
1. 运行 `git status --short --branch`、`git branch --show-current`、`git branch --list`、`git branch --remotes` 和 `git worktree list --porcelain`。 1. 运行 `git status --short --branch`、`git branch --show-current`、`git branch --list`、`git branch --remotes` 和 `git worktree list --porcelain`。
2. 读取当前仓库适用的 `AGENTS.md`、贡献指南或其他明确的分支约定;没有约定时再参考现有分支命名。 2. 读取当前仓库适用的 `AGENTS.md`、贡献指南或其他明确的分支约定;没有约定时再参考现有分支命名。
3. 收集分支用途、简短描述和基准引用。不要默认 `main`、`master`、远程名称、版本分支或合并目标。 3. 收集分支用途、简短描述和基准引用。不要默认 `main`、`master`、远程名称、版本分支或合并目标。
4. 若工作区不干净,说明现有修改会随切换保留;在用户明确选择继续前不得创建分支,也不得自动暂存、提交、还原或清理。 4. 若工作区不干净,列出现有修改并说明直接切换会携带这些修改;不得自动暂存、提交、还原或清理,优先建议使用独立 worktree。
5. `.craftkit/local/**` 和 `.craftkit/cache/**` 不视为业务改动;若它们出现在普通状态中,提示检查 `.craftkit/.gitignore`。`.craftkit/agents/**`、`standards/**`、`knowledge/**`、`handoff/**` 和 `project.json` 属于普通项目改动,应与其他未提交内容一起展示。 5. `.craftkit/local/**` 和 `.craftkit/cache/**` 不视为业务改动;若它们出现在普通状态中,提示检查 `.craftkit/.gitignore`。`.craftkit/agents/**`、`standards/**`、`knowledge/**`、`handoff/**` 和 `project.json` 属于普通项目改动,应与其他未提交内容一起展示。
## 生成和验证名称 ## 生成和验证名称
@@ -34,9 +41,11 @@ description: 根据当前仓库约定生成名称,并在用户确认准确命
- 当前分支和工作区状态; - 当前分支和工作区状态;
- 目标分支名称; - 目标分支名称;
- 基准引用及其提交短哈希; - 基准引用及其提交短哈希;
- 是否会携带未提交修改; - 在当前工作区还是独立 worktree 创建,以及是否影响未提交修改;
- 将执行的唯一创建命令。 - 将执行的唯一创建命令。
工作区干净、当前 checkout 可以切换且用户没有隔离要求时,可以在当前工作区创建。工作区不干净、当前分支被 IDE/服务/测试占用,或用户要求不影响当前 checkout 时,使用独立 worktree;创建前确认目标绝对路径不存在、分支不存在且基准哈希已锁定。
本地基准使用: 本地基准使用:
```text ```text
@@ -49,10 +58,16 @@ git switch -c "<branch>" "<base>"
git switch --no-track -c "<branch>" "<remote>/<base>" git switch --no-track -c "<branch>" "<remote>/<base>"
``` ```
独立 worktree 使用:
```text
git worktree add -b "<branch>" "<absolute-worktree-path>" "<base>"
```
只有用户在看到上述最终信息后明确确认,才执行该命令。用户调整名称或基准后必须重新展示并确认。 只有用户在看到上述最终信息后明确确认,才执行该命令。用户调整名称或基准后必须重新展示并确认。
## 创建后验证 ## 创建后验证
运行 `git branch --show-current`、`git rev-parse --short HEAD` 和 `git status --short --branch`,确认当前分支、起点和工作区状态。远程基准创建时再用 `git rev-parse --abbrev-ref --symbolic-full-name @{upstream}` 检查没有错误 upstream;命令返回“无 upstream”属于预期结果。 在实际创建位置运行 `git branch --show-current`、`git rev-parse --short HEAD` 和 `git status --short --branch`,确认当前分支、起点和工作区状态。独立 worktree 创建后还要复核主工作区分支、HEAD 和未提交内容未变化,并明确报告分支占用路径、后续操作目录以及生命周期结束后的清理义务。远程基准创建时再用 `git rev-parse --abbrev-ref --symbolic-full-name @{upstream}` 检查没有错误 upstream;命令返回“无 upstream”属于预期结果。
本 Skill 到此结束,不执行 push、不设置远程 upstream,也不创建合并请求。失败时报告原始错误和当前仓库状态,不自动重试破坏性替代命令。 `create` 到此不执行 push、不设置远程 upstream,也不创建合并请求。独立 worktree 在分支任务完成前保持 `active`,完成后必须调用 `close` 进入清理门禁。失败时报告原始错误和当前仓库状态,不自动重试破坏性替代命令。
+3 -3
View File
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Create Branch" display_name: "Create Branch(git:branch)"
short_description: "确认名称和基准后安全创建本地 Git 分支" short_description: "创建分支并管理独立 Worktree 的安全关闭"
default_prompt: "使用 $branch 为当前仓库规划并创建一个本地分支,执行前先向我确认。" default_prompt: "使用 $branch 规划并创建本地分支;工作区不干净时使用独立 worktree,分支任务结束后安全关闭它,所有写操作前先向我确认。"
@@ -0,0 +1,62 @@
# Worktree 分支规则
## 创建条件
满足任一条件时,优先在独立 worktree 创建新需求、缺陷或维护分支,不切换当前主工作区:
- 主工作区存在未提交文件、本地配置、环境配置或缓存;
- 用户要求不影响当前 checkout;
- 当前分支正被 IDE、服务或测试占用;
- 当前工作需要与主工作区并行进行。
Worktree 共享仓库对象和本地分支引用。同一个本地分支不能同时被两个 worktree 签出。
## 创建前检查
确认主仓库绝对路径、目标 worktree 绝对路径、分支名、基准引用和完整哈希。检查目标路径不存在、同名本地及远程引用的处理已经确认、目标分支未被其他 worktree 占用。分支名可能包含中文时始终加引号。
取得分支创建授权后执行唯一命令:
```text
git worktree add -b "<branch>" "<absolute-worktree-path>" "<base>"
```
创建后验证新 worktree 的分支、HEAD 和状态,并重新检查主工作区分支、HEAD 和未提交内容未变化。报告该分支的占用路径,提示主工作区不能同时签出,并记录任务完成后的清理义务。
## 关闭条件
创建分支的当次操作完成不等于 worktree 生命周期结束。只有分支开发任务、验证和用户要求的交付已经完成,并且用户不再要求保留现场时,才进入 `close`。
关闭前必须满足:
- worktree 没有未提交或未跟踪文件;
- 没有进行中的 merge、rebase、cherry-pick 或 revert;
- 当前 HEAD 已被预期本地分支引用;
- 主仓库、worktree 路径、分支和 HEAD 均已准确确认。
展示检查结果、移除后保留的分支和唯一清理命令,取得用户确认后执行:
```text
git -C "<main-repository>" worktree remove "<absolute-worktree-path>"
```
不得直接删除目录,不得默认使用 `--force`,不得删除或重置分支,也不得批量清理其他 worktree。
## 关闭后验证
```text
Test-Path -LiteralPath "<absolute-worktree-path>"
git -C "<main-repository>" rev-parse --verify "<branch>"
git -C "<main-repository>" worktree list --porcelain
git -C "<main-repository>" status --short --branch
```
目录不存在、占用记录已释放、分支和提交仍存在、主工作区原有状态未变化时,关闭才算完成。
## 分支占用错误
```text
fatal: '<branch>' is already used by worktree at '<path>'
```
该错误表示分支仍由指定 worktree 签出。若任务仍在进行,继续在该 worktree 中操作;若任务已经结束,执行 `close`。不得强制 checkout、直接删除目录或删除分支绕过占用保护。
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Commit Message" display_name: "Commit Message(git:commit-msg)"
short_description: "只读分析 Git 变更并生成可复核的提交信息" short_description: "只读分析 Git 变更并生成可复核的提交信息"
default_prompt: "使用 $commit-msg 分析当前 Git 变更并建议提交信息,不要暂存或提交。" default_prompt: "使用 $commit-msg 分析当前 Git 变更并建议提交信息,不要暂存或提交。"
+1 -1
View File
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Export Git Changes" display_name: "Export Git Changes(git:export)"
short_description: "预览并安全导出 Git 变更文件和分类清单" short_description: "预览并安全导出 Git 变更文件和分类清单"
default_prompt: "使用 $export 预览这批 Git 变更,排除环境配置后再导出交付快照。" default_prompt: "使用 $export 预览这批 Git 变更,排除环境配置后再导出交付快照。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Git Identity" display_name: "Git Identity(git:identity)"
short_description: "查看并安全设置 Git 提交用户名和邮箱" short_description: "查看并安全设置 Git 提交用户名和邮箱"
default_prompt: "使用 $identity 检查当前 Git 提交身份,并在我确认后设置正确作用域。" default_prompt: "使用 $identity 检查当前 Git 提交身份,并在我确认后设置正确作用域。"
+7 -3
View File
@@ -1,6 +1,6 @@
--- ---
name: integrate name: integrate
description: 评估两个 Git 分支的集成风险,并在用户逐步确认后通过隔离 worktree 准备、验证和发布预集成分支。适用于合并前验证或准备 MR/PR;直接修改目标分支、一键推送或未经审查处理冲突不应触发本 Skill。 description: 评估两个 Git 分支的集成风险,并在用户逐步确认后通过隔离 worktree 准备、验证、发布和清理预集成分支。适用于合并前验证、准备 MR/PR 或关闭预集成 worktree;直接修改目标分支、一键推送或未经审查处理冲突不应触发本 Skill。
--- ---
# Git 分支集成 # Git 分支集成
@@ -12,6 +12,7 @@ description: 评估两个 Git 分支的集成风险,并在用户逐步确认
- `assess`:只读比较分支、预判冲突和识别交付风险。执行时读取 [评估规则](references/assess.md)。 - `assess`:只读比较分支、预判冲突和识别交付风险。执行时读取 [评估规则](references/assess.md)。
- `prepare`:在独立 worktree 中创建预集成分支、执行 merge 并验证。执行时读取 [准备规则](references/prepare.md)。 - `prepare`:在独立 worktree 中创建预集成分支、执行 merge 并验证。执行时读取 [准备规则](references/prepare.md)。
- `publish`:推送预集成分支并准备 MR/PR。执行时读取 [发布规则](references/publish.md)。 - `publish`:推送预集成分支并准备 MR/PR。执行时读取 [发布规则](references/publish.md)。
- `cleanup`:本地交付或发布完成后关闭独立 worktree。执行时读取 [Worktree 生命周期](references/worktree-lifecycle.md)。
用户要求“集成”但未明确阶段时,从 `assess` 开始。前一阶段的授权不能自动扩展到后一阶段。 用户要求“集成”但未明确阶段时,从 `assess` 开始。前一阶段的授权不能自动扩展到后一阶段。
@@ -22,7 +23,9 @@ description: 评估两个 Git 分支的集成风险,并在用户逐步确认
- fetch、worktree 创建、分支创建、merge、冲突暂存、commit、push 和创建 MR/PR 分别展示准确命令并取得对应确认。 - fetch、worktree 创建、分支创建、merge、冲突暂存、commit、push 和创建 MR/PR 分别展示准确命令并取得对应确认。
- 工作区不干净、引用不可解析、已有 worktree 冲突或分支归属不清时停止写操作。 - 工作区不干净、引用不可解析、已有 worktree 冲突或分支归属不清时停止写操作。
- 不自动使用 `ours`、`theirs`、rebase、reset、stash、强制推送或历史改写。 - 不自动使用 `ours`、`theirs`、rebase、reset、stash、强制推送或历史改写。
- 不直接把预集成分支合入目标分支,也不自动删除 worktree 或分支。 - 不直接把预集成分支合入目标分支,也不删除预集成分支。
- Worktree 生命周期结束后必须进入 `cleanup` 门禁;未确认准确路径、状态、HEAD 和分支引用前不得移除,也不得使用 `--force`。
- 预集成是否合格以目标分支到预集成分支的真实差异为准,不能只凭 merge 输出、分支名、提交数量或测试通过判断。
## 环境和本地配置 ## 环境和本地配置
@@ -36,6 +39,7 @@ description: 评估两个 Git 分支的集成风险,并在用户逐步确认
- 只读集成评估报告; - 只读集成评估报告;
- 本地隔离且已验证的预集成分支; - 本地隔离且已验证的预集成分支;
- 经独立授权推送的预集成分支及 MR/PR 建议。 - 经独立授权推送的预集成分支及 MR/PR 建议;
- 生命周期已经结束并安全移除额外 worktree,同时保留预集成分支和提交。
报告源、目标、预集成分支和对应哈希,列出冲突处理、验证结果、环境配置判断及仍需人工完成的动作。 报告源、目标、预集成分支和对应哈希,列出冲突处理、验证结果、环境配置判断及仍需人工完成的动作。
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Integrate Branches" display_name: "Integrate Branches(git:integrate)"
short_description: "隔离评估、准备并发布可审查的预集成分支" short_description: "隔离准备、发布并安全清理预集成 Worktree"
default_prompt: "使用 $integrate 先只读评估这两个分支的集成风险,再决定是否准备预集成分支。" default_prompt: "使用 $integrate 先只读评估分支集成风险,再准备和验证预集成分支;生命周期结束后安全移除额外 worktree。"
@@ -7,6 +7,8 @@
- 当前工作区和目标 worktree 路径没有未处理状态或归属冲突。 - 当前工作区和目标 worktree 路径没有未处理状态或归属冲突。
- 环境配置风险已按项目规范处理;凭据风险为零。 - 环境配置风险已按项目规范处理;凭据风险为零。
创建、复用或关闭独立 worktree 时,同时读取 [Worktree 生命周期](worktree-lifecycle.md)。
## 隔离创建 ## 隔离创建
根据项目规范决定预集成分支基于源还是目标。没有规范时说明两种历史形状,由用户选择,不能默认套用固定组织模型。 根据项目规范决定预集成分支基于源还是目标。没有规范时说明两种历史形状,由用户选择,不能默认套用固定组织模型。
@@ -32,4 +34,6 @@ git status --short --branch
确认源、目标引用仍等于操作前哈希;只有预集成分支发生变化。记录 merge 提交、测试结果和未验证边界。 确认源、目标引用仍等于操作前哈希;只有预集成分支发生变化。记录 merge 提交、测试结果和未验证边界。
保留 worktree 供用户复核。清理 worktree、删除分支或中止 merge 都是独立操作,不属于自动收尾。 必须以目标引用为基准检查 `git diff --check "<target>...HEAD"`、`git diff --stat "<target>...HEAD"`、`git diff --name-status "<target>...HEAD"` 和 `git log --oneline "<target>..HEAD"`。最终文件清单包含本地配置、缓存、IDE 文件或无关需求文件时停止,不得进入交付阶段。
需要人工复核时暂时保留 worktree 并标记为 `delivery-ready`。用户确认仅本地交付,或完成已授权的发布后,生命周期进入 `cleanup-ready`,必须继续执行清理门禁。中止 merge 和删除分支不属于自动收尾。
@@ -23,3 +23,7 @@
- 关联任务或项目文档。 - 关联任务或项目文档。
默认只给出标题、描述、命令或网页入口。实际创建 MR/PR 需要新的明确授权;审核、合并、删除远程分支和删除 worktree 均不在本阶段自动执行。 默认只给出标题、描述、命令或网页入口。实际创建 MR/PR 需要新的明确授权;审核、合并、删除远程分支和删除 worktree 均不在本阶段自动执行。
## 发布后收尾
完成用户已授权的推送或 MR/PR 创建后,确认远程结果和本地预集成分支仍可解析,然后将 worktree 标记为 `cleanup-ready`,继续读取 [Worktree 生命周期](worktree-lifecycle.md) 并执行清理门禁。移除额外 worktree 不删除本地或远程分支。
@@ -0,0 +1,103 @@
# Worktree 生命周期
## 适用范围
本规则管理预集成独立 worktree 从创建到安全移除的完整生命周期。主工作区不干净、用户要求不影响当前 checkout、当前分支被 IDE/服务/测试占用、需要复用旧预集成分支或预计发生冲突时,必须使用独立 worktree。
Worktree 只隔离工作目录;所有 worktree 共享仓库对象和本地分支引用。同一个本地分支不能同时被两个 worktree 签出。
## 生命周期状态
- `planned`:已确认仓库、分支、基准和绝对路径,尚未创建。
- `active`:worktree 已创建,开发、合并或验证正在进行。
- `delivery-ready`:差异和验证完成,等待本地交付确认或发布。
- `cleanup-ready`:本地交付已确认,或用户要求的发布已经完成,可以清理。
- `cleanup-blocked`:存在未提交内容、进行中的 Git 操作、引用风险或需要保留的现场。
- `removed`:额外 worktree 已移除,分支和提交仍存在。
## 创建和占用记录
创建前确认目标绝对路径不存在、目标分支未被其他 worktree 占用、基准引用可解析并记录完整哈希。分支名可能包含中文时始终加引号,不使用 `$HOME`、`$home` 或 `$CODEX_HOME` 作为任务变量。
创建后记录并报告:
- 主仓库绝对路径;
- worktree 绝对路径;
- worktree 当前分支和 HEAD;
- 主工作区当前分支、HEAD 和未提交状态;
- 该分支已被额外 worktree 占用,主工作区不能同时签出;
- 后续操作目录和生命周期结束后的清理义务。
## 新建或复用预集成
旧预集成已经合入目标分支时,默认新建增量预集成分支,使交付历史易于审计。只有用户明确选择复用,并且旧预集成可解析、上次源分支基线已包含、旧预集成与目标分支关系已查清、最终 `<target>...HEAD` 可收敛为本次需求文件时,才能继续复用。
历史中存在直接合入错误版本分支、无关需求或异常大量文件时,不得只因引用可解析就复用。复用时推荐先合入源分支最新增量,再合入最新目标分支;项目规范规定其他拓扑时以项目规范为准。
## 交付范围和结束条件
Merge 输出出现大量目标分支文件不等于 MR/PR 包含这些文件。必须检查:
```text
git diff --check "<target>...HEAD"
git diff --stat "<target>...HEAD"
git diff --name-status "<target>...HEAD"
git log --oneline "<target>..HEAD"
git log --first-parent --oneline -20
```
只有同时满足以下条件,生命周期才可进入 `cleanup-ready`:
- 本次开发或预集成已经完成;
- 用户要求的验证已经完成;
- 用户确认仅本地交付,或用户要求的发布已经完成;
- worktree 没有未提交或未跟踪文件;
- 没有进行中的 merge、rebase、cherry-pick 或 revert;
- 当前 HEAD 已被预期本地分支引用;
- 用户不再要求保留现场。
用户要求保留现场时标记为 `delivery-ready`,明确说明生命周期尚未结束以及分支仍被该 worktree 占用。
## 清理门禁
移除前展示主仓库和 worktree 的准确绝对路径、分支、HEAD、状态、分支引用验证和唯一清理命令。检查至少包括:
```text
git -C "<worktree>" status --porcelain
git -C "<worktree>" branch --show-current
git -C "<worktree>" rev-parse HEAD
git -C "<worktree>" rev-parse -q --verify MERGE_HEAD
git -C "<main-repository>" worktree list --porcelain
git -C "<main-repository>" branch --contains "<head-sha>"
```
`MERGE_HEAD` 不存在是预期结果。还要依据 Git 状态确认不存在 rebase、cherry-pick 或 revert;任一操作未结束时标记为 `cleanup-blocked`。
用户确认准确清理信息后执行:
```text
git -C "<main-repository>" worktree remove "<absolute-worktree-path>"
```
不得直接删除目录,不得默认使用 `--force`,不得为解除占用而删除或重置分支,也不得批量清理其他 worktree。
## 清理后验证
```text
Test-Path -LiteralPath "<absolute-worktree-path>"
git -C "<main-repository>" rev-parse --verify "<branch>"
git -C "<main-repository>" worktree list --porcelain
git -C "<main-repository>" status --short --branch
```
只有目录不存在、worktree 列表已释放占用、分支和提交仍存在、主工作区原有状态未变化时,才能标记为 `removed`。
## 分支已被占用
出现以下错误表示目标分支仍由额外 worktree 签出:
```text
fatal: '<branch>' is already used by worktree at '<path>'
```
继续在错误给出的 worktree 中操作,或者在其生命周期结束并通过清理门禁后移除。不得强制 checkout、直接删除目录或删除分支绕过 Git 的占用保护。
@@ -1,4 +1,4 @@
interface: interface:
display_name: "版本发布" display_name: "版本发布(git:release)"
short_description: "准备版本并分阶段授权提交标签与发布" short_description: "准备版本并分阶段授权提交标签与发布"
default_prompt: "使用 $release 为当前仓库准备可审查的版本发布。" default_prompt: "使用 $release 为当前仓库准备可审查的版本发布。"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "knowledge", "name": "knowledge",
"version": "0.3.0", "version": "0.3.1",
"description": "项目初始化、任务交接、复盘与知识沉淀工作流。", "description": "项目初始化、任务交接、复盘与知识沉淀工作流。",
"author": { "author": {
"name": "CraftKit" "name": "CraftKit"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Distill Task Knowledge" display_name: "Distill Task Knowledge(knowledge:distill)"
short_description: "从当前任务提炼并分流可复用项目知识" short_description: "从当前任务提炼并分流可复用项目知识"
default_prompt: "使用 $distill 从本次任务提炼有证据的长期知识,确认前不要写入。" default_prompt: "使用 $distill 从本次任务提炼有证据的长期知识,确认前不要写入。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Task Handoff" display_name: "Task Handoff(knowledge:handoff)"
short_description: "生成本地任务交接文档和新任务接续提示词" short_description: "生成本地任务交接文档和新任务接续提示词"
default_prompt: "使用 $handoff 总结当前任务,先确认交接内容保存在本地还是作为项目共享文件。" default_prompt: "使用 $handoff 总结当前任务,先确认交接内容保存在本地还是作为项目共享文件。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Project Init" display_name: "Project Init(knowledge:init)"
short_description: "按成熟或空项目模式初始化 Agent 上下文与项目资料" short_description: "按成熟或空项目模式初始化 Agent 上下文与项目资料"
default_prompt: "使用 $init 初始化当前项目,先判断成熟项目或空项目,并询问我是否有参考项目。" default_prompt: "使用 $init 初始化当前项目,先判断成熟项目或空项目,并询问我是否有参考项目。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Maintain Project Lessons" display_name: "Maintain Project Lessons(knowledge:lessons)"
short_description: "初始化、维护、审计并提升项目问题经验" short_description: "初始化、维护、审计并提升项目问题经验"
default_prompt: "使用 $lessons 维护项目问题经验,先确认模式和拟修改内容。" default_prompt: "使用 $lessons 维护项目问题经验,先确认模式和拟修改内容。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Trace Agent Failure" display_name: "Trace Agent Failure(knowledge:trace)"
short_description: "基于证据复盘 Agent 偏离和规则失效原因" short_description: "基于证据复盘 Agent 偏离和规则失效原因"
default_prompt: "使用 $trace 复盘本次 Agent 偏离,先给出证据和尚未执行的改进建议。" default_prompt: "使用 $trace 复盘本次 Agent 偏离,先给出证据和尚未执行的改进建议。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Git Worklog" display_name: "Git Worklog(knowledge:worklog)"
short_description: "根据真实 Git 提交生成每日或周期工作日志" short_description: "根据真实 Git 提交生成每日或周期工作日志"
default_prompt: "使用 $worklog 根据指定日期和作者的 Git 提交生成简洁工作日志。" default_prompt: "使用 $worklog 根据指定日期和作者的 Git 提交生成简洁工作日志。"
+3 -3
View File
@@ -1,7 +1,7 @@
{ {
"name": "skill", "name": "skill",
"version": "0.2.0", "version": "0.2.1",
"description": "项目规范检索及 Codex Skill 创建、迁移与维护工具。", "description": "项目规范检索与维护工具。",
"author": { "author": {
"name": "CraftKit" "name": "CraftKit"
}, },
@@ -9,7 +9,7 @@
"interface": { "interface": {
"displayName": "Skill", "displayName": "Skill",
"shortDescription": "项目规范与 Skill 维护工具", "shortDescription": "项目规范与 Skill 维护工具",
"longDescription": "提供项目规范渐进检索与索引维护,以及 Codex Skill 的创建、迁移、检查和来源同步工作流。", "longDescription": "提供项目规范的渐进检索、冲突识别、缺口分析与索引维护工作流。",
"developerName": "CraftKit", "developerName": "CraftKit",
"category": "Productivity", "category": "Productivity",
"capabilities": ["Read", "Write"], "capabilities": ["Read", "Write"],
@@ -1,4 +1,4 @@
interface: interface:
display_name: "规范维护" display_name: "规范维护(skill:guidance-edit)"
short_description: "建立、检查和维护项目规范索引" short_description: "建立、检查和维护项目规范索引"
default_prompt: "使用 $guidance-edit 检查并维护当前项目的规范索引。" default_prompt: "使用 $guidance-edit 检查并维护当前项目的规范索引。"
@@ -1,4 +1,4 @@
interface: interface:
display_name: "Project Guidance" display_name: "Project Guidance(skill:guidance)"
short_description: "按索引渐进检索项目规范、中性公共基线及冲突依据" short_description: "按索引渐进检索项目规范、中性公共基线及冲突依据"
default_prompt: "使用 $guidance 查找当前任务适用的项目规范,并给出可追溯依据。" default_prompt: "使用 $guidance 查找当前任务适用的项目规范,并给出可追溯依据。"